Communication between ViewModels via events - c#

I have an application where I open a new view (with viewmodel) within another viewmodel. The new Viewmodel needs some parameters from the caller viewmodel.
Now I introduced a class called Messenger which looks like:
public class Messenger
{
private static Messenger instance;
public static Messenger Instance
{
get { return instance ?? (instance = new Messenger()); }
}
private Messenger() { }
public void SendMessage(Message message)
{
OnMessageSent temp = MessageSent;
if (temp != null)
temp(message);
}
public delegate void OnMessageSent(Message message);
public event OnMessageSent MessageSent;
}
The message-class looks like:
public class Message
{
public Type TargetType { get; set; }
public object Sender { get; set; }
public Type ValueType { get; set; }
public object Value { get; set; }
public string AdditionalInfo { get; set; }
}
The usage of this building is:
The receiver-viewmodel of a message has to add
Messenger.Instance.MessageSent += MessageSent;
to it's constructor. And in the implementation can look like:
private void MessageSent(Message message)
{
if(message.TargetType == typeof(SecondViewModel))
{
if (message.ValueType == typeof (double))
{
this.MyValue= (double) message.Value;
}
}
}
This works fine. My question is: Is this a good way for the communication between viewmodels or are there any weakenings in this design?

You don't need to re-invent the wheel when its already there.
Use EventAggregator which internally uses weak event pattern to prevent any memory leaks. Refer to tutorials here and here.
Also you can use Event Aggregator with Reactive Extensions.
Refer to the post for viable approaches you can reuse - Even Aggregator Implementation Sample/Best Practices.

Related

Handle an external API event from a method C#

I'm using Advance Steel API for a library I'm creating and I need to somehow handle an event.
This is the class the API provides
public class OpenDatabase : IDisposable {
public OpenDatabase();
~OpenDatabase();
public UnitsSet Units { get; set; }
public Database CADDatabase { get; }
public ASObjectsAPI.OpenDatabase* Native { get; set; }
public IntPtr Internal { get; }
...
public event DatabaseEventHandler ObjectAppended;
...
...
public void objectAppended(Database db, CADAccess.FilerObject fo);
...
protected void raise_ObjectAppended(object value0, DatabaseEventArgs value1);
public delegate void DatabaseEventHandler(object source, DatabaseEventArgs args);
public delegate void IdMappingEventHandler(object source, IdMappingEventArgs args);
}
I need to handle the event ObjectAppended from another method. How should I proceed? I don't know how to work with events.
I guess the method I need to use is objectAppended(Database, CAD.Access.FilerObject) because I need to get that FilerObject.
If you take a look at this documentation, you'll learn how to subscribe to events like this:
public void SomeMethod() {
yourOpenDatabase.ObjectAppended += ObjAppendHandler;
}
public void ObjAppendHandler(Database db, CADAccess.FillerObject fo) {
//Insert code to handle your event...
}

How to pass abstract method as reference?

