Adding and removing events in one method, c# - c#

Sometimes I need to add and remove events from bunch of controls. And I always do that with one method for adding events:
private void AddEvents(){
textBox.TextChanged += TextChanged;
integerUpDown.ValueChanged += ValueChanged;
colorPicker.SelectedColorChanged += ColorChanged;
//... and so on
}
And same one for removing events:
private void RemoveEvents(){
textBox.TextChanged -= TextChanged;
integerUpDown.ValueChanged -= ValueChanged;
colorPicker.SelectedColorChanged -= ColorChanged;
//... and so on
}
I'm using different types of controls and different types of EventArgs. I'd like to compress it to one method, something like:
private void RemoveEvents(bool add){
textBox.TextChanged add ? += : -= TextChanged;
integerUpDown.ValueChanged add ? += : -= ValueChanged;
//or method approach
ManageEvent(colorPicker.SelectedColorChanged, ColorChanged, add);
//... and so on
}
But that's not possible with ? operator. Is there a way to do it?

I think this is neat.
First, define the following interface and class:
public interface IEventHolder
{
void Attach();
void Detach();
}
public class EventHolder<H> : IEventHolder
{
private Action<H> _add;
private Action<H> _remove;
private H _handler;
public EventHolder(Action<H> add, Action<H> remove, H handler)
{
_add = add;
_remove = remove;
_handler = handler;
}
public void Attach() { _add(_handler); }
public void Detach() { _remove(_handler); }
}
Now you can define this private field:
private List<IEventHolder> _eventHolders = new List<IEventHolder>();
In the Form_Load event I've written this code:
private void Form1_Load(object sender, EventArgs e)
{
_eventHolders.Add(new EventHolder<EventHandler>(h => textBox1.TextChanged += h, h => textBox1.TextChanged -= h, textBox1_TextChanged));
_eventHolders.Add(new EventHolder<EventHandler>(h => numericUpDown1.ValueChanged += h, h => numericUpDown1.ValueChanged -= h, numericUpDown1_ValueChanged));
_eventHolders.Add(new EventHolder<MouseEventHandler>(h => textBox2.MouseMove += h, h => textBox2.MouseMove -= h, textBox2_MouseMove));
_eventHolders.ForEach(eh => eh.Attach());
}
Notice that the line _eventHolders.ForEach(eh => eh.Attach()); attaches all events.
My handlers look like this:
private void textBox1_TextChanged(object sender, EventArgs e)
{ }
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
this.textBox1.Text = numericUpDown1.Value.ToString();
}
private void textBox2_MouseMove(object sender, MouseEventArgs e)
{
_eventHolders.ForEach(eh => eh.Detach());
}
The textBox2_MouseMove handler detaches all of the events in one go.
I've tested this and it works like a treat.

As you want to avoid having to specify both adding and removing a handler, there is only reflection left.
private static void ManageEvent(object target, string evnt, EventHandler handler, bool add)
{
var ei = target.GetType().GetEvent(evnt);
if(add)
{
ei.AddEventHandler(target, handler);
}else{
ei.RemoveEventHandler(target, handler);
}
}
This method finds the target event info on a given instance, and based on a condition invokes either the add or remove method. You can also make this generic, but then you would have to specify the type of the delegate. This assumes EventHandler, which is quite common.
ManageEvent(textBox, "TextChanged", TextChanged, add);
In C# 6, you can also use nameof(textBox.TextChanged) instead of "TextChanged" which makes your code easier to refactor.
Please note that reflection performs mostly slow, so consider using the approach shown in other answers if you have to call this method a lot.
This all is necessary because the events are located in another class, and you cannot thus access directly their backing field. However, if the event is located in your class, you can pass it as a reference to a similar helper method:
private static void ManageEvent<TDel>(ref TDel source, TDel value, bool add) where TDel : class
{
if(add)
{
source = (TDel)(object)Delegate.Combine((Delegate)(object)source, (Delegate)(object)value);
}else{
source = (TDel)(object)Delegate.Remove((Delegate)(object)source, (Delegate)(object)value);
}
}
This does exactly what normal event methods do, either combine or remove events.
ManageEvent(ref MyEvent, MyEventHandler, add);

The conditional operation returns a value based on the statement's result. Returning "+=" is definitely not valid, since you are basically subscribing to the event. https://msdn.microsoft.com/en-us/library/ty67wk28.aspx
However, why don't you take the simpler approach?
private void AddOrRemoveEvents(bool add)
{
if (add)
{
AddEvents();
}
else
{
RemoveEvents();
}
}

