Define Eventhandler with EventArgs and Event to work in multiple classes - c#

I have my custom EventArgs in a separate class file that I can reference it later from different classes:
using System;
using System.Collections.Generic;
namespace SplitView
{
public class RowSelectedEventArgs:EventArgs {
public Patient selectedRow { get; set; }
public RowSelectedEventArgs(Patient selectedRow) : base(){
this.selectedRow = selectedRow;
}
}
}
In my MasterViewController I defined my event
public event EventHandler<RowSelectedEventArgs> RowClicked;
In DataSource which is in MasterViewController I can raise the event:
if (this.controller.RowClicked != null) {
this.controller.RowClicked (this, new RowSelectedEventArgs (this.controller.list [indexPath.Row]));
}
As you can see I have a field (controller) in my DataSource with which I reference the event. Now I have a SearchSource with the same concept (also field called controller). Now in SearchSource I want to raise the event:
if (this.controller.RowClicked != null) {
this.controller.RowClicked (this, new RowSelectedEventArgs (this.list [indexPath.Row]));
}
But I get
The event 'SplitView.MasterViewController.RowClicked' can only appear
on the left hand side of += or -= when used outside of the type
'SplitView.MasterViewController'
The only difference is that SearchSource is not part of the class MasterViewController (as it is with DataSource). But the event is public so it should work?
How can I raise the same event from different classes?