I will admit, i am doing homework and i am stuck on this one question (Part A). How do i pass the notice method as reference to the railway signal ? Can't i just find out which class was called in the abstract constructor and then print the class name within the notify method? For example:
RailwayUser
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.Writeline(className);
}
RailwaySignal Class
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
This kind of code / design is flawed, since what it does is RailwayUser, registers the object reference with the _watchers List in the RailWaySignal class, which in turn calls the public Notice method on each user when Notify is invoked, which is not how Event Signaling or Function Pointer works. In fact public _watchers is dangerous, as it can be cleared by any user, though that can be moderated using property access
Code with Issue
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
Following shall be the actual code using events and delegates:
Correct Version
Code Snippet Online - https://www.jdoodle.com/embed/v0/uEc
void Main()
{
List<RailwayUser> railwayUsers = new List<RailwayUser>();
railwayUsers.Add(new RailwayUser());
railwayUsers.Add(new RailwayUser());
RailwayUser.TestNotification();
}
public enum Colour
{
Red,
Green,
NoSignal
}
public class RailwaySignal
{
public string Name {get; set;}
public RailwaySignal(string railwaySignalName)
{
Name = railwaySignalName;
}
// Delegate for handling event
public delegate void RailwaySignalEventHandler(object source, Colour e);
// Delagate object for handling event
private RailwaySignalEventHandler _railwaySignalEvent;
// Event Accessor
public event RailwaySignalEventHandler RailwaySignalEvent
{
add
{
lock (this)
{
_railwaySignalEvent += value;
}
}
remove
{
lock (this)
{
_railwaySignalEvent -= value;
}
}
}
// Invoke Event for subscribed clients
private void Notify()
{
if (_railwaySignalEvent != null)
_railwaySignalEvent.Invoke(this, Colour.Green);
}
// Test the Event Invocation
public void TestEvent()
{
Notify();
}
}
public class RailwayUser
{
private static RailwaySignal railwaySignal { get; set;} = new RailwaySignal("Signal1");
public RailwayUser()
{
railwaySignal.RailwaySignalEvent += this.Notice;
}
public static void TestNotification()
{
railwaySignal.TestEvent();
}
public void Notice(object sender, Colour color)
{
Console.WriteLine($"Notice Called, Colour is :: {color}, Sender is :: {((RailwaySignal)sender).Name}");
}
}
Result
Notice Called, Colour is :: Green, Sender is :: Signal1
Notice Called, Colour is :: Green, Sender is :: Signal1
Important Details
Signature of the event is, (object source, Colour e) which helps in passing the relevant information across to the RailwayUser called, We now know the RailwaySignal triggering the notification to the RailwayUser and its Colour value
Event / Delegate has same signature as called method (which is the basis of working of Delegate / function pointers)
For simplification RailwayUser is a non abstract class
Event is executed using Notify() method inside the RailwaySignal, we are calling it artificially using TestNotification() inside RailwayUser just for demo purpose, but ideally it shall be internally triggered and shall pass on current state like Colour
Pre-defined delegates like Func, Action are quite often used for similar notification mechanism, They internally works using similar mechanism, though declaring an explicit event which is internally a delegate is a well defined pattern, especially for the Ui controls
Standard events exposed by the .Net framework have the signature object sender, EventArgs e, where EventArgs can wrap all information from Event executor (RailwaySignal) to Event receiver (RailwayUser)
It seem like a Observer pattern.You can pass SubClass which inherit from RailwayUser object instance into RailwaySignal class
Your RailwayUser class need create public abstract void Notice(Colour state) method.
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
Driver class can inherit RailwayUser class then override Notice method.
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine($"Driver see the {state.ToString()}");
}
}
There are
List<RailwayUser> _watches contain observable object
use SubScript(RailwayUser user) subscription user on _watches List.
RailwayUser Notify() to invoke all your observable Notify method.
look like this.
public class RailwaySignal
{
private List<RailwayUser> _watches;
public Colour Stata { get; set; }
public RailwaySignal()
{
_watches = new List<RailwayUser>();
}
public void SubScript(RailwayUser user)
{
_watches.Add(user);
}
public void Notify()
{
foreach (RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(Stata);
}
}
}
sample:https://dotnetfiddle.net/GcdGMy
You can also use event to pass method into RailwaySignal then invoke Notify method.
public enum Colour
{
Green,
Red,
Disable
}
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Driver see the "+ state.ToString());
}
}
public class Controller : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Controller see the " + state.ToString());
}
}
public class RailwaySignal
{
public delegate void NoticeEvent(Colour state);
public event NoticeEvent Notifys;
public Colour Stata { get; set; }
public void Notify()
{
if (Notifys != null)
{
Notifys(Stata);
}
}
}
use like this.
RailwaySignal railway = new RailwaySignal() { Stata = Colour.Green};
railway.Notifys += new Driver().Notice;
railway.Notifys += new Controller().Notice;
railway.Notify();
sample : https://dotnetfiddle.net/GcdGMy

Design notification structure for API in C#

I am new in designing API class library in C#. I want to build a class library which will be used by many clients. The clients will be referencing this DLL.
My specific scenario: Say for example there is a domain model class available in the API. The clients will query the API and get the list of these domain model classes. Now, on the basic of some condition and data, the clients will evaluate the expressions defined in the domain model class.
Here, whenever this evaluation happens, I want to log the data and the result into the database.
What have I tried: One solution, here, is that the client call the logging method directly on the API class. However, I don't want the client to know what all is happening on the API side. There should be Separation of Concerns and Single responsibility.
Another solution tried is Define few properties in the domain model class and the client will set these properties when the expressions are evaluated. On the domain model class side, I have defined a static event which is fired when these properties are changed. The main API class will be listening to the changes in these properties and will be executing the required actions.
Question: Here, I wanted to know is there any another suggested methodology or best practice to achieve this ?
I will appreciate your time and guidance. Thank you.
Here, is the code that I have tried.
Domain class
public class DomainClass
{
private bool isTrue;
public List<Expression> Expressions { get; set; }
public bool IsTrue
{
get
{
return isTrue;
}
set
{
isTrue= value;
OnPropertyChanged("isTrue");
}
}
#region Events
public static event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
Expression class
public class Expression
{
public string FieldName { get; set; }
public string Operator { get; set; }
public string Value { get; set; }
}
API class
public class APIClass
{
public APIClass()
{
DomainClass.PropertyChanged += DomainClass_PropertyChanged;
}
void DomainClass_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// Call required method.
}
public List<DomainClass> GetDomainClass()
{
// Methods returns list of domain classes
}
public void LogDetails (DomainClass dmClass)
{
// Perform the required logic.
}
}
Client Side
public void ProcessDomainClasses()
{
APIClass api = new APIClass()
List<DomainClass> myDomainClasses = api.GetDomainClass();
for (int dIndex = 0; dIndex < myDomainClasses.Count; dIndex++)
{
// Logic to evaluate the list of expressions in the domain class based on the data the client is having.
// So, the client will check the expression: fieldName [operator] value is true or not
myDomainClasses[dIndex].IsTrue = true;
}
}
Let me know in case you need any more information. I appreciate your time.
Static events looks crappy for me(atleast in this scenario). I'd do it with interface say ILogDetails (give proper name)
public interface ILogDetails
{
void LogDetails(DomainClass dmClass);
}
Implement in ApiClass or some other class.
public class APIClass : ILogDetails
{
public APIClass()
{
}
public List<DomainClass> GetDomainClass()
{
return new List<DomainClass>()
{
new DomainClass(this)//Pass ILogDetails implementation
...
};
}
public void LogDetails(DomainClass dmClass)
{
// Perform the required logic.
}
}
public class DomainClass
{
private bool isTrue;
private ILogDetails logDetails;
//Make constructor internal to prevent client knowing this
internal DomainClass(ILogDetails logDetails)
{
this.logDetails = logDetails;
}
public List<Expression> Expressions { get; set; }
public bool IsTrue
{
get
{
return isTrue;
}
set
{
isTrue = value;
logDetails.LogDetails(this);//Call the LogDetails method
}
}
}