One line if statement to the rescue! There will be a little bit of repetition but none the less it is a one liner
if (add) textbox.TextChanged += TextChanged; else textbox.TextChanged -= TextChanged;

Related

Event pass parameter

I have problem with event. For example let i have event
public event EventHandler<AxisChangedEventArgs> AxisChanged
which fires when Axis pan or zoom or something else. When it's firing i am making Console.WriteLine("Working");. How can i pass CFDBOX parameter into SomeWork anonymous method does not help because it will be imposible to unsubscribe from it. And i cannot override AxisChanged event.
public void AddEvents(CFDBOX CFDBOX) {
CFDBOX.PlotModel.Axes[0].AxisChanged += SomeWork;
}
public void RemoveEvents(CFDBOX CFDBOX) {
CFDBOX.PlotModel.Axes[0].AxisChanged -= SomeWork;
}
public EventHandler<AxisChangedEventArgs> SomeWork =
delegate(object o, AxisChangedEventArgs args) {
Console.WriteLine("Working");
}
;
Take advantage of closure lambda expressions:
private EventHandler<AxisChangedEventArgs> axisChangedEventHandler;
public void AddEvent(CFDBOX CFDBOX) {
// keep a reference of the event handler to remove it later
axisChangedEventHandler = (o, args) => {
// parameter CFDBOX bound to the event handler
Console.WriteLine("Working " + CFDBOX);
};
// register event handler
CFDBOX.PlotModel.Axes[0].AxisChanged += axisChangedEventHandler;
}
public void RemoveEvent() {
// unregister event handler
CFDBOX.PlotModel.Axes[0].AxisChanged -= axisChangedEventHandler;
}
Any parameter which must be passed with an event should be a member of your EventArgs implementation. In your scenario: AxisChangedEventArgs. Hope i get your question.
The sender of the event (in your case o) should always be the instance, which calls the event. So if your event get's fired from different classes (not instances!), you will have to check for the type of o.

Defining event with field working like Handled

I my class I have an event with arguments containing boolean field Prevent and a default function doing some stuff if Prevent == false. I want user of my class to be able to prevent default function from doing stuff by setting Prevent = true in function listening to event. But it turned out that default function is invoked first, with Prevent = true, and then the user-defined function changes the value of the field. Is there any way of changing order of function invokation? Or any other approach which can give me the same results? I've done it by defining Func field used to check if default function should do its things and user can modify the field providing own function. It works, but I don't like the fact that 2 separate functions are needed, one to do stuff when event fires and one to prevent default behaviour.
Some of the code:
public Func<int, int, bool> PreventFunc = new Func<int,int,bool>( (x,y) => {return false;});
public struct MyEventArgs
{
int x;
int y;
bool Prevent;
public MyEventArgs (int i, int j, bool prev) { /*assignments*/ }
}
public delegate void MyEventDelegate(object sender, MyEventArgs e);
public event MyEventDelegate MyEvent = DefaultBehaviourFunction;
void DefaultBehaviourFunction (object sender, MyEventArgs e)
{
if (e.Prevent == true) return;
//do stuff
}
And when invoking an event
MyEvent.Invoke(sender, new MyEventArgs(i, j, PreventFunc(i,j)));
I want it to work without the PreventFunc, like that:
MyEvent += Func;
void Func (object sender, MyEventArgs e)
{
if (e.x == 1) e.Prevent = true;
//do stuff
}
You could accomplish what you are asking for literally, by getting the invocation list from the event delegate field, reversing the order, and invoking each target individually. But that would be an abuse of the event pattern, as well as simply being unwieldy.
Instead, you should implement this the way that other classes do: have the method that raises the event be the one that checks the Prevent value, not some other handler.
For example:
private void OnMyEvent(int i, int j, bool prevent)
{
MyEventDelegate handler = MyEvent;
MyEventArgs e = new MyEventArgs(i, j, prevent);
if (handler != null)
{
handler(this, e);
}
if (e.Prevent)
{
return;
}
// Member of same class, and we already know Prevent is false,
// so just need to pass i and j.
DefaultBehaviourFunction(i, j);
}
then instead of invoking the event handlers directly with MyEvent.Invoke() you'd call the OnMyEvent() method instead.

Prevent next event handler being called

