Sorry for the title, i didn't find it easy to resume.
My issue is that I need to implement a c# dll that implements a 'scan' method, but this scan, when invoked, must not block the main thread of the application using the dll. Moreover, it is a duty that after the scan resolves it rises an Event.
So my issue (in the deep) is that i'm not so experienced at c#, and after very hard investigation i've come up with some solutions but i'm not very sure if they are the "right" procedures.
In the dll i've come up with:
public class Reader
{
public delegate void ReaderEventHandler(Object sender, AlertEventArgs e);
public void Scan(String ReaderName)
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = null;
//Code with blocking scan function here
if (ScanFinnished)
{
alertEventArgs.uuiData = "Scan Finnished!";
}
alertEventArgs.cardStateData = readerState[0].eventState;
ReaderEvent(new object(), alertEventArgs);
}
public event ReaderEventHandler ReaderEvent;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
private uint cardState = 0;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
public uint cardStateData
{
get { return cardState; }
set { cardState = value; }
}
#endregion
}
While in the main app I do:
Reader reader;
Task polling;
String SelectedReader = "Some_Reader";
private void bButton_Click(object sender, EventArgs e)
{
reader = new Reader();
reader.ReaderEvent += new Reader.ReaderEventHandler(reader_EventChanged);
polling = Task.Factory.StartNew(() => reader.Scan(SelectedReader));
}
void reader_EventChanged(object sender, AlertEventArgs e)
{
MessageBox.Show(e.uuiData + " Estado: " + e.cardStateData.ToString("X"));
reader.Dispose();
}
So here, it works fine but i don't know if it's the proper way, in addition i'm not able to handle possible Exceptions generated in the dll.
Also tried to use async/await but found it difficult and as I understand it's just a simpler workaround Tasks.
What are the inconvinients of this solution? how can i capture Exceptions (are they in other threads and that's why i cant try/catch them)? Possible concept faults?
When your class sends events, the sender usually is that class, this. Having new object() as sender makes absolutely no sense. Even null would be better but... just use this.
You shouldn't directly raise events as it might result in race conditions. Might not happen easily in your case but it's just a good guideline to follow. So instead of calling ReaderEvent(new object(), alertEventArgs); call RaiseReaderEvent(alertEventArgs); and create method for it.
For example:
private void RaiseReaderEvent(AlertEventArgs args)
{
var myEvent = ReaderEvent; // This prevents race conditions
if (myEvent != null) // remember to check that someone actually subscribes your event
myEvent(this, args); // Sender should be *this*, not some "new object()".
}
Though I personally like a bit more generic approach:
private void Raise<T>(EventHandler<T> oEvent, T args) where T : EventArgs
{
var eventInstance = oEvent;
if (eventInstance != null)
eventInstance(this, args);
}
Which can then be used to raise all events in same class like this:
Raise(ReaderEvent, alertEventArgs);
Since your scan should be non-blocking, you could use tasks, async/await or threads for example. You have chosen Tasks which is perfectly fine.
In every case you must understand that when you are not blocking your application, your application's main thread continues going like a train. Once you jump out of that train, you can't return. You probably should declare a new event "ErrorEvent" that is raised if your scan-procedure catches an exception. Your main application can then subscribe to that event as well, but you still must realize that those events are not (necessarily) coming from the main thread. When not, you won't be able to interact with your GUI directly (I'm assuming you have one due to button click handler). If you are using WinForms, you'll have to invoke all GUI changes when required.
So your UI-thread safe event handler should be something like this:
void reader_EventChanged(object sender, AlertEventArgs e)
{
if (InvokeRequired) // This true for others than UI Thread.
{
Invoke((MethodInvoker)delegate
{
Text = "My new title!";
});
}
else
Text = "My new title!";
}
In WPF there's Dispather that handles similar invoking.
Related
I have an application with a gui and a Rich Text Box where I output what the program is currently doing since data processing can be quite long.
I tried two approaches for that:
1 In the Backgroundworker method I can just call the following code fine:
GlobalVar.backgroundWorkerAppendText = task.Build_CSV_List();
Processchange();
Whereas I cannot use Form1.Processchange(); in the helper class due to the non static context
2 Therefore I tried to create my very first eventhandler.
The Idea was that helper.UpdateConsole() would raise an event
public event EventHandler OnConsoleUpdate;
public void Consoleupdate()
{
OnConsoleUpdate(this, EventArgs.Empty);
}
to which the Backgroundworker listens and then calls Processchange from its context
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks();
Helper helper = new Helper();
helper.OnConsoleUpdate += Processchange;
task.DoSomeStuffHere()
}
public void Processchange(object sender=null, EventArgs e=null)
{
//MessageBox.Show(GlobalVar.backgroundWorkerAppendText);
GlobalVar.next = false;
backgroundWorker1.ReportProgress(1);
while (GlobalVar.next == false)
{
helper.TimeBreaker(100,"ms");
}
}
Unfortunately this was was not successful. As soon as rising the Event I get the errormessage System.NullReferenceException which -after googling- leads me to the conclusion that there is no listerner attached to the event eventhouh I attached it in the Backgroundworker Do work.
Edit: the OnConsoleUpdate() == null as shown on the screenshot below
event = null
The helper is in another class file "helpers" which might be important for a solution.
i hope you guys can help me out.
Welcome to SO!
A few things immediately jump to mind.
First, let's get the event issue out of the way. You've got the correct approach - you need an event and method to call it, but that method should check if the event is null.
Basically, do this:
public event EventHandler OnConsoleUpdate;
public void ConsoleUpdate()
{
OnConsoleUpdate?.Invoke(this, EventArgs.Empty);
}
The above makes use of ?, a null-condition operator. You can read more about it on this MSDN page.
Second thing... it's unclear what your background worker actually IS. It sounds like it's some kind of custom class you crated? The reason it's important is because .NET actually has a BackgroundWorker class used for running operations... well, in the background. It also has an OnProgressChanged event which you can hook up to which could be used to update the UI (just remember to set the WorkerReportsProgress property to true). And to use the BackgroundWorker mentioned above, you shouldn't need to create any events of your own.
Here's how you can use the standard .NET BackgroundWorker:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
void StartBackgroundTask()
{
worker.DoWork += worker_DoWork;
//if it's possible to display progress, use this
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
//what to do when the method finishes?
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
//start!
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
//perform any "finalization" operations, like re-enable disabled buttons
//display the result using the data in e.Result
//this code will be running in the UI thread
}
//example of a container class to pass more data in the ReportProgress event
public class ProgressData
{
public string OperationDescription { get; set; }
public int CurrentResult { get; set; }
//feel free to add more stuff here
}
void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
//display the progress using e.ProgressPercentage or e.UserState
//this code will be running in the UI thread
//UserState can be ANYTHING:
//var data = (ProgressData)e.UserState;
}
void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//this code will NOT be running in the UI thread!
//you should NOT call the UI thread from this method
int result = 1;
//perform calculations
for (var i = 1; i <= 10; i++)
{
worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result });
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
result *= i;
}
e.Result = result;
}
Now, the thing about the BackgroundWorker class is that it is rather old, and with current .NET versions you can use the async / await keywords to easily handle background operations and UI updates, but this probably is going outside the bounds of this question. That said, the existence of async / await doesn't invalidate the use of BackgroundWorker which is pretty simple in its usage.
There's one more worrisome thing in your code.
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StandardTasks task = new StandardTasks(); //<- you create a task
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper event
task.DoSomeStuffHere(); // <- you do stuff with the task... but the task doesn't know about your helper above! Does `StandardTasks` use `Helper`? If so, how?
}
Do note that events, unless made static, aren't global. So hooking up to an event in one instance of a class won't cause another instance of that class to "fire" that event. It seems one way to fix your issues would be to make the StandardTasks class take Helper as one of the constructor parameters, so the code would look like this:
Helper helper = new Helper(); // <- you create a helper
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper class event to actually do something
StandardTasks task = new StandardTasks(helper); //<- you create a task which will use the helper with the hooked up event above
I had write a little application on c# to reading some plc data by using ethernet protocol. Ethernet socket, open and close are stored inside a .dll library.
Now, i using this public method:
public static string readdata()
{
try
{
...
return (plcdata());
}
catch
{}
}
My doubt: if the plcdata() (that is a method of a .dll) waiting a few second (for istance slow comunication ...) my application may be frozen.
So, i try to add a EventHandler on string returned like this:
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
auxDataTextBox.Text = plcdata();
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
In this case when the "plcdata()" changed, the auxDataIncoming will be raise.
It is correct? Or is better make a timeout control? Or make new thread?
Thanks a lot for yours opinion
Your change won't make a difference, it' still all running on the UI thread. To make plcdata() a non-blocking call you would need to fire it off on another thread e.g.
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
Task.Factory.StartNew(() => {
return plcData();
}).ContinueWith(task => {
auxDataTextBox.Text = task.Result;
}, null, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
This will not unfreeze your application. The effect will be exactly the same. This is because you are still running the plcdata on your UI thread.
The whole event structure you set up does not make sense at all.
You should look into multithreading. A very easy way to do this is using a BackgroundWorker.
I'm implementing a visual version of Tracert (as a learning exercise) in WPF where results go to a listbox. The issues are (1) the listbox bound to tracertDataView is not updating, but (2) my entire application hangs.
I'm sure #2 is a threading issue but I'm not sure how to correct it (in the right way). In addition I'm not sure my technique of updating / binding the results of "DoTrace" are correct.
Here is my datasource in App.xaml
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=TracertResultNodes}"
x:Key="tracertDataView" />
</Window.Resources>
App.xaml.cs
public partial class App : Application
{
private ObservableCollection<TracertNode> tracertResultNodes = new ObservableCollection<TracertNode>();
public void AppStartup(object sender, StartupEventArgs e)
{
// NOTE: Load sample data does work correctly.. and displays on the screen.
// subsequent updates do not display
LoadSampleData();
}
private void LoadSampleData()
{
TracertResultNodes = new ObservableCollection<TracertNode>();
TracertNode t = new TracertNode();
t.Address = new System.Net.IPAddress(0x2414188f);
t.RoundTripTime = 30;
t.Status = System.Net.NetworkInformation.IPStatus.BadRoute;
TracertResultNodes.Add(t);
}
public ObservableCollection<TracertNode> TracertResultNodes
{
get { return this.tracertResultNodes; }
set { this.tracertResultNodes = value; }
}
}
Here is the MainWindow code
public partial class MainWindow : Window
{
CollectionViewSource tracertDataView;
TraceWrapper _tracertWrapper = null;
public MainWindow()
{
InitializeComponent();
_tracertWrapper = new TraceWrapper();
tracertDataView = (CollectionViewSource)(this.Resources["tracertDataView"]);
}
private void DoTrace_Click(object sender, RoutedEventArgs e)
{
((App)Application.Current).TracertResultNodes = _tracertWrapper.Results;
_tracertWrapper.DoTrace("8.8.8.8", 30, 50);
}
}
FYI Internal implementation Detail of instance object "traceWrapper.DoTrace"
/// <summary>
/// Trace a host. Note that this object internally calls the Async implementation of .NET's PING.
// It works perfectly fine in a CMD host, but not in WPF
/// </summary>
public ObservableCollection<TracertNode> DoTrace(string HostOrIP, int maxHops, int TimeOut)
{
tracert = new Tracert();
// The following is triggered for every host that is found, or upon timeout
// (up to 30 times by default)
AutoResetEvent wait = new AutoResetEvent(false);
tracert.waiter = wait;
tracert.HostNameOrAddress = HostOrIP;
tracert.Trace();
this.Results = tracert.NodeList;
while (tracert.IsDone == false)
{
wait.WaitOne();
IsDone = tracert.IsDone;
}
return tracert.NodeList;
}
I don't understand how u used AutoResetEvent, i guess it is not supposed to be used in this way :)
But since Trace run already in another thread, are you sure there is not an event "OnTracertComplete" or something like that in your Tracert class?
If there is not, why you just don't put a DispatchTimer into your application?
That timer would periodically poll until tracert.IsDone becomes true.
If you block the execution of the application thread until an operation completes, you block the execution of the window event loop so window will never be updated.
Another important thing: you cannot update ObservableCollections from another thread.
Be careful and be sure that everything that is updated in the WPF window is executed from the same thread of the window. Don't know what your Trace class do exactly, but your problem here seems to be of course the wait loop, that don't makes sense in a GUI application.
Use notification events or a timer to poll the result. A timer with 1 second resolution seems good to me for this particular implementation and the performance inpact is absolutely minimal.
This is a possible implementation if you are able to modify the Tracert class.
public delegate void TracertCallbacHandler(Tracert sender, TracertNode newNode);
public class Tracert
{
public event TracertCallbacHandler NewNodeFound;
public event EventHandler TracertCompleted;
public void Trace()
{
....
}
// This function gets called in tracert thread\async method.
private void FunctionCalledInThreadWhenPingCompletes(TracertNode newNode)
{
var handler = this.NewNodeFound;
if (handler != null)
handler(this, newNode);
}
// This function gets called in tracert thread\async methods when everything ends.
private void FunctionCalledWhenEverythingDone()
{
var handler = this.TracertCompleted;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
And here is the code to run the tracert,
This is TracertWrapper.
// Keep the observable collection as a field.
private ObservableCollection<TracertNode> pTracertNodes;
// Keep the instance of the running tracert as a field, we need it.
private Tracert pTracert;
public bool IsTracertRunning
{
get { return this.pTracert != null; }
}
public ObservableCollection<TracertNode> DoTrace(string hostOrIP, int maxHops, int timeOut)
{
// If we are not already running a tracert...
if (this.pTracert == null)
{
// Clear or creates the list of tracert nodes.
if (this.pTracertNodes == null)
this.pTracertNodes = new ObservableCollection<TracertNode>();
else
this.pTracertNodes.Clear();
var tracert = new Tracert();
tracert.HostNameOrAddress = hostOrIP;
tracert.MaxHops = maxHops;
tracert.TimeOut = timeOut;
tracert.NewNodeFound += delegate(Tracert sender, TracertNode newNode)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.
SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertNodeFound(this.pTracertNodes, newNode);
}, null);
};
tracert.TracertCompleted += delegate(object sender, EventArgs e)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.
SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertCompleted();
}, null);
};
tracert.Trace();
this.pTracert = tracert;
}
return this.pTracertNodes;
}
protected virtual void OnTracertCompleted()
{
// Remove tracert object,
// we need this to let the garbage collector being able to release that objects.
// We need also to allow another traceroute since the previous one completed.
this.pTracert = null;
System.Windows.MessageBox.Show("TraceRoute completed!");
}
protected virtual void OnTracertNodeFound(ObservableCollection<TracertNode> collection, TracertNode newNode)
{
// Add our tracert node.
collection.Add(newNode);
}
The issue is that not only is the listbox not updating, but my entire application hangs.
This is probably due to the AutoResetEvent blocking in DoTrace. You explicitly call Wait.WaitOne(); on the event handle, but as far as I can tell, never Set() it. This will cause the application to hang forever as soon as you call Wait.WaitOne().
It sounds like tracert.Trace() is an asynchronous method. Does it include some form of callback/event to notify you upon completion? If so, you should use that, not poll in a loop, to determine when it's complete.
(1) the listbox bound to tracertDataView is not updating
You won't see the updates to your listbox, as you're assigning a new collection to the TracertResultNodes property, the binding in this case simply does not work, because a new collection was assigned.
In addition to ensuring that the collection is updated in the same thread as outlined by Salvatore below, you should only add or remove items from the existing collection, and NOT assign the new one generated by your DoTrace function.
private void DoTrace_Click(object sender, RoutedEventArgs e)
{
foreach(var traceNode in _tracertWrapper.Results)
{
((App)Application.Current).TracertResultNodes.Add(traceNode);
}
_tracertWrapper.DoTrace("8.8.8.8", 30, 50);
}
If you do assign a new one, then you'd need to implement INotifyPropertyChanged on your App class, am not sure how (or whether) that would work though (I have not tried this before).
In a similar question:
What is this pattern called? Soft Lock?
I was asking about the name of the pattern for the code listing below.
public class MyClass
{
public event EventHandler MyEvent;
private bool IsHandlingEvent = false;
public MyClass()
{
MyEvent += new EventHandler(MyClass_MyEvent);
}
void MyClass_MyEvent(object sender, EventArgs e)
{
if (IsHandlingEvent) { return; }
IsHandlingEvent = true;
{
// Code goes here that handles the event, possibly invoking 'MyEvent' again.
// IsHandlingEvent flag is used to avoid redundant processing. What is this
// technique, or pattern called.
// ...
}
IsHandlingEvent = false;
}
}
It seems that most of the conversation was centered around why we should an should not do this, so I think that this question provides a better forum to tackle the problem and address all of the issues. What is the better / proper way to handle this?
There are series of problems with that pattern. If you want to invoke the handler only once, you would do something like this:
protected static object _lockObj = new object();
protected static bool _isHandled = false;
void MyClass_MyEvent(object sender, EventArgs e)
{
if(_isHandled)
return;
lock(_lockObj)
{
if(_isHandled)
return;
_isHandled = true;
MyOtherPossiblyRecursiveMethod(); // Actually does all your work
_isHandled = false;
}
}
void MyOtherPossiblyRecursiveMethod()
{
}
This way, only one thread should be able to access the actual work method.
I will use something like:
using( var sl = new SoftLock() )
{
sl.Execute(()=>{....});
}
the execute will raise the internal boolean to prevent re-entering. In the dispose that flag would be resetted. Execute will call the lambda just if the flag is false. This is to ensure flag go to false even if exception happens ( causing handler never executed ) and maybe is a little better to see. Of course this is not thread safe, as the original code, but this because we are talking about preventing double execution from the same thread.
The original code is a sufficient (and very lightweight) way to prevent recursion in a single-threaded app. So if during your event handling function you could get into code that might be firing the event again you will not enter infinite recursion.
But the code is not sufficient to prevent access from multiple threads, due to the potential for race conditions. If you need to ensure only one thread gets to run this event, then you should use a stronger locking mechanism, like a Mutex or Semaphore.
The following works in single- and multi-threaded scenarios and is exception-safe... also if need be it can be modified to allow for a certain level of reentrancy (for example 3 levels)...
public class MyClass
{
public event EventHandler MyEvent;
private int IsHandlingEvent = 0;
public MyClass()
{
MyEvent += new EventHandler(MyClass_MyEvent);
}
void MyClass_MyEvent(object sender, EventArgs e)
{
// this allows for nesting if needed by comparing for example < 3 or similar
if (Interlocked.Increment (ref IsHandlingEvent) == 1 )
{
try {
}
finally {};
}
Interlocked.Decrement (ref IsHandlingEvent);
}
}
Well, the problem is that I've got a lot of code like this for each event passed to the GUI, how can I shortify this? Macros wont do the work I guess. Is there a more generic way to do something like a 'template' ?
private delegate void DownloadProgressDelegate(object sender, DownloaderProgressArgs e);
void DownloadProgress(object sender, DownloaderProgressArgs e) {
if (this.InvokeRequired) {
this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });
return;
}
label2.Text = d.speedOutput.ToString();
}
EDIT:
OK, how can I make this using anonymous delegate in BeginInvoke:
this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });
I personally prefer to put the actual action in an Action, then check if this requires an invoke -- this also has the benefit of not needing to declare so many delegates for use with your BeginInvokes. In other words,
void DownloadProgress(object sender, DownloaderProgressArgs e)
{
Action updateLabel = () => label2.Text = d.speedOutput.ToString();
if (this.InvokeRequired)
{
this.BeginInvoke(updateLabel);
}
else
{
updateLabel();
}
}
void DownloadSpeed(object sender, DownloaderProgressArgs e) {
Action updateSpeed = () =>
{
string speed = "";
speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
label3.Text = speed;
};
if (this.InvokeRequired)
{
this.BeginInvoke(updateSpeed);
}
else
{
updateSpeed();
}
}
This approach lends itself well to use an extension method on Controls that takes an Action and runs it through the check for InvokeRequired.
At a minimum, the extension method should look something like:
public static void MaybeInvoke(this Control c, Action action)
{
if (c.InvokeRequired)
{
this.BeginInvoke(action);
}
else
{
action();
}
}
Annoyingly, the non-generic Action wasn't introduced until .NET 3.5, so you would need to modify things a bit in the examples I gave -- probably using MethodInvoker -- if you're using an earlier version.
You could try T4... but I don't know if it will fit your case well.
One humble thought is to create one routine that handles all your BeginInvoke cases based on a request-type enumerate and using a switch statement. Then at least you only have to check InvokeRequired once. Note that you should probably use if/else rather than return to control the flow.
Well, one way could be to put the generic codes in static class, and access it like, for instance:
Utility.DownloadSpeedUpdate(frm, sender, e);
.net has (in one sense) a rather poor UI framework that actively encourages the problem you have, and the mixing of business logic and UI.
You may be able to make some headway with generics, delegates, and base class/static helper methods.
However, ideally, you need to layer a UI manager on top of .net that helps you out with this properly. This would allow you to separate the UI from the commands that it executes (i.e. you would dynamically bind UI events like keypresses, menu choices, button clicks etc to underlying command objects rather than handling the UI events directly). The UI event handlers would simply look up the command that is bound to the event, then call a centralised "execute Command X" method, and this would handle all the marshalling to the UI thread, etc.
As well as cleaning up this whole mess, this allows you to easily add things like key bindings and scripting/automation to your app, and makes the UI infinitely more scalable and maintainable.
It's no coincidence that this is the core command dispatch approach used in WCF - If you're not using WCF then it's unfortunately up to you to implement an equivalent. It takes a little bit of work to implement a basic command dispatch system, but unless your application is trivial, you'll be glad you did it.
Here's a sample that will save you a bunch of coding if you have many functions requiring InvokeRequired checks. You should notice a few important things:
I use EventHandler<DownloaderProgressArgs> instead of creating new delegates for each function.
The GetInvokeRequiredDelegate function wraps the code that is the same for all of these functions.
This code could be moved into a static class to let you share this among several forms, but that would require more work and a different structure. As it is here, the function just knows which form you're dealing with because the function exists inside that form.
This is all the code that I set up to test GetInvokeRequiredDelegate<T>():
public partial class Form1 : Form
{
public event EventHandler<DownloaderProgressArgs> OnDownloadProgress;
public event EventHandler<DownloaderProgressArgs> OnDownloadSpeed;
public Form1()
{
InitializeComponent();
OnDownloadProgress += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadProgress);
OnDownloadSpeed += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadSpeed);
new System.Threading.Thread(Test).Start();
}
public void Test()
{
OnDownloadProgress(this, new DownloaderProgressArgs() { DownloadSpeed = 1000, speedOutput = 5 });
OnDownloadSpeed(this, new DownloaderProgressArgs() { DownloadSpeed = 2000, speedOutput = 10 });
}
EventHandler<T> GetInvokeRequiredDelegate<T>(Action<object, T> action) where T : EventArgs
{
return ((o, e) =>
{
if (this.InvokeRequired)
{
this.BeginInvoke(action, new object[] { o, e});
} else
{
action(o, e);
}
});
}
void DownloadProgress(object sender, DownloaderProgressArgs d)
{
label2.Text = d.speedOutput.ToString();
}
void DownloadSpeed(object sender, DownloaderProgressArgs e)
{
string speed = "";
speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
label3.Text = speed;
}
}
public class DownloaderProgressArgs : EventArgs {
public int DownloadSpeed;
public int speedOutput;
}