How can i change UI of my page after async call? - c#

I am having async call on my page,
This takes about 1 minute.
I need to change UI after call completes.
Sample code is give below.
protected void Unnamed1_Click(object sender, EventArgs e)
{
apicasystemWPMCheckStatsService.CheckStatsServiceClient obj = new apicasystemWPMCheckStatsService.CheckStatsServiceClient();
string xmlOptionForGetCheckStats = "<options><mostrecent count='1'/><dataformat>xml</dataformat><timeformat>tz</timeformat></options>";
string checkId = "";
TextBox1.Text = TextBox1.Text + "test" + "\r\n";
obj.BeginGetCheckStats("admin#azuremonitoring", "Cu4snfPSGr8=", "PD6B685A0-006A-4405-951E-B24BB51E7966",
checkId, xmlOptionForGetCheckStats, new AsyncCallback(ONEndGetCheckStats), null);
TextBox1.Text = TextBox1.Text + "testdone" + "\r\n";
}
public void ONEndGetCheckStats(IAsyncResult asyncResult)
{
System.Threading.Thread.Sleep(3000);
TextBox1.Text = TextBox1.Text + "testcomplete" + "\r\n";
}
The question is that how can i get "testcomplete" in my textbox. as my page is not getting posted back after this async call....
My current O/P :
test
testdone
Expected:
test
testdone
testcomplet

Simple answer: You can not do it like that. Because once the ASPX page is sent to the client there is no somple way for the server to communicate with that page.
You can do this however with AJAX. In your Unnamed1_Click set up a "flag" in Session that signals that an async operation is pending. In your ONEndGetCheckStats set that "flag" to signal that the operation has completed.
Add an ASP.NET page method (Quick Tutorial) to your code-behind that:
Checks whether the operation is pending and returns null wehen it is
When operation is finished removes everything from Session and returns the text that you need
On your ASPX page wire up a client-side event on the Unamed1 (a poor name for a button btw) button that starts a client-side loop checking the status using that PageMethod. When status is not null anymore Javascript to change the TextBox1 text.

Related

C# WebBrowser stuck on navigating when used in for loop

I have a for loop that changes the URL
for (int i = 1; i < max; i += 50)
{
completed = false;
string currkey = country;
crawler.Navigate(new Uri("http://www.example.net/func.php?dom=" + currkey + "&key=&start=" + i));
Console.WriteLine("Navigating to " + "http://www.example.net/func.php?dom=" + currkey + "&key=&start=" + i);
while (!completed)
{
Application.DoEvents();
Thread.Sleep(500);
}
}
This is my documentcompleted handler
crawler.Refresh();
Console.WriteLine("Getting universities");
getUniversities();
Console.WriteLine("Finished getting universities");
completed = true;
When i get rid of the for loop and use a single link, it seems to navigate to the website correctly, but when i use for loop to load websites in order, it seems that the web browser gets stuck in the second iteration.
Example:
currkey = United States
In the first iteration, the website link will be http://www.example.net/func.php?dom="United States"&key=&start=1, and on the next one it will be http://www.example.net/func.php?dom="United States"&key=&start=51. The navigation gets stuck when trying to load the second link.
I have used the boolean completed to note that the current iteration is finished, but it is still stuck.
Any kind of help is appreciated
Your Thread.Sleep call is blocking the WebBrowser from continuing to load. What you should be doing is attaching to the DocumentCompleted event, and then loading the next page. Please don't use this while/sleep combination in WinForms - you should use the events that the controls expose.
Attach the event:
crawler.DownloadCompleted += CrawlerDocumentCompleted;
Event handler:
private void CrawlerDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//The document has loaded - now do something
}
A final thought
As it looks like you are implementing a crawler, why are you using the WebBrowser control in WinForms to navigate. Surely all you are interested in is the html that the server serves up? Or is the page using JavaScript to load additional elements into the DOM, requiring you to use the WebBrowser?
You could use the WebClient class and the DownloadString or DownloadStringAsync methods. See https://msdn.microsoft.com/en-us/library/fhd1f0sw(v=vs.110).aspx

Firing WebBrowser.DocumentCompleted event whilst in a loop