I have two event handlers wired up to a button click in a Windows form like so:
this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);
both are being called correctly.
However is it possible within FirstHandler() to prevent BtnCreate_Click() being executed? Something like:
void FirstHandler(object sender, EventArgs e)
{
if (ConditionSatisfied)
//Prevent next handler in sequence being executed
}
I know I could just unsubscribe the event, but can this be done programmatically (from within the method)?
As far as I know there is no solution for this. That's because there is no guarantee for the order in which the event handlers are called when the event happens.
Because of that you are not supposed to rely on their order in any way.
Why don't you just replace them with one eventhandler? Something like this:
var rdlc = new RdlcCreator();
this.BtnCreate.Click += (sender, e) => {
rdlc.FirstHandler(sender, e);
if (!rdlc.HasHandledStuff) { // <-- You would need some kind of flag
this.BtnCreate_Click(sender, e);
}
};
That way you can also guarantee the order of the handlers. Alternatively, use the above implementation, but change the signature of FirstHandler to return a bool indicating the condition (as in this case it doesn't really need to have the event's signature anymore):
if (!rdlc.FirstHandler(sender, e)) {
this.BtnCreate_Click(sender, e);
}
EDIT: OR, you just pass the second handler to FirstHandler.
Change the signature of FirstHandler to this:
void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
if (ConditionSatisfied) {
// do stuff
}
else if (nextHandler != null) {
nextHandler(sender, e);
}
}
and then:
this.BtnCreate.Click +=
(s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);
System.ComponentModel namespace contains a CancelEventHandler delegate which is used for this purpose. One of the arguments it provides is a CancelEventArgs instance which contains a boolean Cancel property which can be set be any of the handlers to signal that execution of the invocation list should be stopped.
However, to attach it to a plain EventHandler delegate, you will need to create your own wrapper, something like:
public static class CancellableEventChain
{
public static EventHandler CreateFrom(params CancelEventHandler[] chain)
{
return (sender, dummy) =>
{
var args = new CancelEventArgs(false);
foreach (var handler in chain)
{
handler(sender, args);
if (args.Cancel)
break;
}
};
}
}
For your example, you would use it like this:
this.BtnCreate.Click += CancellableEventChain.CreateFrom(
new RdlcCreator().FirstHandler,
this.BtnCreate_Click
/* ... */
);
Of course, you would need to capture the created chain handler in a field if you need to unsubscribe (detach) it later.
Add the following condition in this.BtnCreate_Click which is the the second event
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
I suggest you to create a some kind of class wrapper. So, you could store there some kind of event flag group (16bit integer, for example) and a few methods to set or unset individual bits (where each means to invoke or not particular EventHandler). You can easily store any count of the Eventhandlers or even Actions, in the class, and invoke in any order you want.
Was finding the solution to the same question, but no luck. So had to resolve myself.
A base class for Cancelable event args
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
Next defines the extension method for the EventHandler, note that Invocation List subscribers invoked in backward order (in my case UI elements subscibe the event as they added to components, so which element is rendered later has most visiblility and more priority)
public static class CommonExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SafeInvokeWithCancel<T>(this EventHandler<T> handler, object sender, T args) where T : CancelableEventArgs
{
if (handler != null)
{
foreach (var d in handler.GetInvocationList().Reverse())
{
d.DynamicInvoke(sender, args);
if (args.Cancelled)
{
break;
}
}
}
}
And here is the usage
public class ChessboardEventArgs : CancelableEventArgs
{
public Vector2 Position { get; set; }
}
So if an UI element has some behaviour on the event, it cancells futher processing
game.OnMouseLeftButtonDown += (sender, a) =>
{
var xy = GetChessboardPositionByScreenPosition(a.XY);
if (IsInside(xy))
{
var args = new ChessboardEventArgs { Position = xy };
OnMouseDown.SafeInvokeWithCancel(this, args);
a.CancelFutherProcessing();
}
};

Button EventHandler in form

