I have a custom checkbox control that inherited from System.Windows.Forms.Control
and it hasn't CheckedChanged event. I want to implement CheckedChange same as dot net native CheckBox. How can I do it well ?
You are inheriting fromn Control, not CheckBox, so the solution is similar to the one proposed by Frigik, but it's not exactly that one.
First of all you have to define the event in your class, i.e.:
public event EventHandler CheckedChanged;
In this way every developer using your control can subscribe/unsubscribe to the event. This is not enough, since the event will never be triggered. To do so, you have to define a method to trigger it, and the call this method whenever the state of your control changes:
private void RaiseCheckedChanged()
{
if (CheckedChanged!= null)
CheckedChanged(this, EventArgs.Empty);
}
Where this method will be called depends on the structure of your control. For instance if you have a property Checked, you could call the method in its setter:
public bool Checked
{
get { return _checked; }
set
{
_checked = value;
RaiseCheckedChanged();
}
}
Try this code :
CheckBox chkList1 = new CheckBox();
chkList1.CheckedChanged += new EventHandler(CheckBox_CheckedChanged);
protected void CheckBox_CheckedChanged(object sender, EventArgs e)
{
// Do your stuff
}
Try this:
public class YourCheckBox:CheckBox
{
public event EventHandler<EventArgs> OnCheckedChangedCustom;
protected override void OnCheckedChanged(EventArgs e)
{
if (OnCheckedChangedCustom!=null)
{
OnCheckedChangedCustom(this, EventArgs.Empty);
}
base.OnCheckedChanged(e);
}
}
Related
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;
I need a custom NumericUpDown where the event ValueChanged should pass CancelEventArgs instead of EventArgs as I want to be able to cancel the editing when certain conditions are verified (e.g. I have two NumericUpDown that must have always different values). If I try to override OnValueChanged I obviously get an error.
protected override void OnValueChanged(CancelEventArgs e)
{
if (e.Cancel)
return;
else
{
EventArgs args = (EventArgs)e;
base.OnValueChanged(args);
}
}
Is there a way to do this?
I would propose to change a little bit your implementation of the cancel behavior, instead of trying to pass the information of Cancellation through the event arguments, you can query it on demand by introducing a new event to your custom component. Here is a simple example:
class CustomNumericUpDown : NumericUpDown
{
protected override void OnValueChanged(EventArgs e)
{
if (QueryCancelValueChanging != null && QueryCancelValueChanging())
return;
else
{
EventArgs args = (EventArgs)e;
base.OnValueChanged(args);
}
}
public event Func<bool> QueryCancelValueChanging;
}
In this situation, the host of your component can subscribe to the new event in order to decide to cancel or not the "ValueChanged" event.
EDIT:
Usage example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CustomNumericUpDown nudTest = new CustomNumericUpDown();
nudTest.QueryCancelValueChanging += NudTest_QueryCancelValueChanging;
}
private bool NudTest_QueryCancelValueChanging()
{
return true;/* Replace by custom condition here*/
}
}
Perhaps you need to learn how to create and manage custom events if you have never done it before, it should be easy to find tutorials on this topic on the web (like this one )
Please forgive my little knowledge!
I have the following class in HIDNewDeviceEventMonitor.cs:
public class HIDNewDeviceEventMonitor : IDisposable
{
// used for monitoring plugging and unplugging of USB devices.
private ManagementEventWatcher watcherAttach;
public HIDNewDeviceEventMonitor()
{
// Catch USB HID plugged instance event watching
watcherAttach = new ManagementEventWatcher();
watcherAttach.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcherAttach.Query = new WqlEventQuery(#"SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_PNPEntity' AND TargetInstance.DeviceID LIKE 'HID\\VID_04D8%'");
watcherAttach.Start();
}
void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine("my device is inserted..");
}
public void Dispose()
{
watcherAttach.Stop();
watcherAttach.Dispose();
}
~HIDNewDeviceEventMonitor()
{
this.Dispose();
}
}
Now, how can I change this class to be able to add an event handler that the class can call from within watcher_EventArrived where someNewEvent is outside the class file, actually in the form.cs:
// code in the form
HIDNewDeviceEventMonitor ok = new HIDNewDeviceEventMonitor();
ok.Inserted += someNewEvent; // <-- my problem, I don't know how to add an event to the class this way
private void someNewEvent()
{
//Enumerate and add to listbox1
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ok.Dispose();
}
I 've seen this thing with other classes, how can I make my class like that?
Your Inserted event should look like this:
public event EventHandler Inserted;
You invoke it like this:
private void OnInserted()
{
if (this.Inserted != null)
{
this.Inserted(this, EventArgs.Empty);
}
}
The signature for the event handler is this:
void someNewEvent(object sender, EventArgs e)
{
//
}
Then you should wrap that code in the constructor of the class:
HIDNewDeviceEventMonitor ok;
public ClassName()
{
ok = new HIDNewDeviceEventMonitor();
ok.Inserted += someNewEvent; // <-- my problem
}
Declare the ok variable outside the constructor, and instantiate it inside. Then add the event handler.
Pro tip: You could use the generic EventHandler<T> if you need to supply a custom implementation of e.
Simply put, you're trying to add events to your HIDNewDeviceMonitor class.
To do this, first you'll need to define a delegate.
public delegate void InsertedHandler;
Next, you'll need to define the event in your HIDNewDeviceMonitor class.
// Notice how the event uses the delegate that's been defined
// v
public event InsertedHandler Inserted;
Now you'll need something that "fires" the event, which could easily be put in your watcher_EventArrived method.
void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine("my device is inserted..");
// Notice how we check the event handler for null.
// If you don't, it could throw a NullReferenceException.
// Irritating, but easy to debug.. Usually..
if (Inserted != null)
Inserted(); // Or whatever parameters you need.
}
We're all done with the HIDNewDeviceMonitor class.
Now whatever class that uses the HIDNewDeviceMonitor can use the EventHandler code that you provided.
However, it'll have to be the same delegate.
public class MyClass
{
HIDNewDeviceMonitor monitor;
public MyClass()
{
monitor = new HIDNewDeviceMonitor();
monitor.Inserted += DeviceInserted;
}
private void DeviceInserted()
{
// Execute code here
}
}
You need to do following in the HIDNewDeviceEventMonitor class:
1.) First define a public event inside the class like this-
public event EventHandler Inserted;
2.) Then fire this event within the code where you detect the changes in events. Like this-
if(Inserted != null)
Inserted(this,null);
The if condition checks if the event is registered by any listener. It's fired in case it is.
Hope this helps.
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
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);
}
}