I am building a list based on items/objets found. When these objects are found I want them to subscribe to an event using prism/IEventAggregator. They will all be of the same type of object but I do not want them to subscribe to the same event. Even though they are of the same type I want them to have unique subscriptions. So when I publish an event I can direct it to each unique/specific object. Does anyone have any thoughts? Is this feasible at all?
Having unique event types for each object you create would be very cumbersome and probably scale very poorly. However you can have a property on your event object that would be the specific instance you want to direct the event to.
The subscribers (your objects) would all receive the event notification but would check the 'recipient property' to see if the event is for them. If not they would ignore it, otherwise act.
Pseudo code:
public class MyEvent {
public MyClass Recipient { get; set; }
}
public class MyClass {
public MyClass(IEventAggregator evt) {
evt.Register<MyEvent>(TakeAction);
}
private void TakeAction(MyEvent event) {
if (!event.Recipient == this) {
return;
}
// Perform the action
}
}
Use a single event and pass a payload that contains information about the event. Then the subscribers can filter the event based on the payload you sent with the event.
https://github.com/PrismLibrary/Prism/blob/master/Documentation/WPF/70-CommunicatingBetweenLooselyCoupledComponents.md#subscription-filtering
Related
I am developing an WPF application and tried to design something event driven using Prism's Event Aggregator.
Currently I am trying to implement something like event queue for Prism events.
To do that I want to subscribe related events and pass them to same method but Event Aggregator wants those methods to have same signature with the event.
Example events:
public class TestEvent1 : PubSubEvent<Class1>
{
}
public class TestEvent2 : PubSubEvent<Class2>
{
}
public class TestEvent3 : PubSubEvent<List<Class3>>
{
}
public class TestEvent3 : PubSubEvent<string>
{
}
Subscriptions:
_eventAggregator.GetEvent<TestEvent1>().Subscribe(OnTestEvent1, true);
_eventAggregator.GetEvent<TestEvent2>().Subscribe(OnTestEvent2, true);
_eventAggregator.GetEvent<TestEvent3>().Subscribe(OnTestEvent3, true);
Example callback method:
private void OnTestEvent1(Class1 object1)
{
// do something
}
Since I only receive data when event published, I tried something like this to use as event payload type, but it doesn't look right:
// Payload
public interface IMessage
{
public object Data { get; set; }
public Type Datatype { get; set; }
public PubSubEvent EventType { get; set; }
}
// Events
public class TestEvent1 : PubSubEvent<IMessage>
{
}
public class TestEvent2 : PubSubEvent<IMessage>
{
}
public class TestEvent3 : PubSubEvent<IMessage>
{
}
// Subscriptions
_eventAggregator.GetEvent<TestEvent1>().Subscribe(EventHandler, true);
_eventAggregator.GetEvent<TestEvent2>().Subscribe(EventHandler, true);
_eventAggregator.GetEvent<TestEvent3>().Subscribe(EventHandler, true);
// Callback
private void EventHandler(IMessage payload)
{
// do something
}
Is this viable and how can I improve or change my "generic" payload?
Extra information:
In my scenario I have multiple UDP servers that periodically receives new data, deserialize it to objects and publish events.
Related "managers" subscribed to those events and get triggered when new data received.
My goal is try to implement an event queue like system in my event receiving classes so it will be easier to deal with multithreading issues.
Here are some diagrams that may help me explain myself better:
My architecture:
My "event queue"
This is more of a code-review question, isn't it?
I would very much prefer strong typing, though, and I wouldn't want to build a queue around the event aggregator, that will always be tedious because you never know when new event types show up. Instead, I'd build my own event aggregator with queueing built in (starting from the existing one).
Also, I'd look into dataflow, for example, because the basic working mode of the event aggregator (fire and forget, one sender, multiple or no receivers) doesn't seem to work well with queueing. If you queue at the sender-side, to you wait for one receiver or all? Do you queue when there are no receivers or do you discard then? If you queue at the receiver-side, why bother at all? The receiver can implement the queue on its own.
When you publish an event with a reference to an object, consider these potential problems:
Any listener will be dependent on the type/assembly of the event parameter.
The referenced object will be held for some time. For how long? You don't know.
The referenced object could have been altered/disposed.
Listeners might handle the referenced object on a different thread.
After dealing with PubSubEvent<T> for several years now, I believe there is only one suitable pattern that successfully handles all cases. Publish your event with a unique identifier, e.g. a Guid.
public class MyItemAddedEvent : PubSubEvent<Guid> {}
Then inject a provider wherever you listen for this event:
public class SomeListener
{
private readonly IMyItemProvider myItemProvider;
[ImportingConstructor]
public SomeListener(IEventAggregator eventAggregator,
IMyItemProvider myItemProvider)
{
this.myItemProvider = myItemProvider;
eventAggregator.GetEvent<MyItemAddedEvent>().Subscribe(OnMyItemAdded, true);
}
private void OnMyItemAdded(Guid id)
{
var myItem = myItemProvider.Get(id);
// Do stuff
}
}
Now it is the responsibility of the provider class to deliver a valid and up-to-date object given a unique id.
Lets say I have a class which has a static event in it. This event is subscribe by three or more different classes. [Lets say 4]
Now when something noteworthy happen the event in class got raised.
As 4 different classes subscribe to the event, the corresponding handler in them execute their respective code.
Now if i don't want the handler code of my class no 3 & 4 to execute. What should i need to do?
Please help.
Thanks in Advance!
Under what conditions do you want your handlers to fire?
You can have the following pattern which will provide a mechanism to prevent the handlers firing based on a unique identifier.
In this example, I create some simple event args which the event will use to tell the handler a unique identifier (Guid):
public class MyEventArgs : EventArgs { public Guid Identifier { get; set; } }
Then, create a simple class to raise the event:
public class EventGenerator
{
public EventHandler<MyEventArgs> TheEvent;
public void RaiseEvent(Guid identifier)
{
if (TheEvent != null)
TheEvent(this, new MyEventArgs(){Identifier = identifier});
}
}
Finally, you can have a class (any number of these) which will all subscribe to the event but will only run it when the event args supplies a different identifier:
public class TheClass
{
private readonly Guid _identifier;
private EventGenerator _eventGenerator;
// The constructor is given the event generator class instance
public TheClass(EventGenerator evGen)
{
// create a unique identifier for the class
_identifier = Guid.NewGuid();
// subscribe to the event
_eventGenerator = evGen;
_eventGenerator.TheEvent += TheEvent;
}
private void TheEvent(object sender, MyEventArgs e)
{
// when the event fires, check the Guid and if it isn't a match, don't continue ...
if (e.Identifier == _identifier) return;
// rest of the handler goes here ...
}
}
This is just an example, however - you may need something different which might be achievable by simply unsubscribing to the event at choice times. It depends on the answer to my initial question.
Okay, I've been searching on the site and Google and can't quite get my head around where things need to be in terms of delegates and eventhandlers and the like so hopefully someone here can help/explain what I need to do.
So, I am writing a simple database application (using SQLite). There is a mainform that is the MDI parent (that's basically a big window with menus at the top). The menus launch other windows that allow view, edit and insert into various tables of the database.
One of those windows is a LOG window which shows my log table.
At the moment, if a user changes something in the window showing the data in TABLE. The operation also writes into the log table. If the Log window is open, however, the log view doesn't update.
So, I've figured out I probably need to 'fire' an event from my TABLE UPDATE code that my LOG window 'subscribes' to (so it can update the DataGridView).
What I can't figure out is where the different 'bits' of the event go.
Should the MdiParent have the public delegate void EventHandler();? If not where?
which class gets the public static event EventHandler logGoneStale;?
The only bit I'm reasonably sure about is that the Window that displays the log (which has a method called public void UpdateLogDataGridView() - which calls the database object/methods to (re-)populate the datagridview) needs to have:
something like logGoneStale += new EventHandler(UpdateLogDataGridView); in it. Is that at least right?
Totally befuddled - it seems none of the event examples/tutorials on MSDN are trying to do what I want to achieve.
You need to define an event in the class that is sending the event, and append an event handler in the class that should receive the event. To make things slightly easier, starting with C# 3.5 you can forget about the delegate keyword altogether and use a lamba expression as event handler. Also note that it in most cases it makes no sense to make an event static, since usually events are fired by an instance, not by a class.
Example:
class SendsEvent
{
public event EventHandler MyEvent;
public void FireEvent()
{
if(MyEvent != null) // MyEvent is null if no handlers have been attached
{
MyEvent(this, new EventArgs()); // event fired here
}
}
}
class ReceivesEvent
{
private SendsEvent eventSource;
public ReceivesEvent(SendsEvent eventSource)
{
this.eventSource = eventSource;
// Attach event handler - can be a lambda expression
// or method with signature
// "void HandleEvent(object sender, EventArgs e)"
this.eventSource.MyEvent += (sender, args) =>
{
// do something when event was fired
Console.Out.WriteLine("Hello. Event was fired.");
};
}
}
class Program
{
public static void Main()
{
var eventSource = new SendsEvent();
var eventReceiver = new ReceivesEvent(eventSource);
eventSource.FireEvent();
}
}
I hope this helps you.
Working with events requires you to have both an event publisher and an event subscriber.
#chris' answer is correct.
Besides, you need to raise the event on the closest point where the action for which you want to be notified takes place.
For example, implementing the INotifyPropertyChanged interface.
public class Customer : INotifyPropertyChanged {
public string Name { get; set; }
public string Address {
get { return address; }
set {
address = value;
if (thereArePropertyChangedEventSubcribers())
raisePropertyChangedEventFor("Address");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void raisePropertyChangedEventFor(string propertyName) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private bool thereArePropertyChangedEventSubcribers() {
return PropertyChanged != null;
}
private string address;
}
So here, the Customer class allows for the publishment of its change of address. So, whenever anyone is interested to be notified when the address has changed, it subscribes to the event like so:
Customer.PropertyChanged += new PropertyChangedEventHandler(customerPropertyChanged);
Or else like so:
Customer.PropertyChanged += customerPropertyChanged;
You might even have noticed that the closest point where the address has changed in directly after it has actually changed. The only requirement is that the method used as the event handler has the same signature as the event itself. If you take a look at the PropertyChangedEventHandler Delegate, one may notice that it signature awaits an object as the first parameter, that is, the object that fired the event, and a PropertyChangedEventArgs instance to notify about the property that has changed.
To come back to your example, you wish to be noticed whenever a log has been inserted into the underlying database so that a refresh of your Log window may occur. There are two questions that need to be answered whenever you want to use events.
What shall my publisher be?
What shall my subscriber be?
What shall my publisher be?
Should the MdiParent have the public delegate void EventHandler();?
Short answer: No!
If not where?
The event declaration best fits the publisher. Should you have a class responsible for logging, then this is where the public delegate void EventHandler(); should reside, as it is it that is responsible to raise the event whenever there are subscribers.
Whenever there is a successful Log inserted, it shall notify whatever subscriber interested to know about the new Log Entry.
public class Log {
public void UpdateLog(string description) {
// insert the new Log line into your database.
if (thereIsAtLeastOneNewLogEntryAddedSubscriber())
raiseTheNewLogEntryAddedEvent();
}
public event EventHandler NewLogEntryAdded;
private raiseTheNewLogEntryAddedEvent() {
NewLogEntryAdded(this, EventArgs.Empty);
}
private bool thereIsAtLeastOneNewLogEntryAddedSubscriber() {
return NewLogEntryAdded != null;
}
}
What shall my subscriber be?
This question can be answered through another question:
What do you need to do when the event fires?
In your case, you wish to update a Log window whenever it is opened.
The only bit I'm reasonably sure about is that the Window that displays the log (which has a method called public void UpdateLogDataGridView() - which calls the database object/methods to (re-)populate the datagridview) needs to have:
something like logGoneStale += new EventHandler(UpdateLogDataGridView); in it. Is that at least right?
Yes, you're right! =D
You actually subscribe to the event per this line. So, it tells the application that the window that displays the log is interested to know about log changes in your database.
public class WindowThatDisplaysTheLog : Form {
public WindowThatDisplaysTheLog() {
InitializeComponent();
log = new Log();
log.NewLogEntryAdded += UpdateLogDataGridView;
}
private void UpdateLogDataGridView(object sender, EventArgs e) {
// Reload your Log entries from the underlying database.
// You now shall see the LogDataGridView updating itself
// whenever a new log entry is inserted.
}
private Log log;
}
The following is an example from MSN website. This is a good example, I just do not understand what this line:
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
is doing?
Is not RaiseCustomEvent an event based on the definition at the top of the program?
Why is an Event is equated to an EventHandler? These are two different types.
Where is RaiseCustomEvent being initialized? If it is not initialized how can we copy it or why do we want to copy something uninitialized to something else?
What is the handler variable there for? Is that an event or an event handler?
I'm very confused and am trying hard to get this event/event handler/delegate issue understood.
Here is the sample code from MSN
namespace DotNetEvents
{
using System;
using System.Collections.Generic;
// Define a class to hold custom event info
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string s)
{
message = s;
}
private string message;
public string Message
{
get { return message; }
set { message = value; }
}
}
// Class that publishes an event
class Publisher
{
// Declare the event using EventHandler<T>
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
public void DoSomething()
{
// Write some code that does something useful here
// then raise the event. You can also raise an event
// before you execute a block of code.
OnRaiseCustomEvent(new CustomEventArgs("Did something"));
}
// Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation behavior
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
// Event will be null if there are no subscribers
if (handler != null)
{
// Format the string to send inside the CustomEventArgs parameter
e.Message += String.Format(" at {0}", DateTime.Now.ToString());
// Use the () operator to raise the event.
handler(this, e);
}
}
}
//Class that subscribes to an event
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
// Subscribe to the event using C# 2.0 syntax
pub.RaiseCustomEvent += HandleCustomEvent;
}
// Define what actions to take when the event is raised.
void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
}
}
class Program
{
static void Main(string[] args)
{
Publisher pub = new Publisher();
Subscriber sub1 = new Subscriber("sub1", pub);
Subscriber sub2 = new Subscriber("sub2", pub);
// Call the method that raises the event.
pub.DoSomething();
// Keep the console window open
Console.WriteLine("Press Enter to close this window.");
Console.ReadLine();
}
}
}
An event is to a delegate type what a property is to any other type.
If you have a property such as this:
public string Name {get;set;}
Obviously you can do something like this:
string name = Name;
The property has an underlying string value that is accessed/modified by the property.
Similarly, an event has a delegate under the hood, and that delegate is of the type defined in the event declaration. That it's an event simply defines how handlers of that event are added/removed from that underlying delegate.
From within the declaring type (that's a key point; you can't do this externally) you can access that underlying delegate in order to invoke it. That's the reason for doing the code that you see; they're accessing the underlying delegate so that they can verify that it has some handlers within it, and if so, it invokes them.
So, to answer the questions explicitly:
Is not RaiseCustomEvent an event based on the definition at the top of the program?
RaiseCustomEvent is the type of the underlying delegate that the event wraps.
Why is an Event is equated to an EventHandler? These are two different types.
It's not strictly equality. It's pulling out the underlying delegate from within the event.
Where is RaiseCustomEvent being initialized? If it is not initialized how can we copy it or why do we want to copy something uninitialized to something else?
In this case it's using the automatic add/remove implementations that the framework will provide, rather than manually defining them. The automatically defined add handler will initialize the underlying delegate if it is currently null. If the event declaration would
define a custom add handler, it would need to handle that case.
What is the handler variable there for? Is that an event or an event handler?
It is a single delegate that represents the combination of all of the event handlers. Within it's definition will be an invocation list of all of the individual methods that make up that delegate. So it's not a single event handler, it's the collection of all of them. Since it's pulled out of the event it no longer strictly represents that event; it's a copy of what was in the event at some point in the past. You cannot change the event (i.e. add a new handler) using the delegate that you've pulled out of it.
I'll try the four questions you pose:
1) Is not RaiseCustomEvent an event based on the definition at the top of the program ?
The CustomEventArgs class holds some data (arguments) for the event we want to declare. It is used as type parameter in the type EventHandler<CustomEventArgs>. This last type is a delegate type, which means it represents one or more methods of the same signature and return type. (Zero methods will be a null reference as the value of the delegate.)
The type of the event RaiseCustomEvent is that delegate type, EventHandler<CustomEventArgs>.
2) Why an Event is equated to an EventHandler? These are two diffeent types
An event consists on a pair of special methods, accessors, one add accessor and one remove accessor. Both have one parameter of the same type which is called the type of the event. That type must be a delegate type. In this case that type is EventHandler<CustomEventArgs>.
In this example the event is a so-called field-like event. It generates a backing field of the same type, EventHandler<CustomEventArgs>, the delegate type. That field has the same name as the event itself!
When they do:
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
they copy the current value of that backing field into the local variable handler. The comment describes why. They want to check for null before they invoke the delegate.
3) Where is RaiseCustomEvent have been initialized? If not initialized how can we copy or why do we want to copy something uninitialized to soemthig else.
It usually gets initialized when people use the add accessor of the event. This takes place through the special C# syntax +=. This is called subscribing to the event. See the Subscriber class.
Actually pub.RaiseCustomEvent += HandleCustomEvent; translates to pub.add_RaiseCustomEvent(HandleCustomEvent);, so it is a call to the add accessor. The add accessor is generated by the compiler (in a field-like event), and it initializes the backing field.
4)I do not know what is handler variable? is that an event or an event handler ?
It is a delegate. It is not an event. It is a copy of the backing field of the field-like event at one moment in time.
I have been looking in to subscribing to an event using a weak event pattern. With the .NET 4.5 framework, we have a slick looking WeakEventManager class. Weakly subscribing to an event is as simple as
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
I'm not a big fan of 'stringly-typed' code however. I have been trying to find a way around using the string name of the event to subscribe to. The only way I have found to obtain the name of the event is using a lambda expression in the class that defines the event. In my scenario, I own the class defining the event so I can change it however I like. I have been trying to find a clean way to subscribe and unsubscribe to my event and here is what I disliked the least.
public event EventHandler<EventArgs> LoggingOn;
public event EventHandler<EventArgs> LoggingOn_Weak
{
add
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.AddHandler(this, eventName, value);
}
remove
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.RemoveHandler(this, eventName, value);
}
}
// In a base class view model in my scenario
private string GetEventName<T>(System.Linq.Expressions.Expression<Func<T>> expression)
{
return (expression.Body as System.Linq.Expressions.MemberExpression).Member.Name;
}
protected void OnLoggingOn(object sender, EventArgs e)
{
var handler = this.LoggingOn;
if (handler != null)
{
handler(sender, e);
}
}
Using custom event accessors I was able to avoid clunky (in my opinion) methods like LoggingOn_Subscribe(EventHandler) or adding name properties for each event. Unfortunately it is not so intuitive in that people subscribing to the event are doing so in the classic manner but have no idea other than the "_Weak" part of the name that indicates it is being subscribed to weakly.
As for my questions..
1) I have never used weak events or custom event accessors before. The code above appears to work, however, I would just like to make sure there is nothing technically wrong with it. Is there anything I'm doing here to shoot myself in the foot?
2) From a design perspective, is this a terrible idea? Are there any major design concerns I should consider? Is there better alternative? Should i just suck it up and subscribe from my subscriber using a stringly-typed event name?
Thoughts?
With .NET 4.6 you can now use the nameof() expression:
WeakEventManager<IMyGrid, MyEventArgs>.AddHandler(myGrid, nameof(IMyGrid.MouseDown), OnMouseDown);
What you could do is use the built-in System.ComponentModel.EventHandlerList. This class is a container for all of your object's event handler delegates. The primary benefit is that no storage is allocated on your object for each event unless there is actually someone subscribed to an event.
The secondary benefit is that in order to use it, you must provide a key for your event.
class MyObject
{
protected EventHandlerList Events = new EventHandlerList();
public static Event1Key = new object();
public event Event1
{
add { Events.AddHandler(Event1Key, value); }
remove { Events.RemoveHandler(Event1Key, value); }
}
}
Now you could create a variation of WeakEventManager that accepted keys rather than string names. So the consumer could say
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, EventSource.Event1Key, source_SomeEvent);