The button which we can create on the form is written in terms of event handler in Form1.Designer.cs as
this.button1.Click += new System.EventHandler(this.button1_Click);
Here Click is public event EventHandler 's type and this EventHandler is a delegate as
public delegate void EventHandler(object sender, EventArgs e);
Now,
why can't it be '='(equals)
this.button1.Click = new System.EventHandler(this.button1_Click);
and also when I am passing the argument this.button1_Click, how does it match up to
void EventHandler(object sender, EventArgs e); delegate ? As here I have two arguments.
Please clear me with this.
Thank you
ttSo, let's see what event is.
Code, you are write
public event EventHandler MyEvent;
will compile to
private EventHandler MyEvent = null;
[MethodImp(MethodImplOptions.Synchronized)]
public void add_MyEvent(EventHandler value) {
MyEvent = (EventHandler)Delegate.Combine(MyEvent, value);
}
[MethodImp(MethodImplOptions.Synchronized)]
public void remove_MyEvent(EventHandler<NewMailEventArgs> value) {
MyEvent = (EventHandler)Delegate.Remove(MyEvent, value);
}
So, as you see, you cannot directly access to delegate and can only call += and -=, which is overridden for event class.
Also you can manually manage this mechanism by overriding methods += and -=.
You can do it like this:
public event EventHandler MyEvent
{
add { //your code for += here }
remove { //your code for -= here }
}
More about event and delegates you can read in book "CLR via C#". I found all of this in this book.
esentially, you are adding a handler to the event, not setting the one handler. you might want to have more handlers for an event. one handler should not preclude having other handlers because there might be multiple actions that you could want to take place in response to a single event that might happen in different classes and in different places and on different threads and under different conditions. += says make me a subscriber to this event (and potentially one subscriber among many).
What if you want to have multiple methods called on Click event. What you are doing with
this.button1.Click += new System.EventHandler(this.button1_Click);
is registering for this.button1_Click method to be invoked when Click event is raised. += adds handler and NOT assigns handler.
1/ it can not be '='(equals) because delegate is like a function pointer
2/ If you want to pass parameter to event button click, you have to make your own button class and implement Click event and have you own EventArgs
sample code:
public class MyEventArg
{
int _param1;
string _param2;
//you can add more param
public MyEventArg(int _param1,string _param2)
{
this._param1 = _param1;
this._param2 = _param2;
}
}
public delegate void MyButtonClickHandler(object sender, MyEventArg e)
public class MyButton:Control
{
public event MyButtonClickHandler OnMyClick;
//You can raise your event here
protected override void OnClick(EventArgs e)
{
MyEventArg e = new MyEventArg(1,"a");//just sample data here
this.OnMyClick(this,e);
}
}
In the form that contains MyButton class instant
public partial class Form1 : Form
{
MyButton myButton = new MyButton();
public Form1()
{
InitializeComponent();
myButton.OnMyClick += new MyButtonClickHandler(this.myButton_OnMyClicked);
}
private void myButton_OnMyClicked(object sender, MyEventArg e){
//your implementation
}
}
Dear Nagaraj Tantri,
For question 1: As said above, Due to Delegate can set up multi-event.
For question 2:As culithay said, if you want to pass custom arguments throug event buttion
click, if you want to use EventHandler and pass cutom own argument
you have to custom your control class and custom own event argument,
the custom event parameter CustomEventArg should inherit EventArg class.
You can take the sample code as below.
// Customs ColorChanged's event parameter.
public class ColorChangedEventArgs : EventArgs
{
private Color color;
public ColorChangedEventArgs(Color c)
{
color = c;
}
public Color GetColor
{
get { return color; }
}
}
//Add this method in your custom control
protected void ibtnTest_Click(object sender, ColorChangedEventArgs e)
{
//TODO;
}
You can also referen MSDN here

Rewrite lambda extension method

I've created an extension method that works just like I wanted. I've noticed that somehow the party and property parameters are 'copied' into the lambda expression. This way I do not need to maintain a custom list of editor/party/property associations.
However, I need to reset the ButtonEdit's ButtonClick event. Since this one is anonymous I cannot use the -= opertor either.
So, my question is - how do I rewrite this method so that the delegate can be removed? Or, which other approach can I use to handle a specific event handler with extra parameters (such as party and property)?
private static void SetupAddressButtonClickEvent(this ButtonEdit editor, Party party, string property)
{
editor.SetAddressDisplayText(party, property);
editor.ButtonClick += (sender, e) =>
{
party.ShowAddressLookupDialog(property);
editor.SetAddressDisplayText(party, property);
};
}
Thank you,
Stefan
Action<object,EventArgs> myaction = (sender, e) =>
{
party.ShowAddressLookupDialog(property);
editor.SetAddressDisplayText(party, property);
};
editor.ButtonClick += myaction;
editor.ButtonClick -= myaction;
edit option 2 could be:
class MyEventHandler
{
... _property;
... _party;
... _editor;
public MyEventHandler(... property, ... party, ... editor)
{
_property = property;
_party = party;
_editor = editor;
}
public void Handler(object sender, EventArgs e)
{
_party.ShowAddressLookupDialog(_property);
_editor.SetAddressDisplayText(_party, _property);
}
}
and then use it like this:
var handler = new MyEventHandler(party,property,editor);
editor.ButtonClick += handler.Handler;
I'm not sure how much this will help you because I don't 100% understand what you're trying to solve.

Categories