When to use empty event accessors in c#? - c#

Interface-Events can be Implemented explicit. For example we are able to pass
delegates to another Event.
Here the TestHandler-Event is wrapped (not sure if its the right term) by the SomeHandler-Event to Implement the ISomeHandleable-Interface.
public delegate void HandlerDelegate();
public interface ISomeHandleable
{
event HandlerDelegate SomeHandler;
}
public class Test : ISomeHandleable
{
event HandlerDelegate ISomeHandleable.SomeHandler
{
add { TestHandler += value; }
remove { TestHandler -= value; }
}
public event HandlerDelegate TestHandler;
public void Fire() => TestHandler?.Invoke();
}
I have just recently seen, that we are also able to Implement ISomeHandleable.SomeHandleras follows:
event HandlerDelegate ISomeHandleable.SomeHandler
{
add { }
remove { }
}
But I have not yet found any documentation and possible usecases to this, and I also dont understand what it does.
I only know, delegates can still be added to ISomeHandleable.SomeHandler but the Event cannot be invoked by the Class Test anymore.
But as you can define Events with empty Accessors, what does it do and how is it meant to be used?

Related

How to pass an event through classes in C#

Here are 3 C# classes :
class StartClass
{
event StartEvent;
// some code
class MidClass
{
private StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
// some code
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
}
// some code
public void OnStartEvent ()
{
// code to be executed on StartEvent called
}
What's best way if you want to attach EndClass.OnStartEvent listener to StartClass.StartEvent event ?
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right ? How to attach StartClass.StartEvent to MidClass.StartEvent ?
In C# an event is much like a property. It is a wrapper for a delegate. Usually we are using auto-implemented events. But we can expand them. Properties have get and set accessors. Events have add and remove accessors.
class MidClass
{
private readonly StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
public event EventHandler StartEvent
{
add => _startClass.StartEvent += value;
remove => _startClass.StartEvent -= value;
}
}
Here, we create a StartEvent in MidClass that is a wrapper for the corresponding event in StartClass. StartClass.StartEvent must be public.
The advantage of this approach is that an event handler subscribing to MidClass.StartEvent will directly be attached to StartClass.StartEvent, with no intermediate call occurring when the event is risen.
One word to naming. Methods named OnEventName are usually used to raise events, while methods named PublisherName_EventName are used for event handlers.
class StartClass
{
public event EventHandler StartEvent;
private virtual void OnStartEvent()
{
StartEvent?Invoke(this, EventArgs.Empty);
}
}
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
_midClass.StartEvent += MidClass_StartEvent;
}
private void MidClass_StartEvent (object sender, EventArgs e)
{
// Code to be executed when StartEvent is triggered
}
}
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right?
If you want to keep the reference to StartClass a private implementation detail of MidClass: Yes, implementing a "proxy" StartEvent in MidClass is the right way to do it.
How to attach StartClass.StartEvent to MidClass.StartEvent ?
By attaching a listener to _startClass.StartEvent which just raises the corresponding event in MidClass:
class MidClass
{
public event EventHandler StartEvent;
private readonly StartClass _startClass;
public MidClass(StartClass startClass)
{
_startClass = startClass;
_startClass.StartEvent += (sender, e) => this.StartEvent?.Invoke(this, e);
}
...
}
Note that I also added the readonly modifier to _startClass: If the value of _startClass changes during the lifetime of MidClass, you need to detach your event handler from the old reference and attach it to the new reference.

Passing a delegate reference to another class so that one subscriber can listen to multiple publishers?

