C# Event Handlers getting more and more - c#

I'm writing a simple Chess-Game at the moment.
In my game there are "Chessfields" and "Options". A Chessfield is every field on the board, a option is every move-possibility of a Figure on the field.
So when i click a chessfield, for every option-field i bind a new event-handler.
Like so:
private void Chessfield_Click(object sender, MouseButtonEventArgs e)
{
// ... some other stuff
PlaceOptions();
// ... some other stuff
}
Where the function PlaceOptions() does this:
private void PlaceOptions(List<(int, int)> Options, int SourceX, int SourceY)
{
foreach ((int, int) option in Options)
{
// ... some other stuff
chessfield.MouseDown -= Chessfield_Click;
// should remove all existing handlers for that field
foreach (MouseButtonEventHandler optionClickHandler in _recentOptionClickHandlers)
{
chessfield.MouseDown -= optionClickHandler;
}
chessfield.MouseDown += (sender, e) => Option_Click(sender, e, chessfield, SourceX, SourceY);
_recentOptionClickHandlers.Add((sender, e) => Option_Click(sender, e, chessfield, SourceX, SourceY));
// ... some other stuff
}
}
_recentOptionClickHandlers is a global variable that stores every handler i added to any option-field:
private List<MouseButtonEventHandler> _recentOptionClickHandlers = new List<MouseButtonEventHandler>();
Now: every time i click on a chessfield, the Chessfield_Click() handler only gets called once.
But there comes the problem:
When i then click on a option-field (so a possible move of a figure), all recently clicked normal chessfields get moved to that field, because all the previous handlers are still active, but i allready deleted them by calling:
foreach (MouseButtonEventHandler optionClickHandler in _recentOptionClickHandlers)
{
chessfield.MouseDown -= optionClickHandler;
}
And the more i click any fields, the more event handlers are getting called (1st time: 1 handler; 2nd time: 2 handlers; 3rd time: 4 handlers; ...)
This problem really drives me crazy since 2 days now.
Thanks in advance

Can't test it now and can't post a comment either, so I'll reply here.
I think that the handler added to _recentOptionClickHandlers is not the same that you're registering for the MouseDown event, as you're creating a new delegate before adding it to your list.
You should try something like this:
EventHandler evt = (sender, e) => Option_Click(sender, e, chessfield, SourceX, SourceY);
chessfield.MouseDown += evt;
_recentOptionClickHandlers.Add(evt);

Related

Passing value using an event

So, first I generate a List containing custom usercontrols made of a button and progressbar, I generate this using a for loop.
Inside this loop I send each events to the desired methods, now what I need is access to the progress bar inside of the reset method, how do I do that?
ProgressTimerList[i].Button.Reset += Button_Reset;
ProgressTimerList[i].Progressbar //////Need access to this object
And
void Button_Reset(object sender, EventArgs e)
{
//////Inside of here
}
Create a class inherited from EventArgs with a property of type Progressbar and pass it to the handler:
public class MyButtonEventArgs : EventArgs{
public --WhateverProgressbarTypeIs-- Bar {get;set;}
}
ProgressTimerList[i].Button.Reset += (sender, e) => Button_Reset(sender, new MyEventArgs { Bar = ProgressTimerList[i].Progressbar });
void Button_Reset(object sender, MyButtonEventArgs e)
{
var wunderBar = e.Bar;
}
By far the easiest way to handle this is to use anonymous methods.
At the point in your code where you are attaching the handler, try this:
ProgressTimerList[i].Button.Reset += (s, e) =>
{
//////Inside of here
ProgressTimerList[i].Progressbar //////Can access this object
};
No need whatsoever for the Button_Reset method.
The other nice thing is that this encapsulates the event handling within a method so that other code can't directly call Button_Reset. As encapsulation is one of the four pillars of OOP this helps to make your code more robust.
If you need to detach the handler you can do this:
EventHandler button_reset = (s, e) =>
{
//////Inside of here
ProgressTimerList[i].Progressbar; //////Can access this object
///more code
///detach
ProgressTimerList[i].Button.Reset -= button_reset;
};
ProgressTimerList[i].Button.Reset += button_reset;
If you have a clash with the name of e within your MainForm_Load then just call it e2 instead.
One other gotcha you might hit is that you're accessing items in an array within your event handler. You probably need to capture the variable locally before using it in the handler.
Like this:
for (var i = 0; i < ProgressTimerList.Count(); i++)
{
var local_i = i;
EventHandler button_reset = (s, e) =>
{
//////Inside of here
ProgressTimerList[local_i].Progressbar; //////Can access this object
///more code
///detach
ProgressTimerList[local_i].Button.Reset -= button_reset;
};
ProgressTimerList[i].Button.Reset += button_reset;
}
((ProgressTimerListType)((Button)sender).Parent).ProgressBar
Solved it using this thanks to Ron Beyer! Thanks!
If someone is up for more detail can I ask, why do I need to cast the sender before I can use it as a Button, not just use sender.Parent?