Instantiating a delegate method to be used in a class library

I'm building an email-monitoring framework that I'll be using for a handful of users, so I'm building a class library to wrap everything in. I'm instantiating the configuration (sender, subject, last-received, ...) in a static class. Therefore, I have something like this.
public static class MyConfig
{
public static int Sender { get; set; }
// and so on and so forth
public static void BuildMyConfig(string theSender, string theRecipient, ...)
{
Sender = theSender;
// yada yada yada...
}
}
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Obviously, what we do with the email will be something completely different in each case. What I'm trying to is instantiate that delegate. Where would I do that? The MyConfig class and then invoke it from there as a static method? The instance of the Monitoring class?
An application would look like...
public class SpecificMonitor
{
Monitoring.BuildMyConfig("foo#bar.com", "bar#foo.com", ...);
Monitoring m = new Monitoring();
m.StartMonitoring();
//But where do I build the delegate method???
}
I've gotten compiling errors with every option I've tried so far. I've also tried overriding a method instead of using a delegate, using interfaces... but I think delegation is where it's at.
Thanks in advance!
Consistent with the rest of your design (although I do not necessarily agree that the design is great) you could allow for the callback to be set in the configuration class
public static class MyConfig
{
public static string Sender { get; set; }
public static DoSomethingWithEmail EmailReceivedCallback { get; set; }
public static void BuildMyConfig(string theSender, string theRecipient,
DoSomethingWithEmail callback)
{
Sender = theSender;
EmailReceivedCallback = callback;
}
}
// Make sure you bring the delegate outside of the Monitoring class!
public delegate void DoSomethingWithEmail(string theContents);
When an incoming email is acknowledged by your application you can now pass the email to the callback assigned to the configuration class
public class Monitoring
{
public void StartMonitoring()
{
const string receivedEmail = "New Answer on your SO Question!";
//Invoke the callback assigned to the config class
MyConfig.EmailReceivedCallback(receivedEmail);
}
}
Here is an example of usage
static void Main()
{
MyConfig.BuildMyConfig("...", "...", HandleEmail);
var monitoring = new Monitoring();
monitoring.StartMonitoring();
}
static void HandleEmail(string thecontents)
{
// Sample implementation
Console.WriteLine("Received Email: {0}",thecontents);
}
Define the constructor so that when people instantiate a Monitoring object, they must define the delegate:
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public Monitoring(Delegate DoSomethingWithEmail)
{
this.DoSomethingWithEmail = DoSomethingWithEmail;
}
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Then pass in the delegate you want when you instantiate each Monitoring:
Monitoring m = new Monitoring(delegate(EmailContents theContents)
{
/* Do stuff with theContents here */
});
m.StartMonitoring();

Updating another ViewModel on PropertyChanged of another ViewModel

Both ViewModels know nothing about each other, but i need to send the new value that have changed in one viewmodel to a method in the other view model, what are my options?
could you please list all possibilities and what would be the best way?
Taken from this answer:
If you want loosely-coupled communication you need an EventAggregator:
//Simplest EventAggregator
public static class DumbAggregator
{
public static void BroadCast(string message)
{
if (OnMessageTransmitted != null)
OnMessageTransmitted(message);
}
public static Action<string> OnMessageTransmitted;
}
Usage:
public class MySender
{
public void SendMessage()
{
DumbAggregator.BroadCast("Hello There!");
}
}
public class MySubscriber
{
public MySubscriber()
{
DumbAggregator.OnMessageTransmitted += OnMessageReceived;
}
private void OnMessageReceived(string message)
{
MessageBox.Show("I Received a Message! - " + message);
}
}
Notice however, that EventAggregators included in MVVM frameworks such as Prism are much more complex and include a whole lot of functionality. This is just a simple example.

Categories

Resources