I'm getting my head around more complex uses of the event/delegate system, and have a use case that I can't find a solution for.
Say I've got two classes which both handle objects that inherit from the same base class:
public class VegetableStore {
List<VegType> vegtypes;
}
public class MeatStore {
List<MeatType> meatTypes;
}
// MeatType and VegType both extend abstract class FoodType
In a separate class, I want to listen for whenever EITHER a new VegType is added to VegetableStore, or a new MeatType is added to MeatStore.
Now, the "easy" way to do it seems to just be something like this:
public class VegetableStore {
List<VegType> vegtypes;
public delegate void VegTypeAddedEventHandler(VegType vegType, bool added);
public event VegTypeAddedEventHandler VegTypeAdded;
//...
protected virtual void OnVegTypeAdded(VegType vegType, bool added) {}
}
public class MeatStore {
List<MeatType> meatTypes
public delegate void MeatTypeAddedEventHandler(MeatType meatType, bool added);
public event MeatTypeAddedEventHandler MeatTypeAdded;
//...
protected virtual void OnMeatTypeAdded(MeatType meatType, bool added) {}
}
And then in my listener class...
vegStore.VegTypeAdded += OnFoodTypeAdded;
meatStore.MeatTypeAdded += OnFoodTypeAdded;
void OnFoodTypeAdded(FoodType foodType, bool added) {}
This feels a lot more messy than it could be though.
What I'd like to do is have some kind of "parent" delegate, such as FoodTypeAddedEventHandler(object source, EventArgs e)
And then be able to automatically subscribe any events that subscribe to this event, to subscribe to events from both Meat and Veg, such that I an intermediate eventmanager class can contain
public event FoodTypeAddedEventHandler FoodTypeAdded
{
add
{
vegStore.VegTypeAdded += value;
meatStore.MeatTypeAdded += value;
{
remove
{
vegStore.VegTypeAdded -= value;
meatStore.MeatTypeAdded -= value;
}
}
The trouble is. vegStore and meatStore don't have a delegate of type FoodTypeAddedHandler, so this results in a type error.
Is there a practical way to go about this? Can't seem to find a reasonably straight forward solution to this problem, but I feel like there must be one. Or, should I just KISS it?
You can use generic delegate like this:
delegate void FoodTypeAddedEventHandler<in T>(T meatType, bool added) where T : FoodType;
Then instead of VegTypeAddedEventHandler you have FoodTypeAddedEventHandler<VegType> and instead of MeatTypeAddedEventHandler you have FoodTypeAddedEventHandler<MeatType>. Finally instead of FoodTypeAddedEventHandler you have FoodTypeAddedEventHandler<FoodType>. After this modifications, your "combined" event handler will compile fine:
public event FoodTypeAddedEventHandler<FoodType> FoodTypeAdded
{
add
{
vegStore.VegTypeAdded += value;
meatStore.MeatTypeAdded += value;
{
remove
{
vegStore.VegTypeAdded -= value;
meatStore.MeatTypeAdded -= value;
}
}
Alternatively - just remove all your delegate types and use Action<FoodType, bool>, Action<VegType, bool> and Action<MeetType, bool>. Then your code will also compile fine.
That works because both custom delegate has its first type parameter (T) contravariant (in modifier). That allows it to accept arguments of derived types (so, delegate FoodTypeAddedEventHandler<FoodType> can accept signatures where first parameter is not exactly FoodType but any derived type (such as VegType). The same works with Action because in Action all type arguments are also contravariant (declared with in modifier). More information about variance in delegates you can read in documentation.

Workaround for generic event handler in Windows Forms

Quite some time ago, I noticed that the Windows Forms editor of Visual Studio does not support events which contain generic type parameters. For example, an event like
public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } }
where
public class ListEventArgs<T> : EventArgs { List<T> args; }
does not even show up in the event list in the property manager of Visual Studio. Now, this is a somewhat artificial example that could easily be modified to work in Visual Studio by rewriting the classes and their events. However, I am currently working on a project where I cannot change some classes for compatibility reasons. The only thing I can do is to change the events of my user control. The events of this control currently look like this:
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
Note that the underlying Plane class (represented by the _Plane instance which is a protected field) cannot be changed. Its DrawingError event and its EventArgs type are declared in the Plane class like this:
public class Plane<T> where T : ISurface
{
...
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
...
public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
Of course, the Windows Forms editor of Visual Studio does not show any of the events of my user control. I have been looking for a number of workarounds to get them shown again, but have not been able to find a workaround that actually works. Here are some things that I tried:
Created a MyPlane class which inherits from Plane and used that instead: public event EventHandler<MyPlane.DrawingErrorEventArgs> DrawingError .... For reasons unknown to me, the events still don't show up in the editor. Perhaps this is due to the parameters of the event, some of which still are generic. Find a minimal working example below.
Created a helper class which defines implicit conversion operators between EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> and EventHandler<GDIPlane.DrawingErrorEventArgs> where GDIPlane is just a dummy class which inherits from Plane<GDISurface>. This does work to some extent, but duplicates event calls since the conversion creates new event handlers which are passed down to _Plane which cannot be removed/unregistered properly.
Tried to inherit from EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>, which obviously does not work since EventHandler<T> is sealed.
Are there any other ways to make my events visible again in the Windows Forms editor?
Best regards
Andreas
EDIT: Minimal working example for 1:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
public class GDIPlane : Plane<GDISurface> { }
GDIPlane _Plane = null;
public event EventHandler<GDIPlane.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
DrawingError does not show up in the list of events in the property manager when clicking on a TestControl instance.
EDIT2: This is the original problem (without any workarounds) where the DrawingError event does of TestControl does not show up either:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
Plane<GDISurface> _Plane = null;
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
This is behavior specific to Visual Studio, and the cause is rooted in the fact that EventHandler<> does not specify covariance on its 'TEventArgs' (it would impose seemingly silly restrictions) and the tools do not perform enough introspection of your code to suss out an appropriate type (even though you've left a trail of type data in constructing the control.) Thus, it seems as though VS does not support generic event properties. You may consider filing a feature request on Microsoft Connect, I wouldn't suggest filing it as a bug as they may label it "by design" and close it.
As a general rule, if you need generic type parameters on your events and you need design time support for them (which are different implementation concerns), you're looking at wrapping them in a presentation-specific facade (e.g. "extra layer of code to facilitate design-time needs".)
Personally, I would reduce the generic typing you have in play now, it seems a bit excessive and if you don't understand covariance/contravariance in generic types it might put you in a tight spot at some point, such as now.
However, to work around your problem:
Consider using a custom event args class which could transport data in a non-generic property, and also use a non-generic EventHandler event/property. Understanding the 'type' of the event is then shifted away from generic type parameters and made the responsibility of your non-generic event args instead. If the 'class' of the event args is insufficient, you can add a property to convey the event type (or data type) so that receiving code can properly interpret it (assuming, of course, that it does not already know by some other means.):
public class DataEventArgs : EventArgs
{
//public string EventTypeOrPurpose { get; set; }
public object Data { get; set; }
}
This is most often only used to ferry data through an event chain, and it is usually implemented as follows:
public class DataEventArgs<T> : EventArgs
{
public T Data { get; set; }
}
Unfortunately, this also has a covariance problem, to resolve it you would actually want something more like this:
public interface IDataArgs<out T>
{
T Data { get; }
}
public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
public DataEventArgs<T>(T data)
{
_data = data;
}
private T _data;
public T Data { get { return _data; } }
}
Even so, these generic versions still don't work around Visual Studio's limitations, this is merely more proper alternative forms of what you already have shown us.
UPDATE: As requested, here is what a "purpose built facade" might look like in the most basic sense. Note that the usercontrol functions as a facade layer in this case as the eventhandler it exposes delegates to the underlying object model. There is no direct access to underlying object model from the user control (from consumer/designer perspective.)
Please note the reference tracking for event handlers is not necessary unless you dispose of these user controls throughout the lifetime of the app (it is only done to ensure proper delegate removal based on the delegate provided, which is wrapped in a closure/delegate, as you see below.)
Also worth noting I did not test-run this code beyond verifying that the designer shows DrawingError in the property grid when dropped onto a form.
namespace SampleCase3
{
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
private Plane<GDISurface> _Plane = new Plane<GDISurface>(); // requires initialization for my own testing
public TestControl()
{
}
// i am adding this map *only* so that the removal of an event handler can be done properly
private Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>> _cleanupMap = new Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>>();
public event EventHandler DrawingError
{
add
{
var nonGenericHandler = value;
var genericHandler = (EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>)delegate(object sender, Plane<GDISurface>.DrawingErrorEventArgs e)
{
nonGenericHandler(sender, e);
};
_Plane.DrawingError += genericHandler;
_cleanupMap[nonGenericHandler] = genericHandler;
}
remove
{
var nonGenericHandler = value;
var genericHandler = default(EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>);
if (_cleanupMap.TryGetValue(nonGenericHandler, out genericHandler))
{
_Plane.DrawingError -= genericHandler;
_cleanupMap.Remove(nonGenericHandler);
}
}
}
}
}
To complement the above, here is what a non-generic event handler would now look like:
private void testControl1_DrawingError(object sender, EventArgs e)
{
var genericDrawingErrorEventArgs = e as Plane<GDISurface>.DrawingErrorEventArgs;
if (genericDrawingErrorEventArgs != null)
{
// TODO:
}
}
Note that the consumer here has to have knowledge of the type for e to perform conversion. The use of the as operator will bypass ancestry checks under the assumption that the conversion should succeed.
Something like this is as close as you're going to get. Yes it is ugly by most of our standards, but if you absolutely 'need' design-time support on top of these components and you cannot change Plane<T> (which would be more appropriate) then this, or something close to this, is the only viable workaround.
HTH

How to create a Custom Routed Event ? WPF c#

I followed this tutorial but I couldn't apply what I learned to my project.
I have a LineGraph object (Dynamic Data Display) and I want to create an event that is raised when the thickness of the LineGraph is equal to 0.
How am I supposed to write it following this tutorial ?
Here is how I would do it with a RoutedEvent:
Create a class that derives from LineGraph, let's say CustomLineGraph:
public class CustomLineGraph : LineGraph {
}
Create our routed event like this:
public class CustomLineGraph : LineGraph {
public static readonly RoutedEvent ThicknessEvent = EventManager.RegisterRoutedEvent("Thickness", RoutingStrategy.Bubble, typeof(RoutedEventHandler, typeof(CustomLineGraph));
// .NET event wrapper
public event RoutedEventHandler Thickness
{
add { AddHandler(CustomLineGraph.ThicknessEvent, value); }
remove { RemoveHandler(CustomLineGraph.ThicknessEvent, value); }
}
}
Now we override the StrokeThickness property so we can raise our custom routed event when the value of that property is 0.
public class CustomLineGraph : LineGraph {
public static readonly RoutedEvent ThicknessEvent = EventManager.RegisterRoutedEvent("Thickness", RoutingStrategy.Bubble, typeof(RoutedEventHandler, typeof(CustomLineGraph));
// .NET event wrapper
public event RoutedEventHandler Thickness
{
add { AddHandler(CustomLineGraph.ThicknessEvent, value); }
remove { RemoveHandler(CustomLineGraph.ThicknessEvent, value); }
}
public override double StrokeThickness {
get { return base.StrokeThickness; }
set
{
base.StrokeThickness = value;
if (value == 0)
RaiseEvent(new RoutedEventArgs(CustomLineGraph.ThicknessEvent, this));
}
}
}
We are done !
Personally, I usually avoid creating events, preferring instead to create delegates. If there is some particular reason that you specifically need an event, then please ignore this answer. The reasons that I prefer to use delegates are that you don't need to create additional EventArgs classes and I can also set my own parameter types.
First, let's create a delegate:
public delegate void TypeOfDelegate(YourDataType dataInstance);
Now a getter and setter:
public TypeOfDelegate DelegateProperty { get; set; }
Now let's create a method that matches the in and out parameters of the delegate:
public void CanBeCalledAnything(YourDataType dataInstance)
{
// do something with the dataInstance parameter
}
Now we can set this method as one (of many) handlers for this delegate:
DelegateProperty += CanBeCalledAnything;
Finally, let's call our delegate... this is equivalent to raising the event:
if (DelegateProperty != null) DelegateProperty(dataInstanceOfTypeYourDataType);
Note the important check for null. So that's it! If you want more or less parameters, just add or remove them from the delegate declaration and the handling method... simple.

how to catch changes in a member variable? (C#)

This seems to be basics of the language, but I do not understand how is this accomplished in .Net. I have a member variable in a class, say a bool _isCommitted. I want something to happen whenever _isCommitted is true. Something like this:
//Whenever _isCommitted == true()
{
Foo()
}
Basically like an event, but here it is my variable. How to? Many thanks..
This is normally done through properties and a backing private field. You need to ensure you only ever access through the property.
private bool _isCommitted;
public bool IsCommitted
{
get { return _isCommitted; }
set
{
if(value)
{
//do something
}
_isCommitted = value;
}
}
At the most basic level, you can create an event in your class:
public delegate void MyHandler(bool b);
public event MyHandler CommittedChanged;
Now people can subscribe to your event like so:
public void SomeHandlerMethod(bool b) { ... }
...
someInstance.CommittedChanged += SomeHandlerMethod;
someInstance.CommittedChanged += ASecondHandlerMethod;
someInstance.CommittedChanged += x => { /* inline handler using lambda */ };
A user can unregister his event handler this way:
someInstance.CommittedChanged -= SomeHandlerMethod;
And wherever you decide to change your variable, you will follow it up with:
if (CommittedChanged != null) CommittedChanged(_isCommitted);
This will call everyone who has registered a function with your event.
Having said this, there are plenty of improvements that you can do. First, make _isCommitted into a property, and do the event callback in its setter. This way, you won't forget to call the handlers.
public IsCommitted {
get { return _isCommitted; }
set {
_isCommitted = value;
if (CommittedChanged != null) CommittedChanged(_isCommitted);
}
}
Read more about events here.
This is enough to get you going. However, if you delve further into the C# framework, you will find a standardized way of using this event framework inside of the System.ComponentModel namespace. Sepcifically, the interface INotifyPropertyChanged, which ties neatly into a more generic event system that also plays well with some of Microsoft's own technologies, such as WPF, allowing GUI elements to pick up on changes to your class automatically. Read more about INotifyPropertyChanged here.
You basically need PropertyChangedEvent PropertyChangedEventHandler Delegate
I think C# properties is what you need.
private bool _isCommitted;
public bool IsCommitted
{
get { return _isCommitted; }
set { if(value){/*DO SOMETHING HERE*/}
_isCommitted = value; }
}

Categories