You can't directly raise an event outside of the type, which defines this event.
All you can do, is a method, which will raise event from outside:
public sealed class MyClass
{
// this should be called from inside
private void OnSomeEvent()
{
var handler = SomeEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
// this should be called from outside
public void RaiseSomeEvent()
{
OnSomeEvent();
}
public event EventHandler SomeEvent;
// other code here...
}

Is the field conroller in SearchSource also fo type MasterViewController?
It seems that it is a different type.

Related

How do I add and subtract event handlers inside a derived abstract class?

Short version
In my abstract class MyCbo_Abstract (derived from ComboBox class), I want to create a custom property that when set will subtract all the control's event handlers, set the base property value, then re-add all the control's event handlers.
What I have so far
I have a concrete ComboBox class derived from an abstract ComboBox class derived from Microsoft's ComboBox class.
public abstract class MyCbo_Abstract : ComboBox
{
public MyCbo_Abstract() : base()
{
}
}
public partial class MyCboFooList : MyCbo_Abstract
{
public MyCboFooList() : base()
{
}
}
My main Form class subscribes to certain base ComboBox events.
Note: The designer has: this.myCboFooList = new MyCboFooList();
public partial class FormMain : Form
{
public FormMain()
{
myCboFooList.SelectedIndexChanged += myCboFooList_SelectedIndexChanged;
}
private void myCboFooList_SelectedIndexChanged(object sender, EventArgs e)
{
// do stuff
}
}
There are times when I want to suppress the invocation of defined event handlers, e.g., when I programmatically set a ComboBox object's SelectedIndex property.
Instead of having to remember to write the code to subtract and re-add event handlers each time I want to modify the SelectedIndex property and suppress its events, I want to create a custom property SelectedIndex_NoEvents that when set will subtract all the control's event handlers, set the base property value SelectedIndex, then re-add all the control's event handlers.
The problem
My problem is that I don't know how to iterate over a EventHandlerList because it has no GetEnumerator. And, in looking at the list in the debugger, saveEventHandlerList is a weird chained thing that I can't figure out how to otherwise traverse.
public abstract class MyCbo_Abstract : ComboBox
{
int selectedIndex_NoEvents;
public int SelectedIndex_NoEvents
{
get
{
return base.SelectedIndex;
}
set
{
EventHandlerList saveEventHandlerList = new EventHandlerList();
saveEventHandlerList = Events;
//foreach won't work - no GetEnumerator available. Can't use for loop - no Count poprerty
foreach (EventHandler eventHandler in saveEventHandlerList)
{
SelectedIndexChanged -= eventHandler;
}
base.SelectedIndex = value;
//foreach won't work - no GetEnumerator available. Can't use for loop - no Count poprerty
foreach (EventHandler eventHandler in saveEventHandlerList)
{
SelectedIndexChanged += eventHandler;
}
saveEventHandlerList = null;
}
}
//Probably don't need this
public override int SelectedIndex
{
get
{
return base.SelectedIndex;
}
set
{
base.SelectedIndex = value;
}
}
public DRT_ComboBox_Abstract() : base()
{
}
}
Before giving you the solution that I created, let me say that this feels extremely hacky. I urge you to seriously think about another solution. There may be all kinds of crazy edge cases where this code breaks down, I haven't thoroughly tested it beyond the example code shown below.
Add the following utility class:
public class SuspendedEvents
{
private Dictionary<FieldInfo, Delegate> handlers = new Dictionary<System.Reflection.FieldInfo, System.Delegate>();
private object source;
public SuspendedEvents(object obj)
{
source = obj;
var fields = obj.GetType().GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var fieldInfo in fields.Where(fi => fi.FieldType.IsSubclassOf(typeof(Delegate))))
{
var d = (Delegate)fieldInfo.GetValue(obj);
handlers.Add(fieldInfo, (Delegate)d.Clone());
fieldInfo.SetValue(obj, null);
}
}
public void Restore()
{
foreach (var storedHandler in handlers)
{
storedHandler.Key.SetValue(source, storedHandler.Value);
}
}
}
You can use it like this:
var events = new SuspendedEvents(obj); //all event handlers on obj are now detached
events.Restore(); // event handlers on obj are now restored.
I used the following test setup:
void Main()
{
var obj = new TestObject();
obj.Event1 += (sender, e) => Handler("Event 1");
obj.Event1 += (sender, e) => Handler("Event 1");
obj.Event2 += (sender, e) => Handler("Event 2");
obj.Event2 += (sender, e) => Handler("Event 2");
obj.Event3 += (sender, e) => Handler("Event 3");
obj.Event3 += (sender, e) => Handler("Event 3");
Debug.WriteLine("Prove events are attached");
obj.RaiseEvents();
var events = new SuspendedEvents(obj);
Debug.WriteLine("Prove events are detached");
obj.RaiseEvents();
events.Restore();
Debug.WriteLine("Prove events are reattached");
obj.RaiseEvents();
}
public void Handler(string message)
{
Debug.WriteLine(message);
}
public class TestObject
{
public event EventHandler<EventArgs> Event1;
public event EventHandler<EventArgs> Event2;
public event EventHandler<EventArgs> Event3;
public void RaiseEvents()
{
Event1?.Invoke(this, EventArgs.Empty);
Event2?.Invoke(this, EventArgs.Empty);
Event3?.Invoke(this, EventArgs.Empty);
}
}
It produces the following output:
Prove events are attached
Event 1
Event 1
Event 2
Event 2
Event 3
Event 3
Prove events are detached
Prove events are reattached
Event 1
Event 1
Event 2
Event 2
Event 3
Event 3
There is no way to easily disable event firing of WinForm controls exposed in the .Net framework. However, the Winform controls follow a standard design pattern for events in that all event signatures are based on the EventHandler Delegate and the registered event handlers are stored in an EventHandlerList that is defined in the Control Class. This list is stored in a field (variable) named "events" and is only publicly exposed via the read-only property Events.
The class presented below uses reflection to temporarily assign null to the events field effectively removing all event handlers registered for the Control.
While it may be an abuse of the pattern, the class implements the IDisposable Interface to restore the events field on disposal of the class instance. The reason for this is to facilitate the use of the using block to wrap the class usage.
public class ControlEventSuspender : IDisposable
{
private const string eventsFieldName = "events";
private const string headFieldName = "head";
private static System.Reflection.FieldInfo eventsFieldInfo;
private static System.Reflection.FieldInfo headFieldInfo;
private System.Windows.Forms.Control target;
private object eventHandlerList;
private bool disposedValue;
static ControlEventSuspender()
{
Type compType = typeof(System.ComponentModel.Component);
eventsFieldInfo = compType.GetField(eventsFieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
headFieldInfo = typeof(System.ComponentModel.EventHandlerList).GetField(headFieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
}
private static bool FieldInfosAquired()
{
if (eventsFieldInfo == null)
{
throw new Exception($"{typeof(ControlEventSuspender).Name} could not find the field '{ControlEventSuspender.eventsFieldName}' on type Component.");
}
if (headFieldInfo == null)
{
throw new Exception($"{typeof(ControlEventSuspender).Name} could not find the field '{ControlEventSuspender.headFieldName}' on type System.ComponentModel.EventHandlerList.");
}
return true;
}
private ControlEventSuspender(System.Windows.Forms.Control target) // Force using the the Suspend method to create an instance
{
this.target = target;
this.eventHandlerList = eventsFieldInfo.GetValue(target); // backup event hander list
eventsFieldInfo.SetValue(target, null); // clear event handler list
}
public static ControlEventSuspender Suspend(System.Windows.Forms.Control target)
{
ControlEventSuspender ret = null;
if (FieldInfosAquired() && target != null)
{
ret = new ControlEventSuspender(target);
}
return ret;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
if (this.target != null)
{
RestoreEventList();
}
}
}
this.disposedValue = true;
}
public void Dispose()
{
Dispose(true);
}
private void RestoreEventList()
{
object o = eventsFieldInfo.GetValue(target);
if (o != null && headFieldInfo.GetValue(o) != null)
{
throw new Exception($"Events on {target.GetType().Name} (local name: {target.Name}) added while event handling suspended.");
}
else
{
eventsFieldInfo.SetValue(target, eventHandlerList);
eventHandlerList = null;
target = null;
}
}
}
Example usage in the button1_Click method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (ControlEventSuspender.Suspend(comboBox1))
{
comboBox1.SelectedIndex = 3; // SelectedIndexChanged does not fire
}
}
private void button2_Click(object sender, EventArgs e)
{
comboBox1.SelectedIndex = -1; // clear selection, SelectedIndexChanged fires
}
private void button3_Click(object sender, EventArgs e)
{
comboBox1.SelectedIndex = 3; // SelectedIndexChanged fires
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Console.WriteLine("index changed fired");
System.Media.SystemSounds.Beep.Play();
}
}
SoapBox Diatribe
Many will say that the use of Reflection to access non-public class members is dirty or some other derogatory term and that it introduces a brittleness to the code as someone may change the underlying code definition such that the code that relies on member names (magic strings) is no longer valid. This is a valid concern, but I view it as no different than code that accesses external databases.
Reflection can be thought of a query of a type (datatable) from an assembly (database) for specific fields (members: fields, properties, events). It is no more brittle than a SQL statement such as Select SomeField From SomeTable Where AnotherField=5. This type of SQL code is prevent in the world and no one thinks twice about writing it, but some external force could easily redefine the database you code relies on an render all the magic string SQL statements invalid as well.
Use of hard coded names is always at risk of being made invalid by change. You have to weigh the risks of moving forward versus the option of being frozen in fear of proceeding because someone wants to sound authoritative (typically a parroting of other such individuals) and criticize you for implementing a solution that solves the current problem.
I was hoping to write code that would programatically locate all event handler method names created using controlObject.Event += EventHandlerMethodName, but as you see in the other answers, code to do this is complicated, limited, and perhaps not able to work in all cases
This is what I came up with. It satisfies my desire to consolidate the code that subtracts and re-adds event handler method names into my abstract class, but at the expense of having to write code to store and manage event handler method names and having to write code for each control property where I want to suppress the event handler, modify the property value, and finally re-add the event handler.
public abstract class MyCbo_Abstract : ComboBox
{
// create an event handler property for each event the app has custom code for
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
private EventHandler evSelectedValueChanged;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EventHandler EvSelectedValueChanged { get => evSelectedValueChanged; set => evSelectedValueChanged = value; }
public MyCbo_Abstract() : base()
{
}
// Create a property that parallels the one that would normally be set in the main body of the program
public object _DataSource_NoEvents
{
get
{
return base.DataSource;
}
set
{
SelectedValueChanged -= EvSelectedValueChanged;
if (value == null)
{
base.DataSource = null;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
string valueTypeName = value.GetType().Name;
if (valueTypeName == "Int32")
{
base.DataSource = null;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
//assume StringCollection
base.DataSource = value;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
}
}
public partial class MyCboFooList : MyCbo_Abstract
{
public MyCboFooList() : base()
{
}
}
Designer has
this.myCboFooList = new MyCboFooList();
Main form code
public partial class FormMain : Form
{
public FormMain()
{
myCboFooList.SelectedValueChanged += OnMyCboFooList_SelectedValueChanged;
myCboFooList.EvSelectedValueChanged = OnMyCboFooList_SelectedValueChanged;
}
private void OnMyCboFooList_SelectedValueChanged(object sender, EventArgs e)
{
// do stuff
}
}
And now, if I want to set a property and suppress event(s), I can write something like the following and not have to remember to re-add the event handler method name
myCboFooList._DataSource_NoEvents = null;

Null Reference Exception When Raising Event in C#

What would be the equivalent C# code against following VB.Net code:
Public Event EndOfVideo()
Private Sub RaiseEndOfVideo()
RaiseEvent EndOfVideo()
End Sub
EDIT
Here is the equivalent C# code that telerik converter generated for me.
public event EndOfVideoEventHandler EndOfVideo;
public delegate void EndOfVideoEventHandler();
private void RaiseEndOfVideo()
{
if (EndOfVideo != null) {
EndOfVideo();
}
}
Calling RaiseEndOfVideo doesn't trigger/invoke EndOfVideo event, and Null Reference Exception is raised.
Consider you have class VideoPlayer which has event EndOfVideo and you want to raise this event and when someone calls method EndVideo on object of VideoPlayer.
Now, like any other member of a class event also initialized to null and gets the value when some handler is attached to it.
Attaching an handler to an event happens using += operator.
public class VideoPlayer
{
public event EndOfVideoEventHandler EndOfVideo;
// Following delegate indicates that the a method accepting no parameter
// and returning void can be attached as an handler to this event.
public delegate void EndOfVideoEventHandler();
public void EndVideo()
{
RaiseEndOfVideo();
}
private void RaiseEndOfVideo()
{
if (EndOfVideo != null)
{
// Following line of code executes the event handler which is
// attached to the event.
EndOfVideo();
}
}
}
public class WebPage
{
public void VideoStopped()
{
Console.WriteLine("Video Stopped");
}
}
Now in Main method of program.cs
static void Main(string[] args)
{
VideoPlayer player = new VideoPlayer();
WebPage page = new WebPage();
player.EndOfVideo += page.VideoStopped;
// Following method call on player object will call internally
// RaiseEndOfVideo which will Raise event and event will execute
// VideoStopped method of page object which is attached in previous line
// and display "Video Stopped" message in Console.
player.EndVideo();
Console.WriteLine("Completed!!! Press any key to exit");
Console.ReadKey();
}
I hope this would help you start understanding how events and delegates work in C#. For further reading you can go thru https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx
This is the generally accepted way to write an event with no parameters:
public class Foo
{
public event EventHandler EndOfVideo;
protected virtual void OnEndOfVideo()
{
var handler = EndOfVideo;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
Your code is what was needed in the old days: creating a delegate and yada yada.
To state the obvious, though, you need to subscribe to an event with something like:
public class Bar
{
public void DoAllTheThings()
{
var foo = new Foo();
foo.EndOfVideo += foo_EndOfVideo;
}
void foo_EndOfVideo(object sender, EventArgs e)
{
Console.WriteLine("EndOfVideo");
}
}
For the sake of completeness, the EventHandler delegate has a generic counterpart, EventHandler<T>, which you would use when you want an event that does have parameters, where T should be a class inheriting from System.EventArgs which holds the information you want your event to expose.
To do that, you will need to create a custom eventHandler to specify the method signatures of the handlers for your event

C# Subscribing an Event

I have the following code with 3 different classes. I am trying to
Subscribe event from class B to method (event handler) defined in
class ControlSystem. All compiles fine, it works no problem but the event handler method is never triggered... What am I doing wrong?
namespace EventTest
{
public class ControlSystem : CrestronControlSystem
{
A myObject = new A();
public ControlSystem(): base()
{
Thread.MaxNumberOfUserThreads = 100;
// Subscribe Event
myObject.mySubObject.BEvent += HandleBEvent;
// Throw Event
myObject.mySubObject.ThrowEvent();
}
public override void InitializeSystem()
{
}
public void HandleBEvent(object sender, EventArgs args)
{
Console.WriteLine("Something happend to {0}", sender);
}
}
public class A
{
public B mySubObject;
public A()
{
mySubObject = new B();
}
}
public class B
{
public EventHandler BEvent;
public B(){}
public void ThrowEvent()
{
EventHandler handler = BEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
Real code links below (it works with Embeded system so you won't be able to compile it). Idea is to have button press to trigger an event which could
alarm other UIs that something happend to it.
http://ideone.com/NJz2Ek
Thanks
You are missing the event keyword.
public event EventHandler BEvent;
is what needs to be there.

How to use a existed event in C#?

I am working a problem which is about delegate and event. I am a newbid in this aspect. I don't know how to call the event.
Would some tell me?
Thanks in advance.
Here is simple example to call event....
// event_keyword.cs
using System;
public delegate void MyDelegate(); // delegate declaration
public interface I
{
event MyDelegate MyEvent;
void FireAway();
}
public class MyClass: I
{
public event MyDelegate MyEvent;
public void FireAway()
{
if (MyEvent != null)
MyEvent();
}
}
public class MainClass
{
static private void f()
{
Console.WriteLine("This is called when the event fires.");
}
static public void Main ()
{
I i = new MyClass();
i.MyEvent += new MyDelegate(f);
i.FireAway();
}
}
There is Link which may helpful.
The event can be invoked in the class in which it is declared. First you'll usually want to check if your event is null.
if (MyEvent != null) MyEvent(this, new EventArgs());
The arguments you pass to the event will depend on the declaration of the event. To give you a little more background, an event is just a compiler trick. When an event such as
public event ChangedEventHandler Changed;
is compiled it will look like
protected ChangedEventHandler _change;
public ChangedEventHandler Change
{
add { _change += value; }
remove { _change -= value; }
}
so anything inside where it is declared will use _change, while anything outside will use Change. In other words, inside where it is declared, it is just a delegate, and all the normal rules apply.
To resuse the event you just need to attach event with the you control for example .
buttonone.Click+= event1;
buttonTwo.Click+= event1;
Fore more details have look : C# Event Implementation Fundamentals, Best Practices and Conventions
Once you have defined the delegate, you need to define when to call the event. I mean you can call the event at assignment of any value to the specific variable.
here is the example of defining the delegate with the same variable class.
public class callbackdel : EventArgs
{
public readonly string resp = null;
public callbackdel(string s)
{
resp = s;
}
}
public delegate void WorkerEndHandler(object o, callbackdel e);
Now in the control you are using, you need to add this method.
public void OnWorkEnd(object o, callbackdel e)
{
WorkEnd(o, e);
}
after creating method and defining the delegate, you can fire the event from any of the delegate simply by calling the method.
OnWorkEnd((object)this, e);
When using an Event you first have to declare it:
// Create some custom arguments for the event
public class SampleEventArgs
{
public SampleEventArgs(string s)
{
Text = s;
}
public String Text {get; private set;}
}
// Define a class that uses the event
public class EventPublisher
{
// Declare the delegate
public delegate void SampleEventHandler(object sender, SampleEventArgs e);
// Declare the event.
public event SampleEventHandler SampleEvent;
// Wrap the event in a protected virtual method
// to enable derived classes to raise the event.
protected virtual void RaiseSampleEvent()
{
// Raise the event by using the () operator.
if (SampleEvent != null)
SampleEvent(this, new SampleEventArgs("Hello"));
}
}
You can then subscribe to the event:
EventPublisher publisher = new EventPublisher();
publisher.SampleEvent += new EventPublisher.SampleEventHandler(SampleEventHandler);
public void SampleEventHandler(object sender, SampleEventArgs args)
{
}
Your event handler will be called when EventPublisher executes RaiseSampleEvent()

How to override method and event in WinForm UserControl in C#?

I have a custom control in C# WinForms called BaseControl and there I have a property called Selected.
I want to have an event SelectedChanged and virtual method OnSelecteChanged in the base control and they should behave in the same manner as we have in Control class for many properties i.e. Click event and OnClick method.
Means anyone who derives from my BaseControl can either bind to the event or can override the OnSelectedChanged method.
So, when the value of Selected property is changed event should be fired and if the method is overridden control should go to that method.
I know how to fire the event but don't know how to do it for method.
Please guide me...
Below is an example of how events should be implemented:
public class BaseControl : Control
{
private object _selected;
public object Selected
{
get { return _selected; }
set
{
if (!Equals(_selected, value))
{
_selected = value;
OnSelectedChanged(EventArgs.Empty);
}
}
}
public event EventHandler SelectedChanged;
protected virtual void OnSelectedChanged(EventArgs e)
{
if (SelectedChanged != null)
SelectedChanged(this, e);
}
}
With this example, you can override OnSelectedChanged in an overriden class, like this:
public class MyControl : BaseControl
{
protected override void OnSelectedChanged(EventArgs e)
{
base.OnSelectedChanged(e);
// My own logic.
}
}
private bool _selected;
public bool Selected
{
get { return _selected; }
set
{
if (value != _selected)
{
_selected = value;
OnSelectedChanged();
}
}
}
public event EventHandler SelectedChanged;
protected virtual void OnSelectedChanged()
{
var handler = SelectedChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
Basically you don't fire the event from your Selected property setter - you call the method, and make the method call the event. Anyone overriding the method should call base.OnSelectedChanged to make sure the event still fires. So your method should look something like this:
protected virtual void OnSelectedChanged(EventArgs e) {
EventHandler handler = Selected; // Or your own delegate variable
if (handler != null) {
handler(this, e);
}
}

Categories