Composite WPF GUI Interface with Event - c#

Using Prism4 and MEF I have created a shell and two modules(M1,M2).
I do want to open a serial port in M1 and by using an interface, with an datareceived event from the opened serial port, I want that M2 gets notified and receives the data from the serial port.
To be more specific, I use the MVVM pattern, therefore I would like to open the serial port within the M1's ViewModel, and inform the M2's ViewModel when data are received.
Unfortunately, I'm quite unsure how to use the interface within the PRISM workflow. I'm thankful for every help. I really need an example for this issue.
I added the code just to make my question clear.
Thanks in advance.
Module A.cs
[ModuleExport(typeof(ModuleA), InitializationMode = InitializationMode.OnDemand)]
public class ModuleA : IModule
{
[ImportingConstructor]
public ModuleB(IEventAggregator eventAggregator_)
{
EventAggregator = eventAggregator_;
}
[Import]
public IRegionManager RegionManager { get; set; }
public void Initialize()
{
this.RegionManager.RegisterViewWithRegion("RegionA", typeof(ZeroGrid1));
}
}
Module B.cs
[ModuleExport(typeof(ModuleB), InitializationMode = InitializationMode.OnDemand)]
public class ModuleB : IModule
{
[ImportingConstructor]
public ModuleB(IEventAggregator eventAggregator_)
{
EventAggregator = eventAggregator_;
}
[Import]
public IRegionManager RegionManager { get; set; }
public void Initialize()
{
this.RegionManager.RegisterViewWithRegion("RegionB", typeof(ZeroGrid2));
}
}
ZeroGrid1.xaml.cs (similar to ZeroGrid.xaml.cs)
[Export]
public partial class ZeroGrid1
{
[ImportingConstructor]
public ZeroGrid1(ZeroGridViewModel1 viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
}
ModuleAViewModel.cs
[Export]
public class ModuleAViewModel: NotificationObject, IDataReciever
{
// OPEN SERIALPORT
//SEND SOMETHING SERIALPORT
//Maybe I also wanna get notification for datareceived here
}
ModuleBViewModel.cs
[Export]
public class ModuleBViewModel: NotificationObject, IDataReciever
{
//GET NOTIFIED WHEN DATARECEIVED FROM SERIALPORT AND RECEIVED DATA
}
IDataReceiver.cs
interface IDataReciever<TData>
{
event Action<TData> DataRecieved;
//some other methods, such as, for example:
//void Open();
//void Close();
}

Define a composite presentation event, by exporting a classed which derives from Prism's 'CompositePresentationEvent' where T is the type of the event's 'payload'.
[Export]
public class DataReceivedEvent : CompositePresentationEvent<object>
{}
Make your two ViewModels import that event:
[Export]
public class ModuleAViewModel: NotificationObject, IDataReciever
{
private DataReceivedEvent _dataReceivedEvent;
[ImportingConstructor]
public ModuleAViewModel(DataReceivedEvent dataReceivedEvent)
{
_dataReceivedEvent = dataReceivedEvent;
_dataReceivedEvent.Subscribe(OnDataReceived);
}
private void OnDataReceived(object payload)
{
// Handle received data here
}
// This method gets called somewhere withing this class
private void RaiseDataReceived(object payload)
{
_dataReceivedEvent.Publish(payload);
}
}
Do the same in ViewModelB and both will get notified if the event is raised anywhere in the application.

There is a QuickStart solution available in MSDN which describes how publishing the event from one module and subscribing to it from the other is performed. You can find the Event Aggregation QuickStart in the following Prism Guide Appendix:
Appendix G: QuickStarts - Event Aggregation QuickStart
For more information of how EventAggregator works you can refer to the following Prism Guide chapter:
Communicating Between Loosely Coupled Components
Regards.

Related

C# : raise event in class bases on event in other class

In my application I have a interface IEncoder that is having event EncoderCaller.
public interface IEncoder
{
event EncoderCaller EncoderCalled;
}
public delegate void EncoderCaller(object Source, EventArgs args);
public class Video
{
public string Title { get; set; }
}
public class VideoEventArgs : EventArgs
{
public Video xVideo { get; set; }
}
public class DetectionAction : IEncoder
{
public event EncoderCaller EncoderCalled;
public void Encode(Video video)
{
//some logic to encode video
OnVideoEncoded();
}
protected virtual void OnVideoEncoded()
{
if (EncoderCalled != null)
EncoderCalled(this, EventArgs.Empty);
}
}
public class Client1: IEncoder
{
}
I need some mechanism by which I should be able to share a contract, if that is implemented by any client then that event will trigger event in my class DetectionAction .
Can someone tell me, Am I doing right thing.
How it can be done?
If you have two classes in the same process, you could consider explicitly chain events like this:
public class Client1 : IEncoder
{
public event EncoderCaller EncoderCalled;
public Client1(IEncoder anotherEncoder)
{
// Listen to event raised on another instance and raise event on this instance.
anotherEncoder.EncoderCalled += OnAnotherEncoderCalled;
}
private void OnAnotherEncoderCalled(object source, EventArgs args)
{
if (EncoderCalled != null)
EncoderCalled(this, EventArgs.Empty);
}
}
In this case, for example, anotherEncoder is DetectionAction.
However, if you are seeking solution for sharing events between two different applications running in different processes, you might be looking at inter-process communication, like this post:
Listen for events in another application
And the above example code still works, but the IEncoder in this case is an implementation with IPC support, for example a message queue listener which raises the event on message received.

How to pass Events from Model to ViewModel in WPF/C#?

In my class I registered an Event from an external DLL, that will be raised when there are changes on variables from the external code.
public class Model
{
....
public void Connect
{
....
client.OnNotification += (s, e) =>
{
this.OnNotification(s,e);
}
}
}
And I have a ViewModel in which I want get notified when this event is raised in class Model.
public class ViewModel
{
...
// call method when Event in class Model is raised
public void DoSomething()
{
}
}
Any Ideas for a clean and easy way to do that?
Thank you.
Solution 1:
Pass in the client to the viewmodel's constructor and let the viewmodel subscribe to OnNotification() itself (pass in an interface if available)
Solution 2:
Make also the model implement INotifyPropertyChanged if you're using MVVM; pass in the interface into the viewmodel's constructor and subscribe to PropertyChanged.
If you're not using MVVM, you can use the same methodology by adding a custom ClientNotification event to the model, pass in the entire model into the viewmodels constructor, and subscribe to the event.
Solution 3:
Use a messaging system (aka message bus) such as Prism's Event Aggregator class or MVVM Light's Messenger class, or write your own.
EDIT: Here's an example using MVVM Light: (note: coding from memory, not tested)
Add a using reference to GalaSoft.MvvmLight.Messaging;
Create a small message class containing the properties you need. You can inherit from MVVM Light's MessageBase class if you want but its not necessary.
public class ClientNotificationMessage : MessageBase
{
public string SomeProperty { get; set;}
public int AnotherProperty { get; set;}
}
In you model's event handler, you send a message by:
client.OnNotification += (s, e) =>
{
var msg = new ClientNotificationMessage() { ... };
Messenger.Default.Send<ClientNotificationMessage>(msg);
}
In the viewmodel constructor, register to receive messages by:
Messenger.Default.Register<ClientNotificationMessage>(this, msg =>
{
// handle incoming ClientNotificationMessage
// if (msg.SomeProperty != ) ...
});
I'm sure there are other additional solutions that other ppl can add.
The solution is basic OOP design and it is not related to MVVM.
In C# you just don't pass events. You subscribe to events. Whenever something interesting happens in Model, fire en event. You can than subscribe to that event in ViewModel for example.
public class Model
{
public event EventHandler SomethingHappened; // e.g. you notification
}
public class ViewModel
{
public ViewModel(Model model)
{
model.SomethingHappend += SomethingHappend;
}
void Model_SomethingHappend(object sender, EventArgs e)
{
DoSomething();
}
void CleanUp()
{
/*
In order to prevent memoryleak:
If you subscribe to event of an object you have not created in this class
(Model.SomethingHappend in this case), you should also unsubscribe.
Otherwise model instance will keep reference to ViewModel instance.
*/
model.SomethingHappend -= SomethingHappend;
}
}
In your case, the event could be named NotificationRecieved instead of SomethingHappend
You should subscribe to the event directly in the object you want to "react" to the event.
The viewModel in this case. Define an EventHandler there and subscribe

Mvvm light messenger, communication between viewmodels

In my ViewmodelA I want to open a new window in ViewModelB, so i used the messenger class, but the probleme is that i need to reference ViewModelB inside the ViewModelA, so that ViewModelB can listen to the messages.
here is my implementation
in ViewModelA:
private void btnAddExecute()
{
// I need to instanciate ViewModelB otherwise it wont work/listen
ViewModelB vb= new ViewModelB();
Messenger.Default.Send(new NotificationMessage("ShowWindow"));
}
in ViewModelB i listen to the broadcasted messages in it's constructor.
is there anyway to decouple ViewmodelA from ViewmodelB ?
I don't exactly see where the coupling is occurring if you are using the messenger properly. There is no need to reference ViewModelB
Edit
Here is a way to do it without reference to an instance ViewModelB. It uses a singleton to register for messages and create ViewModelBs when it receives the notification message. I haven't tested this, it is just an idea. Make sure ViewModelBCreator is used at some point so that the static constructor is called.
public class ViewModelBCreator()
{
private static ViewModelBCreator instance;
static ViewModelBCreator() { instance = new ViewModelBCreator(); }
private ViewModelBCreator()
{
Messaging.Messenger.Default.Register<NotificationMessage>(this, true, NotificationMessageReceived);
}
private static void NotificationMessageReceived(NotificationMessage notification)
{
var vm = ViewModelB();
//Do stuff with the new ViewModelB
}
}
public class ViewModelB
{
public ViewModelB()
{
//etc . . .
}
}
public class ViewModelA
{
public void OpenTheWindow()
{
Messenger.Default.Send(new NotificationMessage("ShowWindow"));
}
}

caliburn.micro eventAggregator: Handling message in Screen Collection

I have following code in my caliburn micro application.
I'm using eventAggregator to send messages from one view model to another.
Consider following code:
public class ShellViewModel : Conductor<IWorkspace>.Collection.OneActive, IShell
{
...
}
public class ViewModelA
{
...
}
public class ViewModelB
{
ViewModelA messageSender{get;set};
...
}
Requirment:
I need to send message from ViewModelA to ViewModelB.
Problem:
There are multiple ViewModel instance are created and added to screen collection. I need to send the message only to a particular ViewModelB instance.
All other viewmodel instance should ignore the message.
Note:
In above code example I'm sending message from viewmodel object messageSender to enclosing class. So the message should be sent to the parent object's instance only.
Question
Is there any build in functionality available in caliburn micro
framework to handle this situation?
If not is there any technique to uniquely identify the viewmodel
instance to process the message?
I don't think Caliburn EventAggregator has this built in. PRISM EventAggregator allows a filter func to be passed in and MVVMLight Messenger uses a token object.
I guess in Caliburn you would need to include something in the message to allow the subscribers to decide whether they should process the message or not.
IHandle<T> is used on the class that needs to "Act" on the message, you publish using IEventAggregator, usually as an injection. remember to Subscribe and Unsubscribe according to Activate and Deactivate when it either goes inactive or gets closed. Only the class that is inheriting the IHandle<T> with its "unique" signature will respond to the broadcast. Now of course if you multiple IHandle<string>'s for example then all VM's will try to process the message that was sent, if they were wired up to do so.
//Handle<T> for any built-in type or something you create like MessageEvent etc.
public class ViewModelB : Screen, IHandle<string>
{
public ViewModelB(IEventAggregator events){
_event = events;
_events.Subscribe(this);
}
private void Handle(string t){
MessageBox.Show(t);
}
}
public class ViewModelA : Screen
{
private readonly IEventAggregator _event;
public ViewModelA(IEventAggregator events)
{
_event = events;
_event.Subscribe(this);
}
public void SomethingWasClicked()
{
_event.PublishOnUIThread("Hello, World!");
}
}
To elaborate on what mvermef said, you need to implement the IHandle<T> interface in the view models that will handle the specific messages. Since he chose a string for the message type, you'd have to parse the message in ViewModelB to handle it if other view models implemented the IHandle<string> interface. Going back to your example and making it more specific for ViewModelB, you could inherit from a base class that handles string-based messages.
public class StringMessageEventBase
{
private string _Message;
public StringMessageEventBase()
: this(null) { }
public StringMessageEventBase(string message)
{
_Message = message;
}
public string Message
{
get { return _Profile; }
set { _Profile = value; }
}
}
public class ViewModelBMessageEvent : StringMessageEventBase
{
public ViewModelBMessageEvent(string message)
: base(profile) {}
}
Now if you implement IHandle<ViewModelBMessageEvent> in your view model, ViewModelB, then only instances of ViewModelB will handle the message.
public class ViewModelB : IHandle<ViewModelBMessageEvent>
{
private readonly IEventAggregator _Aggregator;
public ViewModelB(IEventAggregator aggregator)
{
if (aggregator == null)
throw new ArgumentNullException("aggregator");
_Aggregator = aggregator;
_Aggregator.Subscribe(this);
}
public void Handle(ViewModelBMessageEvent message)
{
// do some thing with your message here
var msg = message.Message;
}
}

Observer pattern in C# / how to make a Form an observer

I fail to find an answer so far, probably just lacking the appropriate keywords to search for.
I want to implement an Observer Pattern in C#, so any Observer object can subscribe to a Subject object and then receives all its notifications. Then it decides based on the Notification type whether it's important or not.
public class Subject
{
private List<Observer> observers;
public void AttachObserver(Observer Observer)
{
this.observers.Add(Observer);
}
public void DetachObserver(Observer Observer)
{
this.observers.Remove(Observer);
}
public void NotifyObservers(CommonNotification Notification) // who we are, what kind of notification, bla bla
{
foreach(Observer Observer in observers)
{
Observer.OnNotify(Notification);
}
}
}
public class Observer
{
public abstract void OnNotify(CommonNotification Notification);
}
So any object wanting to subscribe to a Subject needs to be an inheritance of the Observer class. But how to do that? My MainForm is based on Form. If I replace the Observer class with a general object it won't implement an OnNotify() event.
What's the point I am missing here? I know I should properly implement it using Event handlers but in order to learn how basic design patterns work I rather implement things myself first.
Short Answer: look at first answer to this question: Super-simple example of C# observer/observable with delegates
I understand you wanting to try and implement it yourself but delegates and events are really the natural fit here (and are in fact an implemention of the observer pattern built into c#).
If still want to do it yourself I would recommend using interfaces instead of abstract/concrete classes.
public interface ISubject
{
void AttachObserver(IObserver observer);
void DetachObserver(IObserver observer);
void NotifyObservers(CommonNotification Notification);
}
public interface IObserver
{
void OnNotify(CommonNotification Notification);
}
Your form could then implement IObserver (or ISubject or both!!).
public class MyForm : Form, IObserver
{
...
}
you can use Interface instead of abstract class like this
public Interface IObserver
{
public void OnNotify(CommonNotification Notification);
}
....
public class MyForm:Form, IObserver {
....
}
You should replace your Observer class with an Interface:
public Interface IObserver
{
public void OnNotify(CommonNotification Notification);
}
Then your mainform (or anything else) can implement IObserver
You can implement it very easily using event. I am giving a sample code -
public class MyForm : Form
{
public event Action btn1Clicked;
private void button1_Click(object sender, EventArgs e)
{
btn1Clicked();
}
}
public abstract class AbsObserver
{
protected MyForm Form;
public AbsObserver(Subject subject)
{
subject.Attach(OnNotify);
Form = new MyForm();
Form.btn1Clicked += Form_btn1Clicked;
}
void Form_btn1Clicked()
{
Console.WriteLine("Do button click task");
}
public abstract void OnNotify();
}
public class Observer1 : AbsObserver
{
public Observer1(Subject subject)
: base(subject)
{
}
public override void OnNotify()
{
Console.WriteLine("observer1 notified");
}
}
public class Observer2 : AbsObserver
{
public Observer2(Subject subject)
: base(subject)
{
}
public override void OnNotify()
{
Console.WriteLine("observer2 notified");
}
}
public class Subject
{
private event Action Notify;
public void Attach(Action a)
{
Notify += a;
}
private void NotifyAll()
{
Notify();
}
}
Here forms are not observers. The observers have the object of form and all form related issues are handled by the observers. This is kind of composting.

Categories

Resources