how to clear event handles in c# - 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.

Related

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;

In C#, how can a closure reference itself?

In C#, how can a closure reference itself? The use case is to automatically unsubscribe from an event, after a single occurrence (or on some condition). I know you can create an instance of a delegate, but I'd really like to keep this as a closure, to maintain context. For example:
var someVar = new FooBar();
textBox1.TextChanged += (s, e) => {
doSomething(someVar);
// Unsubscribe from further events
// textBox1.TextChanged -= myself?
// If I don't unsubscribe, I'm needlessly keeping reference
// to someVar, and I don't intend to keep triggering this code
// upon further events.
};
As far as I know it cannot reference itself directly. However, you do something like this instead:
var someVar = new FooBar();
EventHandler closure = null;
closure = (s, e) => {
doSomething(someVar);
// Unsubscribe from further events
// textBox1.TextChanged -= myself?
// If I don't unsubscribe, I'm needlessly keeping reference
// to someVar, and I don't intend to keep triggering this code
// upon further events.
textBox1.TextChanged -= closure;
};
textBox1.TextChanged += closure;
Credit: Can an anonymous method in C# call itself?

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 remove the event?

For Sample ....
SampleClass :
public class SampleClass
{
public delegate void BeforeEditorHandle();
public event BeforeEditorHandle OnBeforeEditor;
}
MainMethod
static void Main(string[] args)
{
SampleClass sc = new SampleClass();
// Add Event
sc.OnBeforeEditor +=new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
// Remove Event
sc.OnBeforeEditor -= new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
}
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
But I think this is very ugly when I have too much sources in the event....
Can anybody tell me the best way to remove the event please ?
You can assign the event handler/lambda to a variable which you can then subscribe and unsubscribe:
var myHandler = () => { };
sc.OnBeforeEditor += myHandler;
sc.OnBeforeEditor -= myHandler;
I'm pretty sure your code here won't work:
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
This is because restating the lambda creates a new different lambda.
You need to store the old reference and use it to unsubscribe:
BeforeEditorHandle myHandler=() => { }
sc.OnBeforeEditor += myHandler;
...
sc.OnBeforeEditor -= myHandler;
For easier unsubscribing you can collect your event handlers in a collection (For example List<BeforeEditorHandle>).
From MSDN:
It is important to notice that you
cannot easily unsubscribe from an
event if you used an anonymous
function to subscribe to it. To
unsubscribe in this scenario, it is
necessary to go back to the code where
you subscribe to the event, store the
anonymous method in a delegate
variable, and then add the delegate to
the event. In general, we recommend
that you do not use anonymous
functions to subscribe to events if
you will have to unsubscribe from the
event at some later point in your
code. For more information about
anonymous functions, see Anonymous
Functions (C# Programming Guide).

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

Categories