I want to show a text in a label for a particular time in my form and this is the code i tried so far :
private void ShowTextForParticularTime(String caption)
{
Timer t = new Timer { Interval = 2000, Enabled = true };
t.Tick += new EventHandler(OnTimerEvent(caption));
}
private void OnTimerEvent(object sender, EventArgs e,String caption)
{
barStaticItem3.Caption = caption;
}
My question is how can I set the "caption" parameter into OnTimerEvent method because that code I wrote doesn't work it gives me this error :
No overload for method 'OnTimerEvent' takes '1' arguments
Use this instead:
t.Tick += (sender, args) => OnTimerEvent(sender, args, caption);
Reason is you need to assign some event handler to the event. But when you state new EventHandler(OnTimerEvent(caption)); you are actually trying to invoke it. The invocation call at compile time of course fails because the method requires 3 parameters (sender, e, caption).
If instead you create an anonymous delegate via lamdas, you can take advantage of their syntax and closure to wire the event while passing in your third caption parameter.
Related
This is a follow-up question to another SO question regarding the use of an async wrapper over an async callback function.
Here is the code as it stands (an excellent solution provided by #Servy):
static Task<ObservableCollection<MyResult>> GetMyDataAsync(Params p)
{
var tcs = new TaskCompletionSource<ObservableCollection<MyResult>>();
DoStuffClass stuff = new DoStuffClass();
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.Result);
stuff.LongDrawOutProcessAsync(p);
return tcs.Task;
}
So, my problem is with the LoadCompleted event; here is the signature:
public event EventHandler<MyArgs> LoadCompleted;
MyArgs contains a property called ResultCollection; however, changing the code like this does not work:
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.ResultCollection);
In fact, I get the error:
'System.EventHandler<MyArgs>' does not take 1 arguments
Which I can see if correct from the signature; so how can I set the LoadCompleted result to the TaskCompletionSource?
EventHandler needs 2 arguments, the first is the instance that raised the event and the second is the event arguments. You need to specify both of them even if you only use one (args).
This should work:
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
This should fix your problem
If you look at EventHandler<T> definition you will see it takes two arguments
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
So you need to pass two arguments in your assignment
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
I'm trying to create a method that takes a delegate as one of its parameters but its not working. What am I doing wrong? I'm getting the error "callback is a variable but is used like a method" on the line that has windowAnimation.Completed += new EventHandler(callback).
private void animateWindowWidth(Window window, double width, double duration, Delegate callback)
{
window.BeginInit();
window.Dispatcher.BeginInvoke(new Action(() =>
{
DoubleAnimation windowAnimation = new DoubleAnimation();
windowAnimation.Duration = new Duration(TimeSpan.FromSeconds(duration));
windowAnimation.From = window.Width;
windowAnimation.To = width;
windowAnimation.FillBehavior = FillBehavior.HoldEnd;
windowAnimation.Completed += new EventHandler(callback);
window.BeginAnimation(Window.WidthProperty, windowAnimation);
}), null);
window.EndInit();
}
I just thought I'd explain a bit of why Delegate by itself does not work.
Delegate is not a true delegate, but a representation of one. It's basically a variable that holds a delegate. This is why an error is given for treating a variable like a method.
A simple example (granted you'd probably never do this) is if you have two delegates to do addition. One with ints and the other with floats. You can store the delegates in a Delegate object and pass that to another function that calls DynamicInvoke() on one variable:
void MyMethod(Delegate d)
{
d.DynamicInvoke(leftHandSide, rightHandSide);
}
No matter which of the two delegates are stored in the Delegate object, you get the appropriate functionality.
In your case, windowAnimation.Completed is expecting an actual delegate method, such as EventHandler. In addition, the constructor of EventHandler expects a delegate method. So using a Delegate object in either situation will not work.
You'll have to wrap it in a delegate or use a lambda function to place the true method call
windowAnimation.Completed += (s,e) => callback.DynamicInvoke();
or changed callback to an EventHandler and create a new one when you when you want to call this method.
You could change your method signature to:
private void animateWindowWidth(
Window window,
double width,
double duration,
EventHandler callback)
and that line that causes the error to
windowAnimation.Completed += callback;
Then create a new EvenHandler when you call the method.
Try to use Action instead of Delegate and replace this string:
windowAnimation.Completed += new EventHandler(callback);
with this:
windowAnimation.Completed += (s, e) => callback();
So, in the end it should look like this:
private void animateWindowWidth(Window window, double width, double duration, Action callback)
{
window.BeginInit();
window.Dispatcher.BeginInvoke(new Action(() =>
{
DoubleAnimation windowAnimation = new DoubleAnimation();
windowAnimation.Duration = new Duration(TimeSpan.FromSeconds(duration));
windowAnimation.From = window.Width;
windowAnimation.To = width;
windowAnimation.FillBehavior = FillBehavior.HoldEnd;
windowAnimation.Completed += (s, e) => callback());
window.BeginAnimation(Window.WidthProperty, windowAnimation);
}), null);
window.EndInit();
}
At the moment I am in the process of building a custom button handler (I needed to integrate the kinect into the button system which also used a mouse) then I got to a horrible thing called Event Handling.. at least an hour yelling at my pc :P. I was wondering, before I go and spend a while changing my system to allow for my new want, which is to have multiple events per handler, I was wondering, is the way I'm going to try work (I would just try, but I'm getting off for the night, so my hope is that I can save some time when I boot the computer up tomorrow and not attempt if my system isn't designed for it)
Also, ive seen a getInvoc list or somthing like that before when I was coding.. Would I add multiple delegates onto it then get that list and itterate over it?
On previous examples I had seen where people used:
public event EventHandler myEventHandler;
I had to use:
private Dictionary<BtnEvent, Delegate> m_events;
and then they did the following to add a handler (their way, not mine):
myObj.myEventHandler += delegate(object sender, EventArgs ea)
{
//do stuff on event
};
first.. If they ran this twice, once with funcA and second with funcb would it run both? or just one?
second, if I applied that logic of += to a Delegate would it work? (I had to use Delegate as I was storing the handlers inside of a dictionary, this allowed for logical access to handlers through use of an enum)
(my code)
private Dictionary<BtnEvent, Delegate> m_events;
//....
m_events = new Dictionary<BtnEvent, Delegate>(6);
m_events.Add(BtnEvent.CLICK_ENTER, null);
m_events.Add(BtnEvent.CLICK_LEAVE, null);
m_events.Add(BtnEvent.CLICK_STAY, null);
m_events.Add(BtnEvent.HOVER_ENTER, null);
m_events.Add(BtnEvent.HOVER_LEAVE, null);
m_events.Add(BtnEvent.HOVER_STAY, null);
//....
public bool addHandle(BtnEvent stateToGet, Delegate function)
{
bool success = false;
if(m_events.ContainsKey(stateToGet))
{
m_events[stateToGet] = function;
}
return(success);
}
// CHANGE ABOVE TO:
public bool addHandle(BtnEvent stateToGet, Delegate function)
{
bool success = false;
if(m_events.ContainsKey(stateToGet))
{
m_events[stateToGet] += function;
}
return(success);
}
Will changing m_events[stateToGet] = function; to m_events[stateToGet] += function; allow me to have multiple event handles (functions I passed to addHandle) be called through the following code?
private void ExecuteEvent(BtnEvent currEvent)
{
if(m_events.ContainsKey(currEvent))
{
if(m_events[currEvent] != null)
{
m_events[currEvent].DynamicInvoke(null);
}
}
}
Please see below code which answers your first question:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
funcA();
funcB();
}
private void funcA()
{
button1.Click += new EventHandler(button1_Click);
}
private void funcB()
{
button1.Click += new EventHandler(button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("I am in event handler");
}
}
On clicking the Button, "I am in event handler" message is shown twice which means += operator works in similar way with delegates as it works with integers or strings. It simply adds the function handler to the queue and upon execution of events, calls all the function pointers in queue.
Regarding your second question, I think you wont achieve the expected behavior by changing = to +=. What I understand from your statement is that, you wish to execute multiple events handlers like CLICK_ENTER, CLICK_LEAVE on calling ExecuteEvent() function. However, since you are storing event handlers and their delegates in a Dictionary, changing = to += will only work in the same way as illustrated in above code.
I have create a backgroundworker in an class it works, but if i call and wait until the end run, call it for the second time it will do the same process twice
i thinks there is somthing wrong with bw.DoWork +=
private void button1_Click(object sender, EventArgs e)
{
nptest.test.start("null", "null");
}
namespace nptest
{
class test
{
public static void start(string str, string strb)
{
if (bw.IsBusy != true)
{
bw.WorkerSupportsCancellation = true;
bw.DoWork += (obj, e) => bw_DoWork(str, strb);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
}
private static BackgroundWorker bw = new BackgroundWorker();
private static void bw_DoWork(string str, string strb)
{
System.Windows.Forms.MessageBox.Show("initializing BackgroundWorker");
}
private static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
Console.WriteLine("Canceled");
}
else if (!(e.Error == null))
{
Console.WriteLine("Error: " + e.Error.Message);
}
bw.Dispose();
}
}
}
problem solved
class test
{
private static List<object> arguments = new List<object>();
// initializing with program startup
public static void bwinitializing()
{
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public static void start(string str, string strb)
{
if (bw.IsBusy != true)
{
arguments.Clear();
arguments.Add(str);
arguments.Add(strb);
bw.RunWorkerAsync(arguments);
}
}
private static BackgroundWorker bw = new BackgroundWorker();
private static void bw_DoWork(object sender, DoWorkEventArgs e)
{
List<object> genericlist = e.Argument as List<object>;
System.Windows.Forms.MessageBox.Show("BackgroundWorker " + genericlist[0]);
}
I would suspect that multiple DoWork events are being inadvertently added.
That is, every time the start method is called it registers a new DoWork event handler. This adds and does not replace the existing handler DoWork handler. So then there will be multiple DoWork handlers called subsequent times .. 1, 2, 3, etc.
// creates a NEW delegate and adds a NEW handler
bw.DoWork += (obj, e) => bw_DoWork(str, strb);
I would recommend not using a closure here, but rather just use a Method Group (with implicit conversion to a delegate) and then pass the data to the RunWorkerAsync call (there is a form that takes an argument for data).
The RunWorkerCompleted += line doesn't have this issue because it is passed a delegate from a Method Group (which is guaranteed to always evaluate to the same delegate object1). Thus the repeated += calls for that line will replace the handler.
Example:
class MyData {
public string StrA { get; set; }
}
// These only need to be setup once (and should be for clarity).
// However it will be "ok" now if they are called multiple times
// as, since the delegates are the same, the += will
// act as a replacement (as it replaces the previous delegate with itself).
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
// Pass data via argument
bw.RunWorkerAsync(new MyData {
StrA = str,
});
void bw_DoWork (object sender, DoWorkEventArgs e) {
var data = (MyData)e.Argument;
var str = data.StrA;
// stuff
}
1 I am not sure if it is guaranteed to be reference-equals equality, but using this approach allows for stable invoking of += and -= from the delegate from the Method Group even if obtained by new DelegateType(MethodGroup).
Wrt. my comment in the main post: if UI elements are accessed from a thread on which they were not created then there will fun "Cross-thread operation exceptions". I believe this usage of a Message Box is "okay" (when not created with an owner from another thread), but the practice of accessing the UI in a BackgroundWorker's DoWork is generally dubious.
Also, do not call bw.Dispose() here; dispose it with the owning container or context. It appears to be nice and benign in this case, but only do it when that BGW instance will never be used again. Calling it from an event handler is also dubious as the BGW is still "active".
I have encounter same problem as above commenter "Power-Mosfet"
and in the end, added a new BackgroundWorker() then assigned to the global bw value will fix my problem.
code is, change from:
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(xxx)
{
// Create a background thread
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
//omited some code
gBgwDownload.RunWorkerAsync(paraObj);
}
to:
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(xxx)
{
// Create a background thread
gBgwDownload = new BackgroundWorker(); /* added this line will fix problem */
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
//omited some code
gBgwDownload.RunWorkerAsync(paraObj);
}
There is also another reason. look for DoWorkEventHandler in its generated code InitializeComponent() If you have generated it through compnent UI properties and also registering it yourself.
Because if you register it again it will not override the previous one but will add another event and will call twice.
In my case, BackgroundWorker was running twice because in the constructor class of my form I declared the DoWork, ProgressChanged and RunWorkerCompleted event handlers, but it was already declared by Visual Studio 2013 in Designer part of this form class.
So, I just deleted my declarations and it worked fine.
thank you....this code is working fine... creating new intance for backroundworker is good idea....
Now we can call this function in for/while loop and can run multiple backgroundworker process.
I coded like this
when button click is done.. without distrubting the main thread flow... multiple process will be running back side....
i just used messagebox to pop up..but we can do timetaking process to run in "bgwDownload_DoWork" function... and multiple process will be created... and her we need not check the BackgroundWorker is busy or not...
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
yourFunction_bw(i);
}
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(int i)
{
// Create a background thread
gBgwDownload = new BackgroundWorker(); // added this line will fix problem
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerAsync(i);
}
private void bgwDownload_DoWork(object sender, DoWorkEventArgs e)
{
int stre = (int)e.Argument;
MessageBox.Show(stre.ToString ()); // time taken process can be added here
}
I ran into this problem today, I put a background worker on a popup form that was doing a long running task when I noticed that every time I showed the form the background worker RunWorkerCompleted event was being called multiple times.
My problem was that I was not disposing of the form after closing it, which meant every time I showed the form it added another handler to the even each time.
Disposing of the form when finished with it solved my problem. Just wanted to mention it here as I came across this page when I went looking for a solution for my situation.
I removed the control from the designer and instantiate a new WorkerProcess in Code:
example:
var bwProcess = new BackgroundWorker();
bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);
bwProcess.RunWorkerCompleted += bwProcess_RunWorkerCompleted;
I'm using a timer to reset a lable I use as a warning box. Basically, if the user does something (more specifically, something goes wrong, ex : He uses a word not recognized by the program), this catches what went wrong early and returns to him what happened so he can change the input.
The reset blanks out the label after 5 seconds to prevent him from seeing something like "please do not use chinese characters" and maybe still thinking an old error is still up. This is what I got reading the invoke (since I hear begininvoke requires an endinvoke, I chose invoke).
private void lblWrn_TextChange(object sender, EventArgs e)
{
Timee = new System.Timers.Timer(5000);
Timee.Elapsed += new ElapsedEventHandler(timerClearWrn);
Timee.Enabled = true;
}
string empty = "";
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(new Action<Label>(lblWrn), new object[] { lblWrn, "" });
}
I am not too sure where I am going wrong with this, and looking up examples, cannot figure out which part to change. Can someone explain to me the error or invoke a bit more?
If it's a Windows Forms application, use System.Windows.Forms.Timer, then you don't need Invoke, as the timer callback is executed on the main thread.
Also, don't create a new timer on every text change.
Actually, Control.BeginInvoke does not need an EndInvoke; it is Delegate.BeginInvoke that does.
First, I would also recommend using a Windows.Forms.Timer, since it looks like you are using winforms - that will automatically fire on the UI thread, making all the problems go away - just run the code you want to run in the handler (don't use Invoke etc)
The problem in your example is that the parameters don't match; an Action<> expects a method name (more accurately: a method group) to be invoked, and the parameters in the array must be suitable. Since you don't show the method you plan to invoke, I can't help there - but lblWarn isn't a method (it is a field).
on this line
lblWrn.Invoke(new Action(lblWrn), new object[] { lblWrn, "" });
shouldn't the bold part be a function and not a object?
You have a few options. Option 1 is a little clunky. Options 2 and 3 are better.
Option 1: Continue with general strategy of using Control.Invoke but use code that calls Invoke correctly, disable auto resetting of the timer, and removes the event handler.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(
(MethodInvoker)(()=>
{
lblWrn.Text = "";
}), null);
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}
Option 2: Use a System.Windows.Forms.Timer instead of System.Timers.Timer.
Option 3: Use the SynchronizingObject property of System.Timers.Timer. This is my preferred option when timers are to be created and used dynamically from a UI thread.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.SynchronizingObject = this; // Tell the Timer to raise the Elapsed event on the UI thread
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Text = "";
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}