So I am trying to execute a method when the page has loaded. The OnAfterRender() override method is too early in my case. the method I am trying to do is in my #code{} block of the razor page.
I basically want to execute getAvailablePrinters when the page is loaded.
as requested my code below:
#code {
private List<string> Printers;
private List<string> LayoutTypes;
private void sendPrint()
private async Task getAvailablePrinters()
{
//get layouts
Layouts = new List<Layout>();
AvailablePrintersRepository availablePrintersRepository = new AvailablePrintersRepository();
try
{
Layouts = await availablePrintersRepository.getAvailablePrintersAsync();
}
catch (Exception e)
{
//show message
}
//sort printers & layouts
Printers = new List<string>();
LayoutTypes = new List<string>();
foreach (Layout layout in Layouts)
{
foreach (string printer in layout.Printers)
{
if (!Printers.Contains(printer))
{
Printers.Add(printer);
}
}
if (!LayoutTypes.Contains(layout.Type))
{
LayoutTypes.Add(layout.Type);
}
}
}
}
#AccessDenied I want to send a request to another API to get back data i need to display to the user. i now have a button to do it but i want to get the data after the page has loaded so the user doesn't have to press the button each time
--
Because the method isn't finished when the page has loaded so that is why I want to do it after the page has loaded
So you believe that the OnAfterRenderAsync and OnAfterRender are called too early in the pipeline, and thus are not fit for the Web Api call you want to do in order to retrieve data, right ?
You are wrong, they are, in my opinion too late for this enterprise, and you should use the OnInitializedAsync life cycle method to execute the HTTP request.
Please see the VS template how a Web Api is made to populate the ForeCast objects
in the FetchData page.
You should try code in various situations to understand how the initialization process works, and see that your ideas or perceptions are wrong. Understand this: You should retrieve your data before your page is rendered, not after it is being rendered. OnAfterRender(Async) may be used to execute code that otherwise it's too early to execute. It is most often used to initialize JS objects.
Hope this helps...
Related
I'm working on an app in Xamarin.Forms, and things have been going pretty steadily, until I ran into a navigation error. The thing that mystifies me about it is that I've already been successfully using the same code calls on other pages, but suddenly with this page, it isn't working.
I have designed a bit of a unique navigation flow because of the visual result that I'm trying to accomplish, using a combination of master-detail that has two tiers of navigation pages using the normal push / pop code. I was following suggestions from this article on medium.com.
The app initializes a main page called "Root Navigation" that initializes the master and detail pages.
public App()
{
InitializeComponent();
MainPage = new RootNavigation();
}
Root Navigation page:
public partial class RootNavigation : MasterDetailPage
{
private MenuPage menuPage;
private NavigationPage OuterPage;
private NavigationPage InnerPage;
public RootNavigation()
{
this.Master = this.menuPage = new MenuPage();
this.menuPage.MenuItemsListView.ItemSelected += Menu_ItemSelected;
var viewModel = new SelectEmployeeViewModel();
var page = new SelectEmployeePage(viewModel);
SetAsDetailPage(page);
}
To navigate forward in the app, I'm using a method called "set as detail page," that bridges the gap between master-detail behavior and navigation push / pop behavior.
private void SetAsDetailPage(ContentPage page)
{
NavigationPage.SetHasNavigationBar(newPage, false);
if (newPage.GetType() == typeof(JobDetailPage))
{
newPage.ToolbarItems.Add(
new ToolbarItem()
{
Text = "Back",
Command = new Command(() => BackButton_Clicked())
});
}
this.InnerPage = this.InnerPage ?? new NavigationPage();
this.InnerPage.Navigation.PushAsync(page);
this.OuterPage = this.OuterPage ?? new NavigationPage(this.InnerPage);
this.Detail = this.Detail ?? this.OuterPage;
}
Then, navigating backward calls one of two methods: "BackButton_Clicked()" or "ReturnToJobList()".
private void ReturnToJobList()
{
while (InnerPage.CurrentPage.GetType() != typeof(JobsPage))
{
var current = InnerPage.CurrentPage.ToString();
System.Diagnostics.Debug.WriteLine($"'{current}' attempting Navigation.Pop()");
InnerPage.PopAsync();
}
}
private void BackButton_Clicked()
{
this.InnerPage.PopAsync();
}
All of the pages that display read-only data have navigated without issue. When I'm done with the page, I have it raise a call to the MessagingCenter, and the root navigation receives the message and performs the desired navigation. For example, the "MenuPage_ItemSelected" event handler fires the code:
if (e.SelectedItem.ToString().ToLower() == "log out")
{
Device.InvokeOnMainThreadAsync(() =>
{
InnerPage.PopToRootAsync();
this.IsPresented = false;
});
}
This seems to be working fine, after I spent a while researching that when a secondary page calls 'pop to root' on a background thread, I have to invoke it on the main thread.
Ok, so finally to the problem page: The "update job notes" page. The page raises a call to the Messaging Center, and the Root Navigation page picks that up and executes the following code:
private async void SaveJobNotes(ContentPage sender)
{
if (sender is UpdateJobNotesPage notesPage)
{
bool result = await SaveNewJobNote(notesPage);
var message = result ? "Saved changes" : "An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
ReturnToJobList();
}
Stepping through the code, it correctly executes SaveNewJobNote() and returns true. Then, it awaits displaying the alert "Saved Changes". Then, the code then gets stuck in an infinite while loop in the "ReturnToJobList()," forever printing out into the debug output [0:] 'Application.Views.UpdateJobNotesPage' attempting Navigation.Pop(). After about a million cycles I get tired of waiting and quit the debugger. I can't seem to do anything to make the page go away!
I've tried a bunch of stuff with investigating the differences between PopAsync and PopModalAsync. After checking what's on the navigation stacks for the different pages in question, everything looks exactly like what I'd expect -- there's nothing on the modal stacks for anything (because I never called PushModalAsync on anything), there's 0 on the RootNavigation stack, 1 on the OuterPage stack, and 4 on the InnerPage stack. That all makes perfect sense to me, but it still doesn't pop the Update Job Notes page. I also tried code with Navigation.RemovePage(page) with no success. The only difference there was that the debugger included printing a warning about my code and suggesting I use PopAsync() instead.
I also tried some different things with making the PopAsync() call from this.Navigation, this.Outer, this.Outer.Navigation, this.Inner, this.Inner.Navigation, all with no success.
I have already looked at a bunch of other questions on Stack Overflow including this question and this question but none of them seem to apply in this case. Any assistance would be greatly appreciated!!
I remember this was happening to me.
I forget exactly what was the cause, but I had some funky navigation going on as well. My problem was around popups and at one point, they were creating a new stack. So when I popped, I was getting unexpected results.
I would suspect you are also creating another stack somewhere, especially if you are at 0 in the debugger.
The culprit is most likely lurking around that InvokeOnMainThread().
I haven't really figured out what the problem is with the code block that I created that was supposed to call InnerNavigation.PopAsync() until the Job List page was visible. It seems like all the variables in that code block evaluate to values that I'd expect, but somehow it doesn't seem to be able to pop anything off the stack.
However, I did change my code block that handles saving Job Notes, and it does now pop the Save Job Notes page off the stack.
private async void SaveJobNotes(ContentPage sender)
{
this.InnerPage.PopAsync(); //I don't understand why this works and the
//other didn't, but it correctly pops the page
if (sender is UpdateJobNotesPage notesPage)
{
bool noteSaved = await SaveNewJobNote(notesPage);
bool progressSaved = await SaveJobProgress(notesPage);
var message = noteSaved && progressSaved ?
"Changes were save successfully" :
"An error occurred; changes not saved";
await DisplayAlert("Save", message, "OK");
}
}
I'm creating a UWP app in C#. I'm trying to run different blocks of code within my OnNavigatedTo function depending on what page sent me there. Right now I have if else statements determining which blocks of code are ran depending on what page sent me to the page I'm on now.
My existing code is shown below:
protected override void OnNavigatedTo(NavigationEventArgs e)
//runs every time MainPage is Navigated to from another page or when program is first started and MainPage loads
{
if ([SomeAttribute == Page2])
{
//attempt to add string from page[2] to List
if (e.Parameter is string && !string.IsNullOrWhiteSpace((string)e.Parameter) && !InspectorInst.Names_list.Contains(e.Parameter))
//if thing passed from previous page (e.Parameter) is a string and isnt null or whitespace and isnt already in the Names_list
{
string s = e.Parameter.ToString();//create string to hold e.Parameter
this.InspectorInst.Names_list.Add(s);//Add string to Names_list
}
}
else{
//do something else
}
base.OnNavigatedTo(e);
}
[SomeAttribute == Page2] should be something that will return true if the page that directed me to the page I'm currently on was Page2. And SomeAttribute would return the page that sent me to the page I'm currently on. I have not been able to find anything in the UWP documentation that would accomplish this.
If am going about this problem the wrong way and there is an easier way to accomplish this, what would that be?
Thanks
I think that if you want a page to have a different behavior depending on where it is called, the normal way would be to pass different values as the second parameter to the Navigate method.
If you absolutely want to know which page has called, then you can examine the last entry in the navigation stack, like this:
var entry = this.Frame.BackStack.LastOrDefault();
if (entry != null)
// Check entry.SourcePageType - it contains the type of the previous page
Note that you may also have to check the NavigationEventArgs.NavigationMode to see if the user is going back from the page or not.
I am using a WebBrowser control for web scraping pages on Yahoo news. I need to use a WebBrowser rather than HtmlAgilityPack to accommodate for JavaScript and the like.
Application Type: WinForm
.NET Framework: 4.5.1
VS: 2013 Ultimate
OS: Windows 7 Professional 64-bit
I am able to scrape the required text, but I am unable to return control of the application to the calling function or any other function when scraping is complete. I also cannot verify that scraping is complete.
I need to
1. Verify that all page loads and scraping have completed.
2. Perform actions on a list of the results, as by alphabetizing them.
3. Do something with the data, such as displaying text contents in a Text box or writing them to SQL.
I declare new class variables for the WebBrowser and a list of URLs and an object with a property that contains a list of news articles..
public partial class Form1 : Form
{
public WebBrowser w = new WebBrowser(); //WebBrowser
public List<String> lststrURLs = new List<string>(); //URLs
public ProcessYahooNews pyn = new ProcessYahooNews(); //Contains articles
...
lststrURLs.Add("http://news.yahoo.com/sample01");
lststrURLs.Add("http://news.yahoo.com/sample02");
lststrURLs.Add("http://news.yahoo.com/sample03");
Pressing a button, whose handler is calling function, calls this code.
w.Navigate(strBaseURL + lststrTickers[0]); //invokes w_Loaded
foreach (YahooNewArticle article in pyn.articles)
{
textBox1.Text += article.strHeadline + "\r\n";
textBox1.Text += article.strByline + "\r\n";
textBox1.Text += article.strContent + "\r\n";
textBox1.Text += article.dtDate.ToString("yyyymmdd") + "\r\n\r\n";
}
The first problem I have is that program control appears to skip over w.Navigate and pass directly to the foreach block, which does nothing since articles has not been populated yet. Only then is w.Navigate executed.
If I could get the foreach block to wait until after w.Navigate did its work, then many of my problems would be solved. Absent that, w.Navigate will do its work, but then I need control passed back to the calling function.
I have worked on a partial work-around.
w.Navigate loads a page into the WebBrowser. When it is done loading, the event w.DocumentCompleted fires. I am handling the event with w_Loaded, which uses a class with logic to perform the web scraping.
// Sets up the class
pyn.ProcessYahooNews_Setup(w, e);
// Perform the scraping
pyn.ProcessLoad();
The result of the scraping is that pyn.articles is populated. The next page is loaded only when criteria, such as pyn.articles.Count > 0.
if (pyn.articles.Count > 0)
{
//Navigate to the next page
i++;
w.Navigate(lststrURLs[i]);
}
More pages are scraped, and articles.Count grows. However, I cannot determine that scraping is done - that there will not be more page loads resulting in more articles.
Suppose I am confident that the scraping is done, I need to make articles available for further handling, as by sorting it as a list, removing certain elements, and displaying its textual content to a TextBox.
That takes me back the foreach block that was called too early. Now, I need it, but I have no way to get articles into the foreach. I don't think I can call some other function from w_Loaded to the handling for me because it would be called for each page load, and I need to call the function once after all page loads.
It occurs to me that some threaded architecture might help, but I could use some help on figuring out what the architecture would look like.
I'm attempting to write a save results extension to the DomainDatasourceView.
I can successfully write the contents of the current page of results but when I attempt to call MoveToNextPage(), the PageIndex stays current. MSDN docs regarding this don't provide any details other than MoveToNextPage returns a bool is it successfully moves to the next page.
The following sample code results in an infinite loop, and the Current page is never changed.
private void WriteResults(DomainDataSourceView resultsview)
{
StringBuilder csvdata = new StringBuilder();
... Do Work on current page ...
if(resultsview.CanChangePage && resultsview.MoveToNextPage())
{
csvdata.Append(WriteResults(resultsview));
}
}
Do I need to listen for the PageChanged Event to continue Saving Results?
Do I need to call Load on the DomainDataSource for each page?
The MSDN Docs on DomainDataSourceView doesn't go into too much details on this subject.
[Edit]
After playing around some more, I was able to determine that the Move...Page commands do call the the DomainDataSource Load operation, however its another Async call, so any consecutive work that needs to be done on the loaded pages, should be handled accordingly.
So I have this interface that is just one big GO button that syncs a bunch of data from one tool to another. The problem is it takes a really long freaking time and some users are left wondering whats the deal. So I am wondering if there is a way that I can put something in my loop so that every so many entries it sends something back to the page to update them on the progress.
Currently it is just an .aspx page with an aspx.cs behind it. The Go button fires off the whole process and it calls Response.Write a ton of times (as well as writing the same thing to a log file I made) but the Responses don't show until the entire thing is done.
Please advise.
You could design a class which will be stored in the session and which will represent the current state of the operation:
public class OperationState
{
public object Result { get; set; }
public int Progress { get; set; }
public string Error { get; set; }
}
An instance of this class could be created when you start the operation and store it in the user session. Then at each step of the operation you could retrieve it from session and update the progress property. Once the operation terminates you could set the Result property or the Error property in case an exception occurs. In the meantime you could design a PageMethod which will be accessible from client script. This method will simply return the State instance from the session. You will then invoke it periodically and asynchronously from javascript to check the progress and update the DOM to notify the user.
I am assuming you are calling another class to do the work. Lets call this the WorkerClass
You can have the WorkerClass have an event hooked up to it, that the .aspx page hooks up too and will write a message when the event is triggered.
// Overload EventArgs to send messageas back up
public delegate void UpdateMethod(object sender, EventArgs e);
public class WorkerClass
{
public event UpdateMethod UpdateMethod;
}
WorkerClass worker = new WorkerClass();
worker.UpdateMethod += new UpdateMethod(worker_UpdateMethod);
EDIT based on Comment it is on there page
If you don't want to refactor to another class doing the work (which I suggest). You can post the messages this way.
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
this.ProcessMassiveWorkLoad();
}
private void ProcessMassiveWorkLoad()
{
for(int i = 0; i < 100000; i++)
{
// Do some work
// Write the fact you have work
Response.Write(string.Format("Done {0} of 100000", i);
}
}
The simplest way to resolve your issue is to call Response.Flush() after each Response.Write.
This will flush the current response buffer back to the client, enabling them to see the current state of the page.
Even David's method would need this to get the responses out to the user in a timely manner.
The better solution would be along the lines of Darin's solution, which would involve some client side scripting of (say) an update panel, that you refresh with a JavaScript timer to get the latest state, but that may introduce other issues for you (needing JavaScript turned on, rewriting the long running method as something you can fire off asynchronously, etc).
If it's any consolation, I've done both in the past, and would use either again.