I would like to raise an event and notify existing subscribers if any exist. But I also expect new subscribers to be notified of all events raised so far as soon as they subscribe. Is this possible out of the box or do I need to implement that functionality myself? Right now my code look like this:
public delegate void GridCompiled(int gridsize);
public event GridCompiled OnGridCompiled;
ctor(){
if (OnGridCompiled != null)
OnGridCompiled(gridsize);
}
If event has 0 subscribers it won't be raised and it also won't be raised for subscribers that subscribe after event has been raised.
In case I need to implement that myself, what are my options?
There is no tracking of raising events, so you would have to implement the functionality yourself. You would need a list to store your previous event arguments in order and execute the related events when a new event listener is added:
class Example {
private readonly List<GridCompiledEventArgs> m_previousEventArgs = new List<EventArgs>();
private EventHandler<GridCompiledEventArgs> m_gridCompiled;
public event EventHandler<GridCompiledEventArgs> GridCompiled {
add {
//Raise previous events for the caller
foreach(GridCompiledEventArgs e in m_previousEventArgs) {
value(this, e);
}
//Add the event handler
m_gridCompiled += value;
}
remove {
m_gridCompiled -= value;
}
}
protected virtual void OnGridCompiled(GridCompiledEventArgs e) {
if (m_gridCompiled != null) {
m_gridCompiled(this, e);
}
m_previousEventArgs.Add(e);
}
}
There are two things you have consider for this solution. If you want to adress them, your solution will become more complex:
If GridCompiledEventArgs can be changed by an event handler (e.g. to return a status to the caller), the event args will be stored in the previous event list with those changes. Also, if an event handler keeps a reference to the event args they might even change it later. If you don't want that, you have to store a copy in m_previousEventArgs and create another copy before you raise the "historic" event.
It is best practice to allow derived classes to override the OnGridCompiled method instead of handling the event or changing its behavior. If a derived class changes OnGridCompiled to intercept the event and not raise it in certain cases, this behavior will not always apply for the "historic" event, since it is raised without OnGridCompiled (which might be just the behavior you want). If you want to change that you have to implement a solution that goes through OnGridCompiled. If this is an option for you, you can avoid this problem by making the class sealed and the OnGridCompiled method private instead of protected virtual.
Related
Imagine that Main.cs calls sub.cs which calls action.cs. action.cs raises and event which sub.cs subscribes to, however, sub.cs does not care about the event it is only main.cs that wants to know about this so sub.cs raises the event again so that main.cs can subscribe to it and discover that action.cs has raised the original event; which seems so cumbersome.
What alternatives are there to passing events on through a chain of method calls?
You can directly attach event exposed in Sub at event exposed in Action, of course event need to be exposed both in Sub and in Action:
class SubClass
{
public event EventHandler MyEvent
{
add
{
_action.MyEvent += value;
}
remove
{
_action.MyEvent -= value;
}
}
private ActionClass _action;
}
With this solution you still have to declare event twice but you do not chain method calls and you can omit event handler in SubClass.
There are alternatives, of course, but you may need to change your design and I don't have enough context to suggest anything. In general I'd start with simplest possible solution. If you need just to bubble one event this may be enough but if you need to expose many of them then you may consider to introduce a third object which exposes what you need and make accessible through SubClass from ActionClass, something like this (but please with better names):
public sealed class Notifications
{
public event EventHandler MyEvent;
internal void RaiseMyEvent(EventArgs e)
{
var myEvent = MyEvent;
if (myEvent != null)
myEvent(this, e);
}
}
class MyAction
{
public Notifications Notifications
{
get { return _notifications; }
}
// ...
}
class SubClass
{
public Notifications Notifications
{
get { return _action.Notifications; }
}
// ...
}
Note that this example is just a proof of concept.
You may want to use pub-sub with topics, for instance see https://www.rabbitmq.com/tutorials/tutorial-three-dotnet.html
(You don't need rabbitmq for that you can implement simple pub-sub yourself or take one from github/MSDN see: https://msdn.microsoft.com/en-us/library/ms752254(v=vs.110).aspx, https://github.com/upta/pubsub/blob/master/README.md)
You could use a callback instead of events.
You can add a callback function as additional parameter to the methods of method chain.
E.g. if the method is doSomething() replace it with doSomething(Action action) and Main.c calls this method with Sub.doSomething(() => ReactToTheEvent()); and Action.cs calls action(); insetad of raising the event.
While copying code for RelayCommand from Josh Smith article I copied following code
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Then after reading this answer on SO I also copied in my class following code from DelegateCommand class of Prism.
protected void NotifyCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
But his gives me an error in NotifyCanExecuteChanged method
The event 'CanExecuteChanged' can only appear on the left hand side of += or -=
This error is not coming if I remove the add and remove overload from event. Can someone please help me understand the reason behind this?
With a field-like event (which is the name for the simple form without add/remove, then when you do if(CanExecuteChanged != null) or CanExecuteChanged(this, ...), the CanExecuteChanged is referring to the backing field, which is a delegate field of type EventHandler. You can invoke a delegate field. However, that is not the case in your example, because there is no obvious thing to invoke. There certainly isn't a local field, and the forwarded event (CommandManaged.RequerySuggested) does not intrinsically expose any "invoke" capability.
Basically, for that to work you would need access to an invoke mechanism. Most commonly, I would expect that to take the form of:
CommandManager.OnRequerySuggested();
but if there is a method that invokes this event (and there doesn't need to be), it could be called anything.
(the On* is a common pattern for a "raise this event" API, doubly-so if it is polymorphic)
I think you want CommandManager.InvalidateRequerySuggested. It forces the RequerySuggested event to be raised.
It seems like your class inherits from the class where event is declared. Event can be directly raised only in the base class, not in inherited class.
If you want to raise it in inherited class, write the following method in your base and call it from inherited class:
protected void RaiseMyEvent()
{
if (MyEvent != null)
{
MuEvent(this, args)
}
}
I have an event in a loop. I am trying to prevent the same method being added to an event more than once. I've implemented the add and remove accessors.
However, I get an error stating that:
ItemsProcessed can only appear on the left hand side of += or -=
When I try to call them, even within the same class.
ItemsProcessed(this, new EventArgs()); // Produces error
public event EventHandler ItemsProcessed
{
add
{
ItemsProcessed -= value;
ItemsProcessed += value;
}
remove
{
ItemsProcessed -= value;
}
}
With an explicit event, you need to provide your own backing store - either a delegate field or something like EventHandlerList. The current code is recursive. Try:
private EventHandler itemsProcessed;
public event EventHandler ItemsProcessed
{
add
{
itemsProcessed-= value;
itemsProcessed+= value;
}
remove
{
itemsProcessed-= value;
}
}
Then (and noting I'm being a little cautious about the "about to turn null" edge-case re threading):
var snapshot = itemsProcessed;
if(snapshot != null) snapshot(this, EventArgs.Empty);
With more recent C# versions, this can be simplified:
itemsProcessed?.Invoke(this, EventArgs.Empty);
I can't tell from your post if you are trying to raise the event from a derived class or not, but one thing I've found is that you can't define an event in a base class and then raise it (directly) in a derived class, for some reason that isn't real clear to me yet.
So I define protected functions in base classes to raise events (that are defined in those base classes), like this:
// The signature for a handler of the ProgressStarted event.
// title: The title/label for a progress dialog/bar.
// total: The max progress value.
public delegate void ProgressStartedType(string title, int total);
// Raised when progress on a potentially long running process is started.
public event ProgressStartedType ProgressStarted;
// Used from derived classes to raise ProgressStarted.
protected void RaiseProgressStarted(string title, int total) {
if (ProgressStarted != null) ProgressStarted(title, total);
}
Then in the derived class, I call RaiseProgressStarted(title, total) instead of calling ProgressStarted(title, total).
It seems like kind of the long way around. Maybe someone else knows of a better way around this problem.
It seems that if you implement the EventHandler explicitly, you can't refer to the 'Property' when firing the event. You must refer to the backing store.
What error? I guess its stack overflow error, because you are calling add and remove on yourserlf (same event). Also you cannot raise event ACCESSOR.
Valid way to do this is to create backing private event, that will be added and removed to from public accessor, and you should raise this private event.
Dang, minute late.
I'm currently developing a tiny technical Framework that is independant of any applications. Business code just refers to this Framework.
According this article : http://msdn.microsoft.com/en-us/library/5z57dxz2.aspx (exemple 2), we need to provide a delegate for the custom event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code and that isn't logical for me, so what is the best way to raise a custom Event with a delegate that is only "internal" and not "public" ?
Thanks for help.
I am not sure if I get it right or not. I think that you feel like if you provide a public Delegate type for your custom event, anyone will be able to Raise that event.
Well, that is not true. Only the class that defines that custom event can raise it. If this is your issue, don't worry.
Not true. It's not allowed to invoke an event outside the class which the event belongs to. Others can only use += and -= operators to your event. Only in the class, you can invoke the event. That is a difference between an event and a normal delegate. That is:
public Data
{
public event EventHandler OnSave
public EventHandler OnLoad;
private void Load()
{
if (OnLoad!=null) OnLoad();
//other operations
}
private void Save()
{
if (OnSave!=null) OnSave();
//other operations
}
}
And outside the class:
Data data = new Data();
data.OnLoad += (s,e) => {};
data.OnSave += (s,e) => {};
data.OnLoad = (s,e)=>{};
//data.OnSave = (s,e)=>{}; //invalid
data.OnLoad();
//data.OnSave(); //invalid
The delegate is just a type declaration describing the "signature" of your event. This has to be public. To actually invoke your event you often implement a method named OnEvent (where you substitute Event with Click or Closed or whatever describes your event). This method should be private (or protected) in your class.
You cannot declare an event using a delegate that is less "visible" than the event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code
That isn't true. You declare an event as follows:
public event FooEventHandler Foo;
The only thing that external code can do with the event is subscribe to it (+=), or unsubscribe from it (-=). It can't access the actual delegate, which is generated by the compiler as a private field. In other words, this code would be invalid :
SomeClass x = new SomeClass();
x.Foo(x, new FooEventArgs()); // compilation error here
Don't forget that an event is actually a pair of methods (add and remove). The compiler rewrites the event declaration to something along those lines:
private FooEventHandler _foo;
public event FooEventHandler Foo
{
add { _foo += value; }
remove { _foo -= value; }
}
(the generated code is actually a bit more complex, with some locking to ensure thread safety)
As you can see, the _foo field is private, so client code can't access it. Only the event's add and remove accessors are accessible.
One way of doing it:
Instead of public event, create a method that will manually subscribe your desired delegates, and store them in `private List _delegates' field.
Then, from the 'inside', call each of them when you desire.
public class Framework
{
public delegate void Method();
public void AttachEvent(Method M)
{
_methods.Add(M);
}
private List<Method> _methods;
private FireMethods()
{
_methods.Foreach(x=>x.Invoke());
}
}
Or, you can embrace 'by design' feature of the events that they aren't publicly Invoke()-able.
:)
I have the following code where I am handling an event twice. However I always want to ensure that mynewclass always handles the event first and then the local event handler code fires. I understand the MyClass event should fire first as that is the one created first but because the thread and enqueuing is taking place, I think its taking too long and its doing something in myhandleeventlocal before I want it to do that. Any way I can wait for it to happen?
public MyMainClass
{
private MyMethod()
{
MyClass mynewclass = new MyClass();
mynewclass.myObject += MyHandler(myhandleventlocal);
mynewclass.loadedevent += EventHandler(loadedevent)
}
private void myhandleventlocal()
{
//do stuff
}
private void loadedevent()
{
//do some stuff
}
}
public MyClass
{
public MyObject myObject;
public event loadedevent;
public MyClass()
{
myObject = new MyObject();
myObject += MyHandler(myhandlevent);
}
private void myhandlevent(long value, string detail)
{
//Start a thread
//Enqueue value and detail
//On seperate thread dequeue value and process it
//Raise loadedevent event
}
}
UPDATE: I have updated my question and code to demonstrate the problem.
By default the event handlers are called in the order you add them, so if you always add the handlers in the order you want them to fire then it should work.
From Jon Skeet's article on events and delegates:
[...] extra delegates are both added to and removed from the end of the list [...]
Note: You can override the default behaviour of events by changing the add and remove operations on your event to specify some other behaviour. You can then keep your event handlers in a list that you manage yourself and handle the firing order based on whatever rules you like.
If you can't guarantee the order the event handlers will be added, just add the one for mynewclass and then in that code call the other code.
Since event handlers are called in the order you add them, based on the code I see in your question, you can't make mynewclass's handler be called first. The event handler that MyClass creates is always added first.
One solution would be to control priority for the event handlers. Instead of using the builtin event handler +=/-= operators, you would instead have methods for adding and removing events where you could specify ordering explicitly. That way, if a class knows it needs to handle the event first, it could ask for such. Be careful, though, because you could easily run into a situation where multiple classes are each insisting that they handle the event first.
Here is some quick and dirty code to get you started:
class MyClass {
private LinkedList<MyEventHandler> eventHandlers;
public enum Ordering { First, Last, ... };
public void AddHandler(MyEventHandler handler, Ordering order) {
switch(order) {
case Ordering.First:
eventHandlers.AddFirst(handler);
break;
// fill in other cases here...
}
}
public void RaiseEvent() {
// call handlers in order
foreach(MyEventHandler handler in eventHandlers)
eventHandler();
}
}
Referring to siride solution, you can also implement your handlers and decide the position that way. Like inverting the order (always add at the begin) or add some logic.