Trigger event with sender from subscripted event or this? - c#

Imagine I have a class (form / window) that has a button. In my class I subscribe the button to a method and I throw my own event in it:
public delegate void MyEventHandler(object source, MyEventArgs e);
public event MyEventHandler MyEvent;
protected virtual void OnMyEvent(MyEventArgs e)
{
if (this.MyEvent != null) this.MyEvent(this, e);
}
private void Button_Click(object sender, EventArgs e)
{
this.OnMyEvent(???, ???);
}
What is the correct way to trigger my event. Should I use this with my own EventArgs?
this.OnMyEvent(this, new EventArgs());
Or should I send my sender and event arguments down to the subscription of my own event?
this.OnMyEvent(sender, e);
What is the correct way. Should we stick to one method only? How is Microsoft doing it? Any code guidelines?
Thank you.

Think about who will be subscribing to your event. Will they need to know if it came from a button? Or will they only need to know that it was fired?
Personally I would prefer the listener to only know the event was triggered and the sender is my class which gives me the freedom to change who can fire the event in the first place e.g. button click, timer etc.

Related

Trespassing an Event

I have a control which has an Event handler. I normally used this control on many pages where I manage the raised event. But now, just once I need to put this control into another control and subsequently on a page. Is there a way to trespassing the event from the original control?
Normal situation
Control1 > Page
Control1
public event EventHandler MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
if(this.MyEvent != null) this.MyEvent(this, e);
}
Page.aspx
<ASP:Control1 id="ctrl1" runat="server" OnMyEvent="ctrl1_MyEvent" />
Page.aspx.cs
protected void ctrl1_MyEvent(object sender, EventArgs e)
{
....
}
Exceptional case
Control1 > Control2 > Page
How can I do to re-raise the event to be managed on the page as described above? Is it possible?
I was planning to declare again on the second control an event handler and then create a method that raise the event, but I was thinking if there is another way to do this.
I'd suggest creating your own class, which I would call 'SomeNameEventManager'.
In your parent control, you directly subscribe / unsubscribe via this class.
And in your child control, you can raise the event by calling the method (e.g. OnChanged).
Example code:
public class SomeNameEventManager
{
ChangedEventHandler(object sender, EventArgs e);
public static event ChangedEventHandler Changed;
public static void OnChanged(EventArgs e) { Changed(this, e); }
}
Maybe you come up with a more dynamic way to do this.
In any case I guess, the way to go is to have a class in the middle.

How can you fire the Button Click event without actually clicking the button?

I am trying to fire the Button Click event without clicking the actual button. Is there a way to invoke that? I want to call the Cancel event and here is what I have so far:
protected void Cancel(object sender, EventArgs e)
{
this.BindGridView();
}
Since all you have is this.BindGridView(), which I am assuming is just another method, you can just call that instead.
However, if you wanted to do more than just that, what I usually do is create methods for all my events in the form of OnXxx and call that from the event handler.
So for your example, you would create an OnCancel method:
private void OnCancel()
{
this.BindGridView();
}
... and then in your event handler:
protected void Cancel(object sender, EventArgs e)
{
this.OnCancel();
}
Doing this allows your event to trigger naturally and still function as well as allowing you the ability to call the same thing from anywhere within your code via this.OnCancel().
You can call the cancel method directly from within the same object:
Cancel(null,null)
Server side I've done it like this:
private void Page_Load(object sender, EventArgs e)
{
//Do something
Cancel(sender, e);
}

Event handler/raiser code snippet

I would like to hear opinions on below code snippet. Is there anything that can be improved? Is the event handler/raiser naming following best practices? I know it is not that useful to handle and raise events in the same class but this is just a snippet.
public class MyControl
{
public MyControl()
{
this.LogWritten += this.HandleMyControlLogWritten;
}
// Event handler
void HandleMyControlLogWritten(object sender, EventArgs e)
{
}
// Event object
public event Action<object, EventArgs> LogWritten;
// Event raiser
protected virtual void OnLogWritten(EventArgs e)
{
if (this.LogWritten != null)
{
this.LogWritten(this, e);
}
}
}
The main change I'd recommend would be to get a copy of the event handler:
// Event raiser
protected virtual void OnLogWritten(EventArgs e)
{
var handler = this.LogWritten;
if (handler != null)
{
handler(this, e);
}
}
This is important if you're planning to (eventually) use this class in a multi-threaded scenario. As such, I find that it's a good "best practice" to get into the habit of using. The issue is that, when using in multiple threads, without creating the copy, it's possible that the only "handler" attached could unsubscribe between the null check and the invocation, which would cause a runtime error. By copying to a temporary variable (the var handler = this.LogWritten;) line, you're effectively creating a "snapshot" of the subscriber list, and then checking it for null and invoking if required.
The other change is in the event declaration itself. Instead of using Action<T1,T2>:
// Event object
public event Action<object, EventArgs> LogWritten;
I would recommend using EventHandler<TEventArgs> (if you want to use a custom EventArgs subclass) or EventHandler (for standard EventArgs). These are more "standard practice", and will be what other developers expect:
// Event object
public event EventHandler LogWritten;

