I have a project where I need to update a labels text from inside another classes method. It's worth noting that this method is being called from a Background worker thread.
I have tried passing in the text to update as a UserState Obj in the Workers ReportProgress(); method and then updating the label when the workers progress changed event is fired off on the main form. This works, but obviously only updates the labels text when the progress changed event is raised.
I have code that's loading/removing proxies constantly and I need a label to show this as it happens (as opposed to only updating when the bg workers progress changed event fires). Hopefully someone can help.
Thanks
Edit* Here's some code to make the problem a little easier to understand: -
public string Request(string action)
{
if (string.IsNullOrWhiteSpace(action))
{
return "";
}
HttpWebRequest req;
string response = string.Empty;
while (response.Equals(string.Empty) && proxy != null)
{
try
{
req = (HttpWebRequest)WebRequest.Create(action);
req.Proxy = proxy;
response = new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd();
}
catch (Exception ex)
{
RemoveProxy(proxy);
MessageBox.Show("Proxy Removed: " + proxy.Address.ToString());
proxy = GenNewProx();
MessageBox.Show("New proxy" + proxy.Address.ToString());
}
}
return response;
}
^^^ - Where i need to set the labels text, using Msgboxs at the moment but updating a label on the main form is obviously preferable
foreach (string url in URLs)
{
result.URL = url;
result.Shares = grabber.GetFacebookShares(url);
result.Tweets = grabber.GetTweetCount(url);
result.PlusOnes = grabber.GetPlusOnes(url);
bgWorker.ReportProgress((outputGridView.Rows.Count * 100) / importGridView.Rows.Count, result);
}
^^^ - Inside the bg workers do_work method on the main form.
2nd Edit*
I'm a bit new to events but could i not fire off a custom event say Proxy_Changed everytime i switch proxys and pass in a string argument with the new proxy/msg w.e and then subscribe to this event in the main form, then set the label on the main forms text = the string args when this event fires off? I'm probably talking jibberish tbh though :/
Here's what I think the important parts of your class that needs to do the background works looks like:
public class Grabber
{
public event EventHandler<MyArgs> NotifyParentUI;
// other code.....
public string Request(string action)
{
if (string.IsNullOrWhiteSpace(action))
{
return "";
}
HttpWebRequest req;
string response = string.Empty;
while (response.Equals(string.Empty) && proxy != null)
{
try
{
req = (HttpWebRequest)WebRequest.Create(action);
req.Proxy = proxy;
response = new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd();
}
catch (Exception ex)
{
RemoveProxy(proxy);
NotifyParentUI(this, new MyArgs()
{ Message = string.Format("Proxy Removed: {0}", proxy.Address.ToString()) });
proxy = GenNewProx();
NotifyParentUI(this, new MyArgs()
{ Message = string.Format("New Proxy: {0}", proxy.Address.ToString()) });
}
}
return response;
}
}
In your main form you have a method to update your label that is thread-safe:
public void UpdateMyLabel(object sender, MyArgs ea)
{
this.Invoke(new MethodInvoker(
delegate()
{
labelControl1.Text = ea.Message;
}
));
}
Also in the main form you must create an instance of your "grabber":
Grabber grabber = new Grabber();
grabber.NotifyParentUI += UpdateMyLabel;
You should have a method that runs on its own thread:
public void ThreadProc()
{
// other code before this....
foreach (string url in URLs)
{
result.URL = url;
result.Shares = grabber.GetFacebookShares(url);
Thread.Sleep(0); // may want to take the Sleeps out
result.Tweets = grabber.GetTweetCount(url);
Thread.Sleep(0);
result.PlusOnes = grabber.GetPlusOnes(url);
Thread.Sleep(0);
}
}
Here's how you start the thread in the part of your main form:
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
As a side note, if you need to pass data to your Thread, look here:
Passing Data to Threads and Retrieving Data from Threads
Use Invoke method to run anonymous function on UI thread to update the label. For example:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, args) =>
{
for (int i = 0; i < 10000; ++i)
{
DoSomeWorkInBackground();
// Update the label in UI thread
MyOtherFormInstance.Invoke((MethodInvoker)delegate()
{
MyOtherFormInstance.SetLabelText(i);
});
DoSomOtherWorkInBackground();
}
};
In your Form:
public void SetLabelText(int i)
{
myLabel.Text = i.ToString();
// not sure that this needed, but try:
myLabel.Invalidate();
}
It sounds from your question and subsequent comments to other answers that you are running your code within a WinForms project, correct me if I am wrong? In a winform the main program thread is usually always static (static void Main()) therefore you must make your EventHandler static also to avoid null exceptions. I believe this will resolve your issue as it sounds like the rest of your code is correct?
Related
I am trying to write an application which transfers data between 2 systems. This application is used by a user, so it is WinForm application. When data transfering is started by a click of the user, the GUI gets frozen even though I start the data transfering in another thread. I am doing something wrong but I couldnt figure it out. here is my SIMPLIFIED code below....
What am I doing wrong?
// Button Click Event
private void btnStart_Click(object sender, EventArgs e)
{
StartThread();
}
// This starts the threaad.
public static void StartThread()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
}
// This is a simplified code.
public static void Test()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
while (continue_)
{
i++;
Thread.Sleep(5000);
FormMain.dal.ExecuteQuery("INSERT INTO A_TEST VALUES('"+i+"')",null,CommandType.Text,out msg);
}
}
Your StartThread() method includes a Thread.Sleep(5000) ... this is happening in your button click method, thus is making the UI thread sleep. Also, it looks like you have an infinite loop on the UI thread as continue_ never gets set to false
I'm guessing what you're trying to achieve here, but this may help:
public static void StartThread()
{
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
Let's have a look at this block in StartThread:
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
You have a while loop dependen on continue_, but you never change it to false. So you get first of all an infinite loop, which causes the GUI to freeze.
why you are modifying i, but never using it, so just remove it.
You don't need also Thread.Sleep(5000);. However, if you really want to wait a time period, you can use an async delay. It will give the GUI free, so that the GUI works until the delay is finished. But for this, you have to declare StartThread as async.
In your:
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
You use Thread.Sleep(5000);
This however still targets your main thread.
I would suggest you to remove this line.
Also, why do you use the variable 'i' while you never use it?
Having an issue getting data from a form control from within a thread. I need to access the data, then modify it.
The following doesn't work I'm aware, but I used it as an example to see what I'm trying to do.
Thread t = new Thread(() => {
foreach (ListViewItem row in listView1.Items)
{
row.SubItems[0].Text = "Checking";
Thread.Sleep(2000);
}
});
t.Start();
I've read the MSDN documentation on making thread safe calls, but I can't seem to get access to the actual list view control. The examples I've seen use delegates to "update" controls, but I need access to the data in the controls before I update the data in them.
Edit:
I'd like to see an example, or a link to an example, detailing how to get access to the ListView1 form control in the foreach loop.
You need to use Invoke pattern, in order to be able to access any UI element or its properties from the thread other then main UI thread. All UI controls on windows allways run on the main thread, to handle message chain correctly between OS and UI presented on the screen.
The (quickly written) example I was talking about, this assumes that you do not need to really use the controls, I included a function that is based off tigran's link
Thread t = new Thread(() => UpdateText(listBox1.Items));
t.Start();
private void UpdateText(ListBox.ObjectCollection items)
{
foreach (var item in items)
{
SetText(item.ToString());
Thread.Sleep(1000);
}
}
You can't do what you want to do. All accesses and updates to UI must go in UI thread. It is mandatory.
What you can do is writing your raw data into cache on UI then processing your cache and callbacks to UI after all processings are finished.
public class CacheData {
private object row;
public CacheData(object row)
{
//initialization
}
public static ProcessedData ProcessData(List<CacheData> dataToProcess)
{
return new ProcessedData();
}
}
public class ProcessedData { }
private void AccessControl()
{
ListView list = new ListView();
List<CacheData> cache = new List<CacheData>();
//Filling the cache on UI
foreach (var row in list.Items)
{
cache.Add(new CacheData(row));
}
//Process result async and then invoke on UI back
System.ComponentModel.BackgroundWorker bg = new System.ComponentModel.BackgroundWorker();
bg.DoWork += (sender,e) => {
e.Result = CacheData.ProcessData(cache);
};
bg.RunWorkerCompleted += (sender, e) => {
//If you have started your bg from UI result will be invoked in UI automatically.
//Otherwise you should invoke it manually.
list.Dispatcher.Invoke((Action) delegate {
//pass e.result to control here)
},null);
};
bg.RunWorkerAsync();
}
Method 1:
Use Invoke like Tigran describes.
For Winforms this would look like:
Thread t = new Thread(() =>
{
if (!Dispatcher.CurrentDispatcher.CheckAccess())
{
Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(() =>
{
foreach (ListViewItem row in listView1.Items)
{
row.SubItems[0].Text = "Checking";
Thread.Sleep(2000);
}
}),
DispatcherPriority.ApplicationIdle,
null);
}
else
{
foreach (ListViewItem row in listView1.Items)
{
row.SubItems[0].Text = "Checking";
Thread.Sleep(2000);
}
}
});
t.Start();
The CheckAccess() Call returns true if called from the UI-Thread otherwise false.
The Dispatcher Class is located in the "System.Windows.Threading" Namespace in the "WindowsBase" NET. Assembly
Dispatcher info copied from: https://stackoverflow.com/a/4429009/1469035
Edit: Changed code to WinForms.
Edit: Code Fixed.
Method 2:
Use a Callback:
Untested Code:
public partial class Form1 : Form
{
private delegate void SetCallback(ListViewItem row, string text);
public Form1()
{
InitializeComponent();
}
private void SomeMethod()
{
Thread t = new Thread(() =>
{
foreach (ListViewItem row in listView1.Items)
{
if (listView1.InvokeRequired)
{
SetCallback d = new SetCallback(SetText);
this.Invoke(d, new object[] { row, "Checking" });
}
Thread.Sleep(2000);
}
});
t.Start();
}
private void SetText(ListViewItem row, string text)
{
row.SubItems[0].Text = text;
}
}
AFAIK readonly Access to Controls from Threads other than the UI-Thread is allowed in Winforms. So you can check any Control-Property you want and pass the required information to the Delegate.
And even if Reading doents work that way, you can just make another Delegate that has a return value. The Invoke() Method returns an object:
Similar to this:
private delegate object GetCallback(ListViewItem row);
private object o;
...
GetCallback g = new GetCallback(GetText);
o = this.Invoke(g, new object[] { row });
private string GetText(ListViewItem row)
{
return row.SubItems[0].Text;
}
Derived From: Link
I have WPF Control hosted in winform which have Menu and some labels.
the WPF controls connect to internet to download some data.
I divide the code into two steps
first just set the control properties
second connect to net
the second runs inside thread,
but Winform controls doesn't take place on the form until the WPF control finish his two steps.
I have tried many approach to make it thread-able
but all ways goes to same destination.
CODE
1- Load WPF Control
private void MDI_Load(object sender, EventArgs e)
{
MenuManager.FillMenu(MainMenu); // I have filled WinForm Menu first, but it doesn't appear until WPF finish
#region = WPF Control =
wpfManager.AddweatherControl();
wpfManager.weatherManager.Start(); // This have to run in another thread
#endregion
}
2- wpfManager.weatherManager.Start
public void Start()
{
//var tsk = System.Threading.Tasks.Task.Factory.StartNew(GetWeather);
//tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); },
// System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.OnlyOnFaulted,
// System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
//System.Threading.Thread t = new System.Threading.Thread(
// () => weatherControl.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(GetWeather))
// );
//t.SetApartmentState(System.Threading.ApartmentState.STA);
//t.Start();
weatherControl.Dispatcher.BeginInvoke(new Action(GetWeather), new object[] { });
}
void GetWeather()
{
#region = Weather =
Yweather.Getweather(UserManager.CurrentUser.Preferences.WeatherCity);
if (Yweather.Online && Yweather.IDayForecast.Count > 0)
{
weatherControl.CurrentDegree.Text = Yweather.IDayForecast[0].CurrentTemperature.ToString();
weatherControl.WeatherTypeName.Text = Yweather.IDayForecast[1].WeatherText;
weatherControl.AllDayDegree.Text = Yweather.IDayForecast[1].LowTemperature + " - " + Yweather.IDayForecast[1].HighTemperature;
weatherControl.WeatherType.Source = wpfManager.BitmapToImageSource(Yweather.IDayForecast[0].Image);
xWeatherDay weatherday1 = weatherControl.OhterDaysPanel.Children[0] as xWeatherDay;
weatherday1.AllDayDegree.Text = Yweather.IDayForecast[2].LowTemperature + " - " + Yweather.IDayForecast[2].HighTemperature;
weatherday1.WeatherType.Source = wpfManager.BitmapToImageSource(Yweather.IDayForecast[2].Image);
}
else
{
weatherControl.CurrentDegree.Text = "0";
weatherControl.WeatherTypeName.Text = "NAN";
weatherControl.AllDayDegree.Text = "0 - 0";
weatherControl.WeatherType.Source = wpfManager.BitmapToImageSource(Yweather.OfflineImage);
}
#endregion
}
It looks like from the code you posted that the delay is due to running GetWeather on the UI thread. Assuming that weatherControl is an instance of the WPF control, this runs on the UI thread because that is the thread that its dispatcher belongs to.
If you want to run code on a background thread, one easy way to do so is to use a BackgroundWorker. You could use it something like this:
public void Start()
{
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
GetWeather();
// put the results of getting the weather in to args.Result
};
worker.RunWorkerCompleted += (sender, args) =>
{
// use args.Result to update the UI
};
worker.RunWorkerAsync();
}
The code in the DoWork event handler runs on a background thread, while the code in the RunWorkerCompleted event handler runs on the UI thread.
I'm trying to lock the main form while a please wait box is shown on the screen, but it won't work. Here's my dilemma.
I have 2 forms. The main form that the user clicks a refresh button to load the list of SQL Servers, and a Please wait form that shows while it's loading the list. The SQL Server thread is a separate thread by default while using C# and it locks out the main thread in order to process the SQL request.
I can add a background worker, but then I can't update my combo box to show the list as its a UI control. If I use a handler for that, my show_dialog() for the please wait box will stop locking down the main form.
How is it even possible to lock this form down without the left click queue being run after the main thread goes active again? I added the code that needs to be executed while the user waits.
public void PullServers()
{
bool ServersFound = false;
foreach (string Value in SQL.LocateSqlInstances())
{
this.cmbServer.Items.Add(Value);
ServersFound = true;
}
if (!ServersFound)
{
this.cmbServer.Items.Add(Strings.Lang("ddServerNoneFound"));
this.cmbServer.SelectedIndex = 0;
}
else
{
if (!s.empty(General.setting("SQLSERVER")))
{
this.cmbServer.Text = General.setting("SQLSERVER");
}
else
{
this.cmbServer.SelectedIndex = 0;
}
}
this.picRefreshServers.Image = Properties.Resources.Refresh;
}
public static Array LocateSqlInstances()
{
using (DataTable sqlSources = System.Data.Sql.SqlDataSourceEnumerator.Instance.GetDataSources())
{
string Servers = null;
foreach (DataRow source in sqlSources.Rows)
{
string instanceName = source["InstanceName"].ToString();
if (!s.empty(instanceName))
{
Servers += source["ServerName"].ToString() + "\\" + instanceName + "[[SERVBREAK]]";
}
}
string[] ServersList = Servers.Split(new string[] { "[[SERVBREAK]]" }, StringSplitOptions.RemoveEmptyEntries);
return ServersList;
}
}
I think you are on the right track with a BackgroundWorker. I have found the following pattern to work well for me.
In your main form, you need to perform the following steps.
Create a BackgroundWorker to perform the long running operation.
Start the BackgroundWorker.
Display the waiting form as a modal dialog.
// Step 1:
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
// Step 2:
bg.RunWorkerAsync();
// Step 3:
waitingForm = new WaitingForm();
waitingForm.ShowDialog();
As you know, you can't update the UI from the bg_DoWork handler since it does not run on the UI thread. So just get the data you need here and pass it on to the the bg_RunWorkerCompleted handler using the e.Result parameter.
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
Array servers = SQL.LocateSqlInstances();
e.Result = servers;
}
The bg_RunWorkerCompleted runs on the UI thread so it is safe to update your controls here. This is where you should close the waiting form and then update your UI.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Close the Waiting form.
waitingForm.Close();
// Retrieve the result of bg_DoWork().
Array servers = e.Result as Array;
bool ServersFound = false;
foreach (string Value in servers)
{
this.cmbServer.Items.Add(Value);
ServersFound = true;
}
if (!ServersFound)
{
this.cmbServer.Items.Add(Strings.Lang("ddServerNoneFound"));
this.cmbServer.SelectedIndex = 0;
}
else
{
if (!s.empty(General.setting("SQLSERVER")))
{
this.cmbServer.Text = General.setting("SQLSERVER");
}
else
{
this.cmbServer.SelectedIndex = 0;
}
}
this.picRefreshServers.Image = Properties.Resources.Refresh;
}
I have the following code in a class library. And I wait for a call back into my main application. I am making a DownloadStringAsync call so I have to wait a few seconds to get the callback after it has finished. I have a 3 of these calls to wait for, so in my main application I am using AutoResetEvent to wait all of them to finish. So I will block until they have been set in the callback function.
However, after testing the callback don't get called. I am thinking when the code gets blocked by the AutoResetEvent its blocking the DownloadStringAsync. As when I comment out this code everything works fine.
So I think as soon as I make a call to: objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber);
And when the code reaches here: handle.WaitOne();
It will block the code in the class library.
Many thanks for any advice.
In my class library code sample.
// Event handler that makes a call back in my main application
// Event handler and method that handles the event
public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
// The method that raises the event.
private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
if (NoGatewayCompletedEvent != null)
{
NoGatewayCompletedEvent(this, e);
}
}
// Start the Async call to find if NoGateway is true or false
public void NoGatewayStatus(string sipUsername, string phoneNumber)
{
string strURL = string.Format("http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber);
if (!wc.IsBusy)
{
try
{
string errorMsg = string.Empty;
wc.DownloadStringAsync(new Uri(strURL));
}
catch (WebException ex)
{
Console.WriteLine("IsNoGateway: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("IsNoGateway: " + ex.Message);
}
}
else
{
Console.WriteLine("WebClient: IsNoGateWay(): Busy please try again");
}
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.Result == "No gateway")
{
OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.VALIDATION_FAILED));
Console.WriteLine("NoGatway() DownloadedCompleted: " + e.Result);
}
else
{
OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.OK));
Console.WriteLine("NoGateway() DownloadCompleted: " + e.Result);
}
}
else
{
this.OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.SERVER_FAILED));
Console.WriteLine("No Gateway: DownloadCompleted() Error: " + e.Error.Message);
}
}
In my main application I register this callback. And wait for the for the result. Then set the AutoResetEvent.
ManualResetEvent[] waitValidateCallResponse = new ManualResetEvent[]
{ new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false) };
// Event handler for NoGateway event
private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
waitValidateCallResponse[0].Set();
}
The part when I am calling and blocking.
NoGateway objNoGateway = new NoGateway()
objNoGateway.NoGatewayCompletedEvent += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);
objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber);
// Block here - Wait for all reponses to finish before moving on
waitEvent.WaitOne(5000, true);
Console.WriteLine("All thread finished");
======================== Edit and added the other 2 callbacks as not to confuse the issue of me just having only one ======================
private void OnCalledNumberBlockedCompleted(object sender, CalledNumberBlockedEventArgs e)
{
Console.WriteLine("OnCalledNumberBlockedCompleted: " + e.CalledNumberBlocked);
waitValidateCallResponse[1].Set();
}
private void OnValidTelephoneNumberCompleted(object sender, ValidTelephoneNumberEventArgs e)
{
Console.WriteLine("OnValidTelephoneNumberCompleted: " + e.validTelephoneNumber);
waitValidateCallResponse[2].Set();
}
Is it as simple as: you always call Set on index 0?
private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
waitValidateCallResponse[0].Set();
}
Try something along these lines:
public void NoGatewayStatus (string sipUsername, string phoneNumber) {
string strURL = string.Format( "http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber );
ManualResetEvent wait1 = new ManualResetEvent( false );
WebClient wc = new WebClient();
Thread thr = new Thread( DownloadSomeStuff );
thr.Start( new DlArguments( strURL, wait1 ) );
// do the other three
if ( !wait1.WaitOne( 10000 ) ) {
Console.WriteLine( "DownloadSomeStuff timed out" );
return;
}
if ( !wait2.WaitOne( 10000 ) ) {
Console.WriteLine( "DownloadOtherStuff timed out" );
return;
}
if ( !wait3.WaitOne( 10000 ) ) {
Console.WriteLine( "DownloadMoreStuff timed out" );
return;
}
}
public void DownloadSomeStuff (object p_args) {
DlArguments args = (DlArguments) p_args;
try {
WebClient wc = new WebClient();
wc.DownloadString( args.Url );
args.WaitHandle.Set();
} catch ( Exception ) {
// boring stuff
}
}
private class DlArguments
{
public DlArguments (string url, ManualResetEvent wait_handle) {
this.Url = url;
this.WaitHandle = wait_handle;
}
public string Url { get; set; }
public ManualResetEvent WaitHandle { get; set; }
}
Does this do it?
After lots of edits, I think I might understand the problem. Windows Forms applications have one main thread; this thread is used to process messages. So when your main thread is blocking, your application can't receive events. And you use WaitOne to keep the main thread blocked.
I'd move the WaitOne() checks to a separate timer thread.
Or you could wait for a limited time, and instruct the application to process messages in between:
foreach (WaitHandle handle in waitValidateCallResponse)
{
while (!handle.WaitOne(300))
Application.ProcessMessages();
Console.WriteLine("events.WaitOne(): " + handle.ToString());
}
The later approach is not something you should do in a library though. It's something of an anti-pattern I think.
The snippet code is peculiar
// Event handler that makes a call back in my main application
// Event handler and method that handles the event
public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
// The method that raises the event.
public void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
if (NoGatewayCompletedEvent != null)
{
NoGatewayCompletedEvent(this, e);
}
}
However in the 2nd last snippet, you attach an event handler for this event as follows.. OnNoGatewayCompleted seems to be a helper method to raise the event.. (it should not be public) but here it seems you have the event handler raise the event again. Unless you have 2 methods named OnNoGatewayCompleted (I'm hoping not)
objNoGateway.NoGatewayCompletedEvent
+= new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);
If you're looking for the waitHandles to be signalled in the event handler, shouldn't the methods OnCalledNumberBlockedCompleted be hooked up to the event instead.
PS: As Marc pointed out.. use WaitHandle.WaitAll ( the for loop demands that the async operations complete in order which may not be the case )
Use WaitHandle.WaitAny(handleArray); to wait on all the handles in the handle array instead of handle.WaitOne(); in a loop