Lambda expression delegates in behavior - c#

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

Related

what does this line means? Can somebody help me by explaining consumer.Received += (model, ea)

this is snip of code. I m not getting "consumer.Received += (model ,ea) =>
As of the docs Received is an event within the EventingBasicConsumer-class. This means that you can register for the event by adding an event-handler to it.
consumer.Recieved += (model, args) => { /*
do whatever you want when the event is triggered
*/};
This is some portion of code (more accurate a delegate) that is executed when the event occurs. In your special case you also get some arguments from the event to appropriately react on that event. The first is of type IBasicConsumer and the second of type BasicDeliverEventArgs. Thus the following is identical to the code I´ve written above:
consumer.Recieved += (IBasicConsumer model, BasicDeliverEventArgs args) => { /*
do whatever you want when the event is triggered
*/};
While you often can omit the first one as it´s not needed the second one holds information the caller of the event (the Rabbit-framework in your case) applied to that delegate. So you can use it and display it in a MessageBox for instance.
var body = args.Body;
Alternativly to using an anonymous method (a method without a name as shown above) you can surely use a named one:
consumer.Recieved += MyHandler;
private void MyHandler(IBasicConsumer model, BasicDeliverEventArgs args)
{
// your stuff here
}
Doing so has the advantage that you can easily remove the handler.
consumer.Recieved -= MyHandler;
So your question boils down to what does consumer.Received += (model, ea) => {stuff} do? Its a Lambda function..
well the Received is an event, which clearly has 2 properties, for which the coder has named them model and ea. Which they they use in {stuff}
Previously you could/would have written (and Im making up variable types here but)
consume.Received += mycall;
void mycall(String model, Mything ea)
{
stuff;
}
There are however disadvantages of the lamda, while you can add them on the fly, its not so easy to remove.. where as the older style you can do consume.Received -= mycall;

How to correctly reset a custom event in C#?

I have this code:
private void loadGENIOFileToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlgFile = new OpenFileDialog();
dlgFile.InitialDirectory = Properties.Settings.Default.PreviousPath;
dlgFile.Title = "Select GENIO file";
dlgFile.Filter = "GENIO files (*.txt)|*.txt";
dlgFile.FilterIndex = 0;
dlgFile.Multiselect = false;
if (dlgFile.ShowDialog() == DialogResult.OK)
{
Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(dlgFile.FileName);
DeleteView();
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.SetGenioFilePath(dlgFile.FileName);
m_oThreadServices.start();
}
}
But I am also trying to implement a MRU handler:
private void OnMruFile(int number, String filename)
{
if (File.Exists(filename))
{
Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(filename);
DeleteView();
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.SetFirstFile(number);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.SetGenioFilePath(filename);
m_oThreadServices.start();
}
else
mruMenu.RemoveFile(number);
}
}
My m_oThreadServices.OnLoadingCompleted line of code seems to require that I use += and as a result, if I first load a file, it adds the first event handler. If I then go to use the MRU list to load a different file it ends up running two OnLoadingCompleted handlers.
I tried m_oThreadServices.OnLoadingCompleted = but it will not allow it. So what is the right way for me to intercept the event handler and not end up calling both sets of code? Am I going about it wrong?
Thank you.
You should make sure your event handlers are unsubscribed from the event source once the event is raised.
In order to do that, you have to modify a bit the anonymous handlers. For instance, this snippet:
m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
should be like this:
EventHandler onLoadingCompleted = null;
onLoadingCompleted = (_sender, _e) =>
{
m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
};
m_oThreadServices.OnLoadingCompleted += onLoadingCompleted;
Same for the other.
The line
EventHandler onLoadingCompleted = null;
is needed to avoid using uninitialized variable compiler error here
m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;
You can remove a handler if it's a named function:
private void OnLoadingComplete_AddFile(_sender, _e)
{
mruMenu.AddFile(dlgFile.FileName);
m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
CreateView();
}
...
m_oThreadServices.OnLoadingCompleted += OnLoadingComplete_AddFile;
...
m_oThreadServices.OnLoadingCompleted -= OnLoadingComplete_AddFile;
Removing a handler that hasn't been added (or has already been removed) is a no-op, so you can just remove the "other" handler before you add one: this will ensure there is at most one handler.
So basically += is syntactic sugar for calling Combine on your event. Delegates are stored in an Invocation List, and the default behavior when an event is fired is for each delegate in the invocation list to get called in the order they were added. This is why you cannot simply set OnLoadingCompleted to one delegate with an = sign - an event stores a list of delegates, not one.
You could remove a delegate with -= (syntactic sugar for calling Remove). Perhaps you want to formally declare the previous delegate somewhere rather than passing it as a lambda. This would let you remove it when you are done with it.
There is no straightforward way of removing anonymous or unknown events from a handler. However, you can take a look at this forum posting on MSDN: https://social.msdn.microsoft.com/Forums/vstudio/en-US/45071852-3a61-4181-9a25-068a8698b8b6/how-do-i-determine-if-an-event-has-a-handler-already?forum=netfxbcl
There is some code and discussion about using reflection to remove delegates from your event handler.
It might be better though to understand exactly what you are wanting to accomplish. Perhaps there is a better way to get the end-result that you are looking for rather than rewire events.
It isn't usually good practice to remove established event code to change the behavior of the code you want to implement. It can lead to unintended consequences, and erratic behavior. If event code is defined, it is almost always best to keep it in place and design your application around it.
On the other hand, if this is code that is added by you, or in your code-base, you can remove it, if you have done the proper research to validate its removal and not cause the application to break elsewhere. The best way to do that would be to have the event code in a named function:
public void MyEventCode(object sender, EventArgs args)
{
// Do event stuff..
}
Then you can remove the event by name:
control.DoMyEvent -= MyEventCode;

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.

how to clear event handles in c#

I'm using global variable named "client"
For example
client.getPagesCompleted += (s, ee) =>
{
pages = ee.Result;
BuildPages(tvPages.Items, 0);
wait.Close();
};
client.getPagesAsync(cat.MainCategoryID);
I need to clear handlers for getPagesCompleted and set another handler. How to easy clear handles?
I know client.getPagesCompleted-=new EventHandler(...). But it is very difficult. I need easy way.
I'm using client.getPagesCompleted=null but error shown. "only use += / -+"
The only way to remove an event handler is to use the -= construct with the same handler as you added via +=.
If you need to add and remove the handler then you need to code it in a named method rather using an anonymous method/delegate.
You don't have to put your event handler in a separate method; you can still use your lambda function, but you need to assign it to a delegate variable. Something like:
MyEventHandler handler = (s, ee) =>
{
pages = ee.Result;
BuildPages(tvPages.Items, 0);
wait.Close();
};
client.getPagesCompleted += handler; // Add event handler
// ...
client.getPagesCompleted -= handler; // Remove event handler
Save the event object to a variable, and use -= to unsubscribe.

Categories