I have a simple app I am developing that needs to iterate through a list of URLs which are passed to a WebBrowsers Navigate function in a for each loop. I was hoping to see the DocumentCompleted event firing after each call of the Navigate function but it only seems to be fired after the whole form has completed loading - and this the loop has completed.
I guess I am missing something fundamental here but some help and advice would be great!
Thanks!
Here is a sample of code that I am trying...
This foreach loop runs n the Form Load event of the WinForms page I am using...
int id = 0;
foreach (DataRow row in quals.Rows)
{
URN = row["LAIM_REF"].ToString();
string URN_formated = URN.Replace("/", "_");
string URL = "http://URL_I_AM_GOING_TOO/";
string FullURL = URL + URN_formated;
wbrBrowser.ScriptErrorsSuppressed = true;
wbrBrowser.Refresh();
wbrBrowser.Navigate(FullURL);
id += 1;
label1.Text = id.ToString();
}
At the point the loop gets to the line:
wbrBrowser.Navigate(FullURL);
I was hoping that the event:
private void wbrBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
...
}
would fire therefore being able to run processes against each of the URLs returned in the loop.
Thanks!
I used:
while (wbrBackground.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); }
after the Navigate function and it now works as expected.

Wait for complete statement or function in winform of c#

I developing winform (c#) to read html form website.
When i click button, Textbox1 don't set text after 1 seconds. It wait unit the end forech.
Now i want, function will set text for textbox in 1 seconds.
how do it?
this is the code:
when kick button1:
private void button1_Click(object sender, EventArgs e)
{
string url = "http://truyentranh8.com/danh_sach_truyen/";
var web = new HtmlWeb();
var doc = web.Load(url);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//tbody/tr/td[#class='tit']/a[#class='tipsy']"))
{
textBox1.Text += node.InnerText + "\n";
Thread.Sleep(1000);
}
}
Thread.Sleep in your case puts the main thread in in sleep mode. It can't update the UI till it gets released and the button1_Click method is over. So you don't see text changes per second. All you'll see is Text being updated all at once.
So make it asynchronous. If you're using .Net 4.5, you can use async/await and make life simple.
private async void button1_Click(object sender, EventArgs e)
{
string url = "http://truyentranh8.com/danh_sach_truyen/";
var web = new HtmlWeb();
var doc = web.Load(url);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//tbody/tr/td[#class='tit']/a[#class='tipsy']"))
{
textBox1.Text += node.InnerText + "\n";
await Task.Delay(1000);
}
}
If you are interested I have written article on this subject.
Do not use Thread.Sleep on an event thread for this task.
The problem is that the UI is not getting a chance to update as it redraws on the thread that is blocked. As such the UI update only appears after all the thread-blocking code ends and the Click handler is exited.
Use an appropriate Timer instead, or if feeling hackish, read up about DoEvents. Alternatively, consider doing the long running task in a BackgroundWorker - the UserState of the Progress event can be used to report partial updates, already marshaled back to the appropriate thread.
Use DoEvents to refresh the form every time you change something on design
private void button1_Click(object sender, EventArgs e)
{
string url = "http://truyentranh8.com/danh_sach_truyen/";
var web = new HtmlWeb();
var doc = web.Load(url);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//tbody/tr/td[#class='tit']/a[#class='tipsy']"))
{
textBox1.Text += node.InnerText + "\n";
Application.DoEvents();
}
}

Page_Load fires infinity times

I have problem with my iframe asp.net page.
Browser url containst parameter which I need to use in my iframe page.
Obviously I can't get access via .NET so I came up with the idea that at the end of the Page_Load method add sth like that :
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
bool isReloaded = Request.QueryString.GetValue<bool>("reloaded");
ContentId = Request.QueryString.GetValue<int>("contentId"); //I need this value
if (!isReloaded)
{
StringBuilder js = new StringBuilder("<script language='javascript'>");
js.Append("var last = window.top.location.href.substring(window.top.location.href.lastIndexOf('/') + 1, window.top.location.href.length); ");
js.Append("window.location.href = window.location.href + '?reloaded=true&contentId=' + last;");
js.Append("if(window.location.href.indexOf('reloaded=true') == -1) window.location.reload();");
js.Append("<" + "/script>");
Response.Write(js.ToString());
}
}
}
In shortcut I use Javascript to get value I need and fire reload() but with changed QueryString.
Page_Load is firing again and now I have bool isReloaded filled with true.
The condition (!isReloaded) blocks that this time javascript will not be added to Response.
I don't know why, but Page_Load fires again, this time without added parameters so it's the same situation as at the beginning and again is adding JS etc.
Result is that Page_load fires endlessly.
What did I do wrong ? What is the reason ?
if you have a look at your code, you have this line:
js.Append("if(window.location.href.indexOf('reloaded=true') == -1) window.location.reload();");
you are checking the location.href for the 'reloaded' var, but note that your page is reloaded as soon as you change the location, and your script keeps executing before it is done, so it results in reloading of the page over an over again without the query string.
remove this line and it should work fine.
another thing though, i changed your code a little bit to register the script on page instead of response.write it,
it shouldnt make any difference, but if your code still doesnt work then try my version:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
bool isReloaded;
int ContentId;
bool.TryParse(Request.QueryString["reloaded"],out isReloaded);
int.TryParse(Request.QueryString["contentId"],out ContentId); //I need this value
if (!isReloaded)
{
StringBuilder js = new StringBuilder();
js.Append("var last = window.top.location.href.substring(window.top.location.href.lastIndexOf('/') + 1, window.top.location.href.length); ");
js.Append("window.location.href = window.location.href + '?reloaded=true&contentId=' + last;");
ExecScript(js.ToString());
}
}
}
void ExecScript(string script)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null && !page.ClientScript.IsClientScriptBlockRegistered("AttachedScript"))
{
page.ClientScript.RegisterClientScriptBlock(page.GetType(), "AttachedScript", script, true);
}
}
Thanks for help.
Now I have sth like that and it's ok.
StringBuilder js = new StringBuilder("<script language='javascript'>");
js.Append("var last = window.top.location.href.substring(window.top.location.href.lastIndexOf('/') + 1, window.top.location.href.length); ");
js.Append("if(window.location.href.indexOf('reloaded=true') == -1) window.location.href = window.location.href + '?reloaded=true&contentId=' + last;");
js.Append("<" + "/script>");
I didn't know that editing location executes reload automatically ;)
Thanks again