Event sender from custom control

I have a control which extends UserControl. This control contains two ComboBox controls. I've created an event handler which fires when either of the combos changes:
public event EventHandler ComboChanged
{
add { cmbA.SelectedIndexChanged += value; cmbB.SelectedIndexChanged += value; }
remove {...}
}
When I add an event handler to this event, is there any way for the sender to be reported as the custom control (i.e. the ComboBox's parent control) rather than the ComboBox itself? Or am I trying to do something I shouldn't be doing here?
You should have something like this :
public event EventHandler MyControlChanged
and then in your userControl two functions for each of the ComboBox
protected void oncmbA_SelectedIndexChanged(object sender, EventArgs e)
{
if (MyControlChanged!=null)
MyControlChanged(this, e);//or some new Eventagrs that you wish to communicate
}
protected void oncmbB_SelectedIndexChanged(object sender, EventArgs e)
{
if (MyControlChanged!=null)
MyControlChanged(this, e);//or some new Eventagrs that you wish to communicate
}
this would then refer to the UserControl and not to the combobox that fired your UserControl's event.
Yoann's answer is the way to go. Here's a similar pattern, but with some minor differences.
// Default listener makes null-check unnecessary when raising event.
// Note that no custom implementations are provided for add, remove.
public event EventHandler ComboChanged = delegate { };
...
foreach(var comboxBox in new[] {cmbA, cmbA})
{
// Attach listener to combo-box's event that raises our own event.
// Lambda-expression is ok since we don't intend to ever unsubscribe.
comboBox.SelectedIndexChanged += (sender, args) => ComboChanged(this, args);
}

How to write a "Preview"-type event that can cancel a main event?

Let's say I have an event. Let's call it DoStuff. Before DoStuff happens, I'm trying to use another event, PreviewDoStuff, to check if I need to prevent DoStuff from being called. The problem I'm facing with this is that I'm unable to get any kind of value back from the PreviewDoStuff, like a return code or anything due to how events are written. And event args aren't passed back to the caller, so I can't get the event args "handled" property. I'm sure there has to be a way to do it, but I'm not sure how. Any ideas?
Thanks!
Declare the event as EventHandler<CancelEventArgs>. Then the listener can set Cancel to true, and you can use that value to determine whether to raise the "real" event or not.
Mandatory code sample:
public event EventHandler<CancelEventArgs> PreviewDoStuff;
public event EventHandler DoStuff;
private void RaiseDoStuff()
{
CancelEventArgs args = new CancelEventArgs();
OnPreviewDoStuff(args);
if (!args.Cancel)
{
OnDoStuff(EventArgs.Empty);
}
}
protected void OnPreviewDoStuff(CancelEventArgs e)
{
EventHandler<CancelEventArgs> previewDoStuff = PreviewDoStuff;
if (previewDoStuff != null)
{
previewDoStuff(this, e);
}
}
protected void OnDoStuff(EventArgs e)
{
EventHandler doStuff = DoStuff;
if (doStuff != null)
{
doStuff(this, e);
}
}
For an example of this in real-life use, check the FormClosing event, which uses a FormClosingEventArgs class, which in turn inherits from CancelEventArgs.
Have your PreviewDoStuff set an internal flag which DoStuff checks when it fires. Even better, have the code that raises DoStuff check the flag before it raises the event.
I assume you mean you want to create your own event called DoStuff. I think it's possible to pass "ref" arguments:
public delegate void PreviewDoStuffFunc(ref bool Handled);
public event PreviewDoStuffFunc PreviewDoStuff;
But the standard way would be to use something like CancelEventArgs:
public event CancelEventArgs PreviewDoStuff;
public event EventArgs DoStuff;
Then after you fire the preview event, you check the Cancel:
var e = new CancelEventArgs();
if (PreviewDoStuff != null)
PreviewDoStuff(this, e);
if (!e.Cancel)
DoStuff(this, EventArgs.Empty);

Categories