Check if event (doubleClick) is running

I am writing a tool which switchs between a lot of states. For some events I need to be sure they wont get executed a second time while the called function (inside the event) is running. This is how I managed it before:
// Global variables //
public bool func1IsRunning = false;
public bool func2IsRunning = false;
...
public void listView_DoubleClick(object sender, EventArgs e)
{
if(!func1IsRunning)
{
func1();
func1IsRunning = false;
}
}
public void func1()
{
func1IsRunning = true;
// some code in here //
}
But with every extension of my tool the list of the global variables grows up. Also the events and functions getting less clear to read.
Isnt there a way like this(?):
public void listView_DoubleClick(object sender, EventArgs e)
{
if(DoubleClick.IsHandled)
{
func1();
}
}
public void func1()
{
// some code in here //
// ................. //
DoubleClick.IsHandled = true; // at the end of the function //
}
So what I am looking for is a way to determine if an event is still running or not. My code is working, im just unhappy with how it looks like.
Any ideas?
UPDATE 1
I decided to use Steve's answer as it solves my problem by the clearest way.
Anyway it is NOT running correctly for now.
Here is how my code looks like:
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= new EventHandler(listView_DoubleClick);
itemEdit();
}
finally
{
listView.DoubleClick += new EventHandler(listView_DoubleClick);
}
}
The code above is NOT disabling the handler.
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
itemEdit();
}
finally
{
listView.DoubleClick += listView_DoubleClick;
}
}
This code is also not disabling the handler.
This is the line where the handler gets enabled (MainForm.Designer.cs):
this.listView.DoubleClick += new System.EventHandler(this.listView_DoubleClick);
There are no errors raised. The event just gets fired again and again. Where is the problem?
UPDATE 2:
As Sinatr asked in the comments below if my function is really waiting or just enabling user input he discovered where the mistake was made.
Steve's answer is correct according to my wrong written question. Thanks a lot to all of you guys.
Just disable the event handler
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
listView.DoubleClick += listView_DoubleClick;
}
}
EDIT
Looking at your comment I suspect that this DoubleClick event is assigned to more than one control. If this is the case, using the global listView global instance of a listview doesn't disable the double click on other controls that are linked to the same code.
If this is the case then you need a more generic approach
public void listView_DoubleClick(object sender, EventArgs e)
{
Control c = sender as Control;
try
{
if(c != null)
{
c.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
if(c != null) c.DoubleClick += listView_DoubleClick;
}
}
Of course, this is just to enable/disable DoubleClicks events, it cannot works if you assign this event handler to other standard events like Click that have the same signature (object sender, EventArgs e)
How about something like the following using locks:
private object globalLock = new object();
private Dictionary<int, object> lockObjects = new Dictionary<int, object>();
public void listView_DoubleClick(object sender, EventArgs e)
{
object lockObject;
lock (globalLock) // to avoid two threads creating the object
{
if (!lockObjects.ContainsKey(1))
lockObjects.Add(1, new object());
lockObject = lockObjects[1];
}
if (Monitor.TryEnter(lockObject) // enter only if no thread has already entered
{
try { func1(); }
finally { Monitor.Exit(lockObject); }
}
}
This is different to Steve's logic in the matter that it is thread-safe.
A simple state-machine should solve your problem without requiring too many variables. Create an Enum named AppState like this:
enum AppState
{
Ready = 1,
InsideListView1Click = 2,
InsideListView1DoubleClick = 3
InsideListView2Click = 4,
InsideListView2DoubleClick = 5
}
This enum could grow as you add new controls and/or event-handlers to your application. Now use a single global variable that keeps track of the application state and modify it inside event-handlers appropriately:
private AppState m_State = AppState.Ready;
And in the event-handlers you would do:
private void ListView1_DoubleClick(object sender, EventArgs e)
{
lock
{
if(m_State != AppState.Ready)
return;
else
m_State = AppState.InsideListView1DoubleClick;
}
//Do your stuff
m_State = AppState.Ready;
}
This way newer calls will be ignored instead of being queued. If you expect to be in multiple states at the same time, you could apply [Flags] attribute on this enum as well. Also note that enums are thread-safe and evaluating them is atomic, so multi-threading shouldn't be a problem either.

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.

c# timer strange behavior

private void aMethod()
{
aTimer = new System.Timers.Timer(3000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimerEvent);
aTimer.Enabled = true;
aTimer.Start();
}
private void button4_Click(object sender, RoutedEventArgs e)
{
fileEntries = Directory.GetFiles(#"C:\Users\John\Documents\Visual Studio 2010\Projects\ComeOn\ComeOn\bin\Debug\come");
aMethod();
index = 0;
}
private void OnTimerEvent(Object sender, ElapsedEventArgs e)
{
Bitmap LogoImg = new Bitmap(fileEntries[index]);
LogoImg.MakeTransparent(LogoImg.GetPixel(1, 1));
this.Dispatcher.Invoke(
new Action(() => image1.Source = GetBitmapSource(LogoImg)));
index++;
}
The length of fileEntries is 3. I created a timer which will start on 3 seconds. First it will execute image1.Source = GetBitmapSource(LogoImg)//for fileEntries[0] for 3 seconds, then for fileEntries[1] for 3 seconds and in the end fileEntries[2] for 3 seconds.
But, my program does this:
Start the timer, run fileEntries[0], fileEntries[1] and fileEntries[2] for 0.05 seconds, then wait 3 seconds, then start again. Why is this?
How often did you click that button?
Every time you press the button, a new event handler will be hooked to the timer. You never unsubscribe the event handler.
You should either prevent the button from being clicked while you are performing the required work, or you should unsubscribe before subscribing again.
As Hans Passant states in his comment, you should probably also look into using a BackgroundWorker.
You shouldn't do
aTimer = new System.Timers.Timer(3000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimerEvent);
aTimer.Enabled = true;
aTimer.Start();
more than once. Do it in Form_Load event, or in constructor. in your OnTimerEvent event, prevent your code from being executed when files aren't initialized, for example
int index = -1;
private void OnTimerEvent(Object sender, ElapsedEventArgs e)
{
if(index != -1)
{
Bitmap LogoImg = new Bitmap(fileEntries[index]);
LogoImg.MakeTransparent(LogoImg.GetPixel(1, 1));
this.Dispatcher.Invoke(
new Action(() => image1.Source = GetBitmapSource(LogoImg)));
index++;
}
if (index == 3) // when all 3 were loaded, reset index. You can also stop the timer if you won't be loading files the second time
{
index=-1;
}
}
Or you should unsuscribe before you add new event handler. But keeping track of how many event handlers are added to an event is tricky (or I should say I havn't found a way to do it yet).
As #Steven Jeuris said, when an event handler is added to an event, it is literaly ADDED, to event handlers LIST. So every time when your timer elapses every event handler on the list is executed, which means if there are 3 event handlers added (as in your case) the event handler method will execute 3 times.

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