Make a c# / wpf event fire only once? - c#

I have a case where I want a given event to execute once, and only once. I'm trying to do it this way, but I'm having prolems (else I wouldn't be asking). Note that the following code is inside the function that decides that the event needs to be fired.
EventHandler h = delegate(Object sender, EventArgs e) {
FiringControl.TheEvent -= h; // <-- Error, use of unassigned local variable "h"
// Do stuff
}
FiringControl.TheEvent += h;
In general this should work because of the way scope is preserved for delegates until after they're done running, but, since h is in the process of being built when I try to use it it is still considered to be "uninitialized", apparently.

You could set h = null first and then set it equal to something else? Like this?
EventHandler h = null;
h = delegate(Object sender, EventArgs e) {
FiringControl.TheEvent -= h; // <-- Error, use of unassigned local variable "h"
// Do stuff
}
FiringControl.TheEvent += h;
But are you sure this is the right approach? What are you trying to do overall?

Related

Storing loop values from click button?

I am currently using a loop to create a new User Control on my windows form. I want it to add a single instance of this User Control in a new position where Y is incremented by 125 each time.
I'm pretty new with C# and Visual Studio, so with the below code, the first instance is being replicated each time I press the 'add' event. I was just wondering if someone can give some assistance on the best way to store the value of 'y' from this first instance to be passed into the loop the second time? Or if there is any better way to do this.
Thanks in advance!
private void btnAddRow_Click(object sender, EventArgs e)
{
int y = 175;
for (int i = 0; i <= 0; i++)
{
NewSkillRow nSkill = new NewSkillRow();
nSkill.Location = new Point(75, y += 125);
Controls.Add(nSkill);
btnAddRow.Click += new EventHandler(btnAddRow_Click);
}
}
Make your y variable local to the class (you can also initialize it with its default):
private int y = 175;
The event handler is called every time you click the button. So remove the initialization of y from there.
private void btnAddRow_Click(object sender, EventArgs e)
{
var nSkill = new NewSkillRow();
nSkill.Location = new Point(75, y += 125);
Controls.Add(nSkill);
}
Note that the event handler attachment was removed. Reattaching an event handler from within the handler would lead to an increasing number of invokations every time the button is clicked.
The loop is fine, but not necessary: For just one iteration, you can as well just omit it.
The use of the y += 125 is also ok, it relies on the specification that the return value of an assignment operator is the value that has been assigned.

C# / WPF create new button array with multiple function

I have wrote a code with this description:
First button calls FuncPopup(); function, after that every popup dialog creates new button. New button Create FuncPopup();. Old buttons should have various behavior.
private void FuncPopup()
{
FuncMenu popup = new FuncMenu();
popup.ShowDialog();
if (popup.DialogResult.HasValue && popup.DialogResult.Value)
{
i++;
newBtn[i] = new Button();
FuncGird.Children.Add(newBtn[i]);
Grid.SetColumn(newBtn[i], i);
Grid.SetRow(newBtn[i], j);
newBtn[i].Click += (sender, e) => clicked(i);
}
}
void clicked(int g) {
if (g >= i)
{
FuncPopup();
}
else (g < i){
OtherFunction();
}
}
i is a global variable. I expect Old buttons run OtherFunction(); but they always run FuncPopup();.
That's because as you said i is global variable, and you attach the following handler:
newBtn[i].Click += (sender, e) => clicked(i);
And you increment i all the time. You might think that value of i is fixed at the moment you attach this handler but it's not so. i in clicked(i) is the same global variable, which increments with every call. So g always equals i in that handler, and so for all buttons FuncPopup is called.
Instead save i to local variable and use that:
int tmp = i;
newBtn[i].Click += (sender, e) => clicked(tmp);

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?

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.

What's the benefits of registering an event handler implicitly

What's the benefits of registering an event as:
void MyMethod()
{
button1.Click += delegate (object sender, EventArgs e)
{
..
}
}
in comparison with:
void MyMethod()
{
button1.Click += new System.EventHandler(this.button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
..
}
UPDATE:
And what about unsubscribing it?
The benefit is that you don't have to come up with a name and a place in your class.
For a light function, tightly coupled to the code that register the event, the short version is more convenient.
Note that you can also exchange delegate for a =>
button1.Click += (object sender, EventArgs e) =>
{
..
}
You can be even more concise:
button1.Click += ( sender, e ) =>
{
};
Syntactically it's cleaner (as long as it doesn't lead to long blocks of code which would be better broken up into named methods).
The inline declaration is a closure, which gives you access to the variables adjacent to the anonymous method.
From: What are 'closures' in .NET?
In essence, a closure is a block of code which can be executed at a
later time, but which maintains the environment in which it was first
created - i.e. it can still use the local variables etc of the method
which created it, even after that method has finished executing.
See also: http://csharpindepth.com/articles/chapter5/closures.aspx
When registering event handler with an anonymous delegate or lambda, you can write shorter code and use closures. But you cannot unsubscribe from the event, and if the event handler code is too long, your code becomes ugly.
It's just about coding style.
Worth mantioning that declaring it like in first case let you to avoid "spaghetti code" and inject into the handler function local variables (if needed) in more natural(human readable way).
To be more clear. By writing like in first case you can:
int x = 0;
System.Windows.Forms.Button button = new System.Windows.Forms.Button();
button.Click += (o,e)=> {
++x;
};
Inside the event handler you can access the local variable declared actually out for the scope of the handler method. For most people it seems "natural", but if you think like a developer it shouldn't be even possible.
Good luck.

Categories