I'm using the LocalMessageReceiver and LocalMessageSender classes to communicate between two Silverlight applications (One in browser and other Out of browser).
I need to execute messageReceiver.MessageReceived in a new thread.
This is my base code:
public MainPage()
{
LocalMessageReceiver messageReceiver = new LocalMessageReceiver("Msg", ReceiverNameScope.Global, LocalMessageReceiver.AnyDomain);
messageReceiver.MessageReceived += MessageReceiver_MessageReceived; //I want to execute this in a new Thread.
messageReceiver.DisableSenderTrustCheck = true;
try
{
messageReceiver.Listen();
}
catch (ListenFailedException)
{
//to do
}
}
private void MessageReceiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
string command = e.Message;
switch (command)
{
case "SCAN":
e.Response = Scan(e.Message); //I need to send a response
break;
default:
e.Response = "NULL";
break;
}
}
This might be what you want...
messageReceiver.MessageReceived += (s, e) =>
Task.Factory.StartNew(() => MessageReceiver_MessageReceived(s, e));
or using threads ...new Thread(() => MessageReceiver_MessageReceived(s, e)).Start();
Related
I'm creating a simple program that pings all the servers on our network and returns whether the ping requests were successful.
I'm trying to utilise background workers so that the user can press the ping button and the pings run in the background while they can do other things on the UI
DoWork runs fine, there's no loop to keep it there infinitely, and it reaches the line:
r = pinger.Send(s)
and then from my understanding it ends and so the RunWorkCompleted method should be called?
I'm relearning programming after a long abscense so if I missed something obvious I apologise
...
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
}
private void Ping_Btn_Click(object sender, EventArgs e)
{
count = Convert.ToInt32(pingSeconds_TxtBox.Text);
if (backgroundWorker1.IsBusy != true)
{
// Start operation
backgroundWorker1.RunWorkerAsync();
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if(worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
for(int i = 0; i <= count; i++)
{
MessageBox.Show("something is happening");
// Create ping object
Ping pinger = new Ping();
PingReply r;
// IP to test ping
string s = "###";
try
{
r = pinger.Send(s);
}
catch (Exception b)
{
MessageBox.Show(b.ToString());
}
}
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Show me something");
if(e.Cancelled == true)
{
statusLbl1.Text = "Cancelled";
} else if(e.Error != null)
{
statusLbl1.Text = "Error: " + e.Error.Message;
} else
{
statusLbl1.Text = "YEEEEEEEET";
}
}
...
You need to attach your backgroundWorker1_RunWorkerCompleted event handler to the RunWorkerCompleted event. The C# compiler doesn't hook handlers to events based on naming conventions. You have to do it explicitly.
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
I strongly suggest you convert this code to use async await which is much better at representing the flow of code control, rather than using the old BackgroundWorker which is basically deprecated.
Note the following:
The main event handler should be async void but all other async functions should be async Task.
Use of SemaphoreSlim.WaitAsync(0) to check if we are busy.
Ping object needs a using or finally to dispose it, as does the CancellationTokenSource.
<= count looks like it should be < count because you begin at 0.
SemaphoreSlim sem = new SemaphoreSlim(1, 1);
CancellationToken token;
private async void Ping_Btn_Click(object sender, EventArgs e)
{
if (!await sem.WaitAsync(0))
return;
var tokenSource = new CancellationTokenSource();
try
{
var count = Convert.ToInt32(pingSeconds_TxtBox.Text);
await RunPingsAsync(count, tokenSource.Token);
statusLbl1.Text = "YEEEEEEEET";
}
catch (OperationCanceledException)
{
statusLbl1.Text = "Cancelled";
}
catch (Exception e)
{
statusLbl1.Text = "Error: " + e.Error.Message;
}
finally
{
sem.Release();
tokenSource.Dispose();
}
MessageBox.Show("Show me something");
}
private Task RunPingsAsync(int count, CancellationToken token)
{
for(int i = 0; i < count; i++)
{
token.ThrowIfCancellationRequested();
MessageBox.Show("something is happening");
// IP to test ping
string s = "###";
// Create ping object
using (Ping pinger = new Ping())
{
var r = await pinger.SendPingAsync(s);
}
}
}
If you want to keep an infinite loop, then you have to make a loop in your backgroundWorker1_DoWork Method. Something like this
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker) sender;
while (!worker.CancellationPending)
{
//Do your stuff here
for(int i = 0; i <= count; i++)
{
MessageBox.Show("something is happening");
// Create ping object
Ping pinger = new Ping();
PingReply r;
// IP to test ping
string s = "###";
try
{
r = pinger.Send(s);
}
catch (Exception b)
{
MessageBox.Show(b.ToString());
}
}
}
}
Also, it is not a good idea to display message boxes from your background thread, Log it in console or any file.
I'm a newb in programming and I'm trying to do my first thingy that would be for someone else and not just me (so shouldn't be that crappy ^^ )
It's a Online-Checker for clients in LAN network (so he can just paste a list of clients, and it returns the online or offline).
fyi: I'm using Try/Catch because ping.send to an offline host returns in an Error which crashed the application.
Currently it looks like this:
private void btn_check_Click(object sender, EventArgs e)
{
string[] hosts = txt_hosts.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (String host in hosts)
{
pinger(host);
}
}
public void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
txt_result.Text += "true" + Environment.NewLine;
Application.DoEvents();
}
catch
{
txt_result.Text += "false"+Environment.NewLine;
Application.DoEvents();
}
}
Now, the interface is like frozen whenever a ping.send is processing (and that's quiet long cause of the timeout of pings).
Is there any way to do this threaded? Before I tried to start a thread, but that doesn't work either because both write in txt_result and that returns an error.
Thanks for any help!
If use acync/await:
// send request
foreach (string host in hosts)
pinger(host);
// async function
async void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
bool bResp;
try
{
var result = await ping.SendPingAsync(host, 4000);
bResp = result.Status == System.Net.NetworkInformation.IPStatus.Success;
}
catch { bResp = false; }
txt_result.Text += bResp.ToString() + Environment.NewLine;
}
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
pinger(host);
});
It could throw an exception at the line : txt_result.Text = "...";
Because you are trying to modify a value in a thread from another thread.
So you could write:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
txt_result.Text = "...";
}));
Which will request the UI thread to modify the value.
Run on a background worker.
public void pinger(string host)
{
var bw = new BackgroundWorker();
bw.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
e.Result = new object[] { result};
}
catch(Exception ex)
{
// Catch specific exceptions here as needed
}
};
bw.RunWorkerCompleted += (bw_txt_results);
bw.RunWorkerAsync();
}
private void bw_txt_results(object sender, RunWorkerCompletedEventArgs e)
{
txt_result = e.result[0].ToString();
}
I am using C# .net 4.0 VS 2010.
I got a code in a form that basically adds a Task on form load in order to run a UDP Listener (on infinite loop). Whenever the Listener gets something from UDP socket, i add a line and the message to the multiline-textbox (this.textBox4.Text).
However i get an exception saying "Cross-thread operation not valid: "Contol 'textBox4' accessed from a thread other than the thread it was created on."
I didn't want to end the loop just to pass the value. Is there a way to do this? Here are my codes:
//main form load menu
private void frm_Menu_Load(object sender, EventArgs e)
{
Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
.... // more code here
}
//async function
public Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
{
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
... // more codes here
return 1;
});
}
//Listening on UDP socket
public void goListening()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
string received_data;
byte[] receive_byte_array;
try
{
while (!done)
{
receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
// display on TextBox4
this.textBox4.Text = "a\r\nb";
this.textBox4.Text = received_data.ToString().Trim();
}
}
catch (Exception e)
{
//gives "Contol 'textBox4' accessed from a thread other than
//the thread it was created on." when receiving a message.
MessageBox.Show(e.ToString());
}
listener.Close();
}
Version 2 - After answers by #cremor and #George87
private void frm_Menu_Load(object sender, EventArgs e)
{
MyValue = "Menu,7";
Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
.... // more code here
}
private Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
{
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
.... // more codes here
return 1;
});
}
//Listening
private void goListening()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
string received_data;
byte[] receive_byte_array;
try
{
while (!done)
{
receive_byte_array = listener.Receive(ref groupEP);
received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
string aa = received_data.ToString().Trim();
if ( aa != "")
{
SetText("a\r\nb");
SetText(received_data.ToString().Trim());
aa = "";
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
listener.Close();
}
private delegate void SetTextCallback(string text);
private void SetText(string text)
{
try
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.BeginInvoke(d, new object[] { text });
}
else
{
SetText(text);
}
this.textBox4.Text = text;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
....
UI controls can only be changed by the thread they were created in. You need to check InvokeRequired (WinForms) or Dispatcher.CheckAccess() (WPF) and then call Invoke/BeginInvoke.
Normally when using c# and use multi threading you should use delegates to make things work and not violate Cross-thread politics.
In other words you are not allowed to use objects defined in one thread from other threads. To make this happen you should use delegates to force the owning thread to perform the task for the calling thread.
Instead of:
// display on TextBox4
this.textBox4.Text = "a\r\nb";
you could use this:
define this methods:
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
SetText(text);
}
this.textBox1.Text = text;
}
and call them from the tread like this
SetText("a\r\nb");
You can try changing your async function to use the current syncronisation context
return Task.Factory.StartNew<int>(() =>
{
if (WhatToDo == "OpenYourEars")
goListening();
return 1;
},
new CancellationToken(),
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
I am using async and await keywords to make an app responsive
I have the following code in the View when the property is raised in the ViewModel.
VIEW: Button Click Event
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
// Property Change
_viewModel.Words = await Task.Factory.StartNew(() => File.ReadAllLines("Words.txt").ToList()); // takes time to read about 3 - 4 seconds
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Factory.StartNew(() =>
{
var words = _viewModel.Words.ToList();
words.Reverse();
// Property Change
_viewModel.Words = words;
});
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Factory.StartNew(() =>
{
// Property Change
_viewModel.Words = _viewModel.Words.Shuffle().ToList();
});
break;
}
await Task.Factory.StartNew(() => DownloadSomething(_viewModel.Words)); // takes time to read about 30 - 40 seconds
button1.IsEnabled = true;
}
_viewModel.Progress gets updated in the View
VIEW: Private Method Takes 30 - 40 seconds To Complete
private void DownloadSomething(IEnumerable<string> words)
{
// Property Change
_viewModel.Progress = 0;
foreach (var word in words)
{
// Property Change
_viewModel.Word = word;
try
{
// Some code WebClient download code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
// Property Change
_viewModel.Progress++;
}
}
VIEW: Property Change Event Handled
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressValue),
// _viewModel.Progress);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Value = _viewModel.Progress;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Value = _viewModel.Progress;
break;
case "Words":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Max Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressMaxValue),
// _viewModel.Words.Count);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Maximum = _viewModel.Words.Count;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Contant on a Label Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<string>(SetLastWordValue),
// _viewModel.Word);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
labelLastWord.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
labelLastWord.Content = _viewModel.Word;
})
);
// This will throw an exception
// (it's on the wrong thread)
//labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
The UI updates perfectly for the progressBar1 and labelLastWord! I am facing a problem though, when progressBar1 and labelLastWord is updating the rest of the UI is frozen.
Is there a work around to fix this?
I greatly appreciate any help!
I strongly recommend you follow the guidelines in the Task-based Async Programming document. This is far better than just shunting work off to the thread pool via StartNew. If you do have CPU-bound operations, you could use Task.Run, but for everything else, use the existing async-ready endpoints.
Also, I find it useful to treat the entire VM as being in the UI context. So PropertyChanged is always raised in a UI context. Data binding in particular depends on this.
private async Task<List<string>> ReadAllLinesAsync(string file)
{
var ret = new List<string>();
using (var reader = new StreamReader(file))
{
string str = await reader.ReadLineAsync();
while (str != null)
{
ret.Add(str);
str = await reader.ReadLineAsync();
}
}
return ret;
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
_viewModel.Words = await ReadAllLinesAsync("Words.txt");
List<string> words;
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Run(() =>
{
words = _viewModel.Words.ToList();
words.Reverse();
});
_viewModel.Words = words;
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Run(() =>
{
words = _viewModel.Words.Shuffle().ToList();
});
_viewModel.Words = words;
break;
}
await DownloadSomething(_viewModel.Words);
button1.IsEnabled = true;
}
private async Task DownloadSomething(IEnumerable<string> words)
{
_viewModel.Progress = 0;
foreach (var word in words)
{
_viewModel.Word = word;
try
{
await ...; // async WebClient/HttpClient code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
_viewModel.Progress++;
}
}
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
progress1.Value = _viewModel.Progress;
break;
case "Words":
progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
As a closing note, I recommend you purchase and read thoroughly Josh Smith's MVVM book. You're using terms like "View" and "ViewModel", but the way you're using these components, you're completely avoiding all the advantages of the MVVM pattern.
I have a list Uri's that I want "clicked" To achieve this I"m trying to create a new web-browser control per Uri. I create a new thread per Uri. The problem I'm having is the thread end before the document is fully loaded, so I never get to make use of the DocumentComplete event. How can I overcome this?
var item = new ParameterizedThreadStart(ClicIt.Click);
var thread = new Thread(item) {Name = "ClickThread"};
thread.Start(uriItem);
public static void Click(object o)
{
var url = ((UriItem)o);
Console.WriteLine(#"Clicking: " + url.Link);
var clicker = new WebBrowser { ScriptErrorsSuppressed = true };
clicker.DocumentCompleted += BrowseComplete;
if (String.IsNullOrEmpty(url.Link)) return;
if (url.Link.Equals("about:blank")) return;
if (!url.Link.StartsWith("http://") && !url.Link.StartsWith("https://"))
url.Link = "http://" + url.Link;
clicker.Navigate(url.Link);
}
You have to create an STA thread that pumps a message loop. That's the only hospitable environment for an ActiveX component like WebBrowser. You won't get the DocumentCompleted event otherwise. Some sample code:
private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
Here is how to organize a message loop on a non-UI thread, to run asynchronous tasks like WebBrowser automation. It uses async/await to provide the convenient linear code flow and loads a set of web pages in a loop. The code is a ready-to-run console app which is partially based on this excellent post.
Related answers:
https://stackoverflow.com/a/22262976/1768303
https://stackoverflow.com/a/21775343/1768303
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplicationWebBrowser
{
// by Noseratio - https://stackoverflow.com/users/1768303/noseratio
class Program
{
// Entry Point of the console app
static void Main(string[] args)
{
try
{
// download each page and dump the content
var task = MessageLoopWorker.Run(DoWorkAsync,
"http://www.example.com", "http://www.example.net", "http://www.example.org");
task.Wait();
Console.WriteLine("DoWorkAsync completed.");
}
catch (Exception ex)
{
Console.WriteLine("DoWorkAsync failed: " + ex.Message);
}
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
// navigate WebBrowser to the list of urls in a loop
static async Task<object> DoWorkAsync(object[] args)
{
Console.WriteLine("Start working.");
using (var wb = new WebBrowser())
{
wb.ScriptErrorsSuppressed = true;
TaskCompletionSource<bool> tcs = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
tcs.TrySetResult(true);
// navigate to each URL in the list
foreach (var url in args)
{
tcs = new TaskCompletionSource<bool>();
wb.DocumentCompleted += documentCompletedHandler;
try
{
wb.Navigate(url.ToString());
// await for DocumentCompleted
await tcs.Task;
}
finally
{
wb.DocumentCompleted -= documentCompletedHandler;
}
// the DOM is ready
Console.WriteLine(url.ToString());
Console.WriteLine(wb.Document.Body.OuterHtml);
}
}
Console.WriteLine("End working.");
return null;
}
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
}
From my experience in the past the webbrowser does not like operating outside of the main application thread.
Try using httpwebrequests instead, you can set them as asynchronous and create a handler for the response to know when it is succesfull:
how-to-use-httpwebrequest-net-asynchronously
A simple solution at which the simultaneous operation of several WebBrowsers occurs
Create a new Windows Forms application
Place the button named
button1
Place the text box named textBox1
Set properties of text
field: Multiline true and ScrollBars Both
Write the following
button1 click handler:
textBox1.Clear();
textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
int completed_count = 0;
int count = 10;
for (int i = 0; i < count; i++)
{
int tmp = i;
this.BeginInvoke(new Action(() =>
{
var wb = new WebBrowser();
wb.ScriptErrorsSuppressed = true;
wb.DocumentCompleted += (cur_sender, cur_e) =>
{
var cur_wb = cur_sender as WebBrowser;
if (cur_wb.Url == cur_e.Url)
{
textBox1.AppendText("Task " + tmp + ", navigated to " + cur_e.Url + Environment.NewLine);
completed_count++;
}
};
wb.Navigate("https://stackoverflow.com/questions/4269800/webbrowser-control-in-a-new-thread");
}
));
}
while (completed_count != count)
{
Application.DoEvents();
Thread.Sleep(10);
}
textBox1.AppendText("All completed" + Environment.NewLine);