Why I can't pass RoutedEventHandler directly in C# - c#

I got a UserControl where I want an event to be public:
public event RoutedEventHandler CloseButtonClicked;
But to dispatch to this event, the following doesn't work:
public MyUserControl()
{
InitializeComponent();
closeButton.Click += CloseButtonClicked;
}
While the following does work:
public MyUserControl()
{
InitializeComponent();
closeButton.Click += (sender, args) => CloseButtonClicked(sender, args);
}
Why is that?

The difference between your two scenarios is when the CloseButtonClicked event is evaluated.
In the first, non-working example, the value of the event field is evaluated at the time that the program statement closeButton.Click += CloseButtonClicked; is executed. Unless you've already set the field to some useful value, then later when the Click event is raised nothing will happen.
In the second, working example, the value of the event field is evaluated at the time that the Click event is raised. That statement declares an anonymous method (via the lambda expression) which on execution invokes the delegate instance stored in the CloseButtonClicked event field. So as long as that field gets set before the event is raised, it doesn't matter that the field wasn't set when you executed the closeButton.Click += (sender, args) => CloseButtonClicked(sender, args); statement.
Note that even the second statement will fail, with a NullReferenceException, if the CloseButtonClicked event field hasn't been initialized when the Click event is raised. When raising events, you should protect against this by checking for null first (and preferably protecting against any thread race conditions by saving the value into a local first). For example:
closeButton.Click += (sender, args)
{
RoutedEventHandler handler = CloseButtonClicked;
if (handler != null)
{
handler(sender, args);
}
};
Naturally, the above boilerplate is often encapsulated into a helper method (extension or otherwise).

This line doesn't tell what to execute when closeButton.Click occurs:
closeButton.Click += CloseButtonClicked;
To get actual method into it. You need to set CloseButtonClicked first:
CloseButtonClicked += MyMethodWhichDoesSomething;
Now create method MyMethodWhichDoesSomething and there raise an event (CloseButtonClicked(sender, e);)
What this line does, it actually creates a method which raises event...
closeButton.Click += (sender, args) => CloseButtonClicked(sender, args);

Related

C# event handlers : when are the subscribers invoked?

After doing some reading here is what I think is happening, and I am just looking for confirmation or correction.
The samples below will be using anonymous lambdas for brevity, obviously doing that you would lose the ability to unsubscribe.
MyEvent += (sender, args) => {Console.WriteLine("One")};
MyEvent += (sender, args) => {Console.WriteLine("Two")};
MyEvent += (sender, args) => {Console.WriteLine("Three")};
With the subscribers in place, we would invoke the Event:
var handler = MyEvent;
if(handler != null){
handler(this, EventArgs.Empty) // <-- Interested in this moment
}
So here is where I will need correcting/clarification/guidance. As I understand it, what invoking handler is effectively doing is (I understand it's not doing EXACTLY this, this is for illustrative purposes only).
foreach(var subscriber in self){
subscriber(sender, args);
}
I'm explicitly not talking about using this with BeginInvoke. So what happens is basically invoking the handler causes the handler to loop through all of it's subscribers in some undefined order and invoke them passing the appropriate args from the invoking thread (in this example, again not talking about BeginInvoke)
Put another way, handler(this, EventArgs.Empty) is basically just doing this immediately, on the invoking thread:
anonymous1(..., ...)
anonymous2(..., ...)
anonymous3(..., ...)
Modifying the above slightly:
var handler = MyEvent;
if(handler != null){
handler(this, EventArgs.Empty) // <-- Interested in this moment
}
Console.WriteLine("Done Invoking Subscribers");
We can always expect the output to be (since none of our subscribers are async and we are not using BeginInvoke):
// some undefined order of the following 3 lines:
One
Two
Three
// Always followed by
Done Invoking Subscribers
Generally speaking, do I have this idea right?
Yes
Sorry to be that direct but the simple answer is yes, that's how it works.

Handling events with C# Extension Methods

I recently learned about using C# extension methods to make calling events easier and I've been using them more and more. I recently hit a strange issue that I don't understand though, and I was wondering if someone could explain it.
The issue occurs when trying to set an eventhandler extension method as an event handler of another event. Here is an example of what I'm doing:
public static class EventHandlerExtensions
{
public static void Raise<TEventArgs>(
this EventHandler<TEventArgs> eventHandler,
object sender, TEventArgs args) where TEventArgs:EventArgs
{
if (eventHandler != null)
{
eventHandler(sender, args);
}
}
}
public class Test
{
private event EventHandler<EventArgs> EventA;
private event EventHandler<EventArgs> EventB;
public Test()
{
Console.WriteLine("::Start");
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
}
}
In this example, EventA should be triggered as a result of EventB being triggered. However, when I run this code, EventB fires, but the extension method on A doesn't find any listeners for it.
If I change the order around, everything works fine:
Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Also, calling EventA.Raise from a lambda works fine:
Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
This is just a simple example, but I'm trying to create a class which can re-dispatch events of event sources added to it in the cleanest way possible. I don't want to create named methods just for redispatching the same events, and I'd rather not store lists of lambda functions that I can unhook from the event handlers later. Mostly, I'm just curious as to why this is happening?
Any ideas?
You capture old value of EventA into the closure by your Raise function. Since later you use += it changes value of EventA, but your closure still have an old value.
You code:
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
Can be expanded into equivalent code which makes it clear why you get old delegate:
var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)
You can add following to before EventB += EventA.Raise to verify that code actually raises old event for A:
EventA += (s, a) => Console.WriteLine("Old Event A raised");
Delegate objects are immutable. Much like strings. So when you assign EventA, you create a new object. EventB is still targeting the old one, the one that didn't have any event handler assigned yet. You have to swap the two statements to fix the problem.