Weather fetching is not working more than once

I have a problem with the Wunderground forecast that I am using to retrieve data in c# program.
When I click to retrieve data once everything is working correctly but when I hit the button once more I am getting this error:
Here is my code:
private void bweather_DoWork(object sender, DoWorkEventArgs e)
{
string lat = Math.Round(deciLat).ToString();
string lng = Math.Round(deciLon).ToString();
string latlong = String.Format("{0},{1}", lat.Replace(',', '.'), lng.Replace(',', '.'));
//Initialize Current as a new Day
dow.Current = new WeatherLib.WDay();
//Using Wunderground as the provider we populate the property with current data for the latlong entered into the textbox
try
{
dow = WeatherLib.WProvider.Wunderground(latlong);
writeToLogFile("Retrieve weather info successfully on: " + latlong);
}
catch (Exception ex)
{
writeToLogFile(ex.Message);
}
}
Here is the refresh button:
private void weather_refresh_Click(object sender, EventArgs e)
{
writeToLogFile("Weather button pressed");
weather_descripton.Clear();
weather_speed_textbox.Clear();
weather_tem_textbox.Clear();
weather_rain_text.Clear();
weather_wind_dir_textbox.Clear();
weather_descripton.AppendText("Searching.......");
if (!bweather.IsBusy)
{
bweather.CancelAsync();
}
bweather.RunWorkerAsync();
}
And here are the event handlers:
// Weather handlers
bweather.WorkerSupportsCancellation = true;
bweather.DoWork += bweather_DoWork;
bweather.RunWorkerCompleted += bweather_RunWorkerCompleted;
Any idea why is this not working as it should?
Thank you
Well the error message suggests that you're trying to use the same background worker more than once.
You're asking it to cancel if it's still busy, but that doesn't mean it'll cancel immediately. As far as I can tell, the BackgroundWorker code isn't even checking whether it's been cancelled, which means cancelling it won't really achieve anything useful.
I would suggest that if it's busy, you should instead just ignore the request. In fact, it might be better to disable the button completely when you start the operation, and only re-enable it when the operation completes.

Categories