Method that takes a delegate as a parameter? - c#

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();
}

Related

C# add extra parameter in the handler of an event

In C#/Unity, is there a way to pass more arguments to an event handler that were not specified in the delegate?
Assume that I cannot modify the delegate. (It comes from an external class.)
I need to be able to do this so I can unsubscribe from my handler later (onClick -= HandleClick somewhere else in the code).
Example:
delegate void A(str s);
event A onClick;
Awake() {
onClick += HandleClick;
}
void HandleOnClick(str s, int a) {
// How to get access to this second argument a here?
}
I know I can do the following:
int a = 10;
onClick += (s) => HandleOnClick(s, 10);
But this won't let me unregister the lambda (since it's an anonymous delegate) if I wire it up this way.
One way you can try to use a variable to hold the delegate reference which is able to subscribe or unsubscribe from the event.
Awake() {
int a = 10;
var e1 = (s) => HandleOnClick(s, a);
onClick += e1;
onClick -= e1;
}

Error using TaskCompletionSource.TrySetResult()

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);

No overload for method 'OnTimerEvent' takes '1' arguments

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.

How to accept a function reference as an argument?

I'm currently passing a EventHandler object to a util function like this:
Timer newTimer(int interval, System.Timers.ElapsedEventHandler handler) {
....
timer.Elapsed += handler;
....
}
newTimer(1000, new System.Timers.ElapsedEventHandler(myTimer_Tick));
But this is ugly and forces every caller to create an EventHandler object. How do I change that to something like this?
Timer newTimer(int interval, ref Function handler) {
....
timer.Elapsed += new System.Timers.ElapsedEventHandler(handler);
....
}
newTimer(1000, ref myTimer_Tick);
It's not clear why you're using ref here, but if myTimer_Tick has the right signature, you don't need to change your method at all - you can just use:
newTimer(1000, myTimer_Tick);
This uses a method group conversion instead an explicit delegate creation statement. It does the same thing though.
If you want to be able to use parameterless void methods, you could write a helper method to take an Action and wrap that in an ElapsedEventHandler via a lambda expression:
Timer StartTimer(int interval, Action action)
{
...
timer.Elapsed = (sender, args) => action();
...
}

Removing anonymous event handler

I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?
You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.
The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;
No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)

Categories