How to correctly reset a custom event in C#? - 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;

Related

How to remove ViewTreeObserver in Xamarin?

Let's just say I need to get and set a View's height. In Android, it's known you can get a view height only after it's drawn. If you're using Java, many answers, one of the most well-known way is like this one below, taken from this answer:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Thus I search C#/Xamarin version, and found this works:
int viewHeight = 0;
ViewTreeObserver vto = view.ViewTreeObserver;
vto.GlobalLayout += (sender, args) =>
{
viewHeight = view.Height;
};
Thing is, it fired again and again. In Java version, it can be removed with
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
How to do it in C#\Xamarin? Should I resort to using boolean properties to know whether it's executed or not? Is there not way to do it like the android one?
If you are using C# Events, avoid using anonymous events if you need to unsubscribe, or you can implement the IOnGlobalLayoutListener and add/remove the listener:
C# EventHandler Style:
Create an EventHandler method for the event to invoke:
void Globallayout_handler(object sender, EventArgs e)
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
Subscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout += Globallayout_handler;
Unsubscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout -= Globallayout_handler;
Java Listener Style in C#:
Add and implement ViewTreeObserver.IOnGlobalLayoutListener:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer,
ViewTreeObserver.IOnGlobalLayoutListener
{
~~~~
public void OnGlobalLayout()
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
}
Now you can use the Java way to add and remove this listener:
aView.ViewTreeObserver.RemoveOnGlobalLayoutListener(this);
aView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
Even though the answer given by ShshiHangover is correct in principle, the unsubscribing didn't work for me as expected (using the regular method #1).
The reason is probably that the ViewTreeObserver in the called method can be different from the one the event handler subscribed to, so removing it may not work (i.e., the handler method is called continuously).
The correct way of doing this is to unsubscribe from the event sender object while ensuring that IsAlive yields true:
void ViewTreeObserver_GlobalLayout(object sender, EventArgs e)
{
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive) {
vto.GlobalLayout -= ViewTreeObserver_GlobalLayout;
}
}
Neither #Daniel or #SushiHangover methods would actually unsubscribe for me (maybe an sdk bug?). My only solution was to set a bool flag on first run. It would be nice to know how to actually unsubscribe however...
Getting the ViewTreeObserver via sender never seems to be IsAlive whereas getting the tree from the View does. However either way the event doesn't get properly removed.
private void Setup()
{
cameraView = FindViewById<SurfaceView>(Resource.Id.camera_view);
//need to wait for view to inflate to get size
isSetup = false;
ViewTreeObserver vto = cameraView.ViewTreeObserver;
vto.GlobalLayout += Vto_GlobalLayout;
}
void Vto_GlobalLayout(object sender, System.EventArgs e)
{
//this didn't work either
//ViewTreeObserver vto = cameraView.ViewTreeObserver;
//vto.GlobalLayout -= Vto_GlobalLayout;
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive)
vto.GlobalLayout -= Vto_GlobalLayout; //even after removing it seems to continue to fire...
if (!isSetup)
{
isSetup = true;
DoYourCodeNow();
}
}

Temporarily stop form events from either being raised or being handled?

I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.

Multiple Invokes off of one Delegate

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.

FileSystemWatcher Events Untouchable by System.Delegate?

I am writing integration tests that involve FileSystemWatcher objects. To make things easier, I want to unsubscribe everything from an event delegate without having to hunt down every subscription. I already saw related post, Is it necessary to unsubscribe from events?. This is somewhat a duplicate, but I am specifically asking why this doesn't work with a FileSystemWatcher object.
It would be nice to do something like the following:
private void MethodName()
{
var watcher = new FileSystemWatcher(#"C:\Temp");
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Changed = null; // A simple solution that smells of C++.
// A very C#-ish solution:
foreach (FileSystemEventHandler eventDelegate in
watcher.Changed.GetInvocationList())
watcher.Changed -= eventDelegate;
}
No matter how the Changed event is referenced, the compiler reports:
The event 'System.IO.FileSystemWatcher.Changed' can only appear on the left hand side of += or -=
The above code works just fine, when working with an event in the same class:
public event FileSystemEventHandler MyFileSystemEvent;
private void MethodName()
{
MyFileSystemEvent += new FileSystemEventHandler(watcher_Changed);
MyFileSystemEvent = null; // This works.
// This works, too.
foreach (FileSystemEventHandler eventDelegate in
MyFileSystemEvent.GetInvocationList())
watcher.Changed -= eventDelegate;
}
So, what am I missing? It seems that I should be able to do the same with the FileSystemWatcher events.
When you declare event in your class, it is an equivalent (almost) of the following code:
private FileSystemEventHandler _eventBackingField;
public event FileSystemEventHandler MyFileSystemEvent
{
add
{
_eventBackingField =
(FileSystemEventHandler)Delegate.Combine(_eventBackingField, value);
}
remove
{
_eventBackingField =
(FileSystemEventHandler)Delegate.Remove(_eventBackingField, value);
}
}
Notice that there is no set or get accessor for event (like for properties) and you can't explicitly write them.
When you write MyFileSystemEvent = null in your class, it is actually doing _eventBackingField = null, but outside your class there is no way to directly set this variable, you have only event add & remove accessors.
This might be a confusing behavior, because inside your class you can reference an event handler delegate by event name, and can't do that outside the class.
Short answer is += and -= are public operators while = is a private operator to the class that's declaring the event.

Is there a way to put an assertion on an event?

Can I test the assertion that an event was fired? Something like this:
[TestMethod]
public void EventFiresWhenChangingProperty()
{
var sut = new SystemUnderTest();
var eventRegister = new EventRegister(sut.PropertyChanged);
sut.AnyProperty = "new value";
Assert.EventWasFired(eventRegister);
}
Of course I could create an event handler, that puts some record into the test context or in an instance variable that is only read by this specific test but this seems a little bit too much plumbing. I am looking for something like the code above.
I usually hook up an anonymous method as an event listener, and set a testable value in it, like so:
var sut = new SystemUnderTest();
bool eventWasRaised = false;
sut.PropertyChanged += (s, e) => eventWasRaised = true;
sut.AnyProperty = "new value";
Assert.IsTrue(eventWasRaised);
That gives a minimal amount of plumbing. If you want to add some cleaning up, you can create a variable holding the event handler:
var sut = new SystemUnderTest();
bool eventWasRaised = false;
EventHandler eh = (s, e) => eventWasRaised = true;
sut.PropertyChanged += eh; // attach event handler
sut.AnyProperty = "new value";
Assert.IsTrue(eventWasRaised);
sut.PropertyChanged -= eh; // detach event handler
Although at first it seems like "unneeded plumbing" code adding a listener to the event is actually a good idea and I think you should do that to test the event.
There are some mocking frameworks that enable you to do just that you want but it adds additional dependencies to your code.
Keep it simple - subscribe to the event.
I agree with Fredrik Mörk's solution and use it fairly often. One note: it's best to insert a Sleep of at least 20-30 milliseconds after the triggering action to ensure that enough time is given for the event handler to kick in - sometimes I've had race conditions develop.

Categories