Detaching an handler the first time you call it

I would like to call the event handler just one time, and then detach it. I tried to write:
EventHandler handler = (s, e) =>
{
// Do something
//
// blabla
// Detach the handler
SizeChanged -= handler;
};
SizeChanged += handler;
However on the line SizeChanged -= handler I get this error
Use of unassigned local variable 'handler'
Do you have idead on how I should proceed ? I thought about using a boolean flag, but I will do that only if I can't find a way to detach the handler.
The C# compiler will first create the lambda expression you wrote before assigning the result to the variable. So when the lambda is defined, handler doesn't have a value.
It works though if you assign a value of null to EventHandler before.
Since it's a closure and local variables are captured in the closure, at the time of the call handler will have the correct value and it will work:
EventHandler handler=null;
handler = (s, e) =>
{
// Do something
SizeChanged -= handler;
};
SizeChanged += handler;
To all people downvoting: It won't cause a NullReferenceException. handler is a local variable which is captured in the closure, so the value of handler inside the lambda will change, when it changes in the method that contains the closure. I tested it actually on my PC and it works perfectly.
This is because it really is unassigned yet. Try making a named method out of it, so the symbol is known prehand.
private void OnEvent(object sender, EventArgs e)
{
// Do something
AnEvent -= OnEvent;
}
private void RegisterOnce()
{
AnEvent += OnEvent;
}
I would also recommend to run the DoSmething code only after detatch and implement some locking mechanism, in case you have multithrading, to prevent from multiple threads call the event at the exact same time, not having time to detatch and therefore, all run.

Lambda expression delegates in behavior

I have an attached behavior that used on a listbox, it should automatically select the first element in the list, if the list contains only one element.
The only way that I have found to hook the listbox when the list changes, is to use the listbox' itemcollections CollectionChanged event:
private static void ListenToItemsCollectionChange(ListBox listBox)
{
var collection = (INotifyCollectionChanged)listBox.Items;
collection.CollectionChanged += (sender, args) => SelectAndSetFocusToFirstElement(listBox);
}
The problem now, is that there is no way of unsubscribing from the event, which potentially leads to multiple invokations of SelectAndSetFocusToFirstelement( ).
The normal solution to this, is to not use lambdas. But then I would loose my listbox, which I need for selecting the first element.
Any suggestions on how this can be solved?
Full code
A Lambda is just a shortcut for a delegate, so you can rewrite the lambda as
NotifyCollectionChangedEventArgs collectionChangedDelegate = (sender, arg) =>
{SelectAndSetFocusToFirstElement(listBox)};
then you can add to the collection changed event
collection.CollectionChanged += collectionChangedDelegate
and remove
collection.CollectionChanged -= collectionChangedDelegate
I got a little bit confused what do You meand by "But then I would loose my listbox" ?
Maybe this solution will be sufficient
You could keep the event handler in temporary variable like that
EventHandler handler = (a, b) => { }; // You must use aproperiate delegate
collection.CollectionChanged += handler
and if You want to unsubscribe You could use -= handler

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