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
Related
I'm not sure how to make navigation using mvvm. I'm a beginner so I haven't used any framework like mvvm light.
I found good example https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/. But it is not exactly what I'm looking for because in my app each view will cover all window. So when I will change page i will have no controls access from the mainview.
So I decided to make one MainViewModel for changing ViewModels (as in Rachel Blog) but each ViewModel should know about MainViewModel to execute change view. So when I create PageViewModel, I pass in constructor MainViewModel with public method, for example, changeview().
Is it a good way of doing this? Or, maybe, there's a better way to achieve this?
The child viewmodels should not know about main viewmodel.
Instead they should raise events with names like Forward or Back and so forth. ChangeView is the only example you give, so we’ll go with that.
We'll have the child viewmodel expose commands that cause the events to be raised. Buttons or MenuItems in the child view's XAML can bind to the commands to let the user invoke them. You can also do that via Click event handlers calling viewmodel methods in the child view code behind, but commands are more "correct", because at the cost of a little more work in the viewmodel, they make life a lot simpler for creators of views.
Main viewmodel handles those events and changes the active page viewmodel accordingly. So instead of child calling _mainVM.ChangeView(), child raises its own ChangeView event, and the main VM’s handler for that event on the child calls its own method this.ChangeView(). Main VM is the organizer VM, so it owns navigation.
It’s a good rule to make code as agnostic as possible about how and where it’s used. This goes for controls and viewmodels. Imagine if the ListBox class required the parent to be some particular class; that would be frustrating, and unnecessary as well. Events help us write useful child classes that don’t need to know or care anything about which parent uses them. Even if reuse isn’t a possibility, this approach helps you write clean, well-separated classes that are easy to write and maintain.
If you need help with the details, provide more code, and we can go through applying this design to your project.
Example
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
FooViewModel = new FooViewModel();
FooViewModel.Back += (object sender, EventArgs e) => Back();
}
public FooViewModel FooViewModel { get; private set; }
public void Back()
{
// Change selected page property
}
}
public class FooViewModel : ViewModelBase
{
public event EventHandler Back;
private ICommand _backCommand;
public ICommand BackCommand {
get {
if (_backCommand == null)
{
// It has to give us a parameter, but we don't have to use it.
_backCommand = new DelegateCommand(parameter => OnBack());
}
return _backCommand;
}
}
// C#7 version
public void OnBack() => Back?.Invoke(this, EventArgs.Empty);
// C# <= 5
//protected void OnBack()
//{
// var handler = Back;
// if (handler != null)
// {
// handler(this, EventArgs.Empty);
// }
//}
}
// I don't know if you already have a DelegateCommand or RelayCommand class.
// Whatever you call it, if you don't have it, here's a quick and dirty one.
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> exec, Func<object, bool> canExec = null)
{
_exec = exec;
_canExec = canExec;
}
Action<object> _exec;
Func<object, bool> _canExec;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExec == null || _canExec(parameter);
}
public void Execute(object parameter)
{
if (_exec != null)
{
_exec(parameter);
}
}
}
How to invoke BackCommand from child XAML:
<Button Content="Back" Command="{Binding BackCommand}" />
I have a view who is a winform:
public partial class View : Form, IView
{
private static object eventsLock = new Object();
private EventHandler CustomClick;
public View()
{
InitializeComponent();
this.button.Click += FireCustomClickEvent;
}
event EventHandler IView.Click
{
add { lock (eventsLock) { this.CustomClick += value; }}
remove { lock (eventsLock) { this.CustomClick -= value; }}
}
private void FireCustomClickEvent(object sender, EventArgs e)
{
this.CustomClick?.Invoke(sender, e);
}
public void MakeViewDoStuff()
{
//Do stuff...
}
}
The IView interface is as follow:
public interface IView
{
event Eventhandler Click;
void MakeViewDoStuff();
}
And I have this presenter:
public class Presenter : IPresenter
{
private IView view;
public Presenter(IView view)
{
this.view = view;
this.AttachView();
}
private void AttachView()
{
this.view.Click += SomePresenterStuff;
}
private void SomePresenterStuff(object sender EventArgs e)
{
this.view.MakeViewDoStuff();
//Do stuff now that the event was raised.
}
}
Now I can test that the event is raised and that the presenter makes the view do stuff with this test function:
this.mockedView.Raise(v => v.Click += null, new System.EventArgs());
this.mockedView.Verify(v => v.MakeViewDoStuff(), Times.Once());
But what I want to know is: Should I test that the presenter is attaching in its constructor?
So basically, should I test that private void AttachView() is called in the constructor of Presenter and how would I do this?
Some notes:
I think that adding a Boolean flag to keep the attached state of the presenter is kind of a bad hack since it would involve changing the production code only to be able to test it and would not be used in production.
EDIT:
I kind of agree with Sergey Berezovskiy that it might not be the presenter responsability nor a behavior, but what if my view had a CloseView event, that when raised, the presenter should unsubscribe to the view events. Wouldn't we want to test this?
I believe presenter's responsibility is
reacting to view's Click event by doing some stuff
If that happens - who cares how presenter was attached to event? That's just technical details not related to business logic. Your current test verifies exactly what should be verified.
There are lot of technical stuff which you can verify
presenter stores view in private field?
presenter attaches to event?
presenter stays attached over time?
But it doesn't really matter while presenter reacts to view's event in required way.
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;
}
}
After a major edit to this quesiton, I'm hoping it's now clear.
I'm very lost with binding in WPF when 1 change should affect multiple properties.
I regularly use VVM to bind my ViewModel to my View and I would say I'm OK with it.
I am trying to implement a state controller. This means that, what ever settings I made in part of my UI, the reflection is through out.
For example in my part of my UI, I can toggle a feature on or off, such as "show images"
When I make this change, I'd like everything in my application to be notified and act accordingly.
So, my StateController class will have a property
public bool ShowImages
And in my View, I'd likely have something like
<image Visible ="{Binding ShowImages", Converter={StaticConverter ConvertMe}}" />
The problem I have is how I go about making the StateController alert all of my ViewModels of this.
Currently, in each ViewModel I'm assuming I'd have to have the same property repeated
public bool ShowImages
EG
public class StateController : BaseViewModel
{
public bool ShowImages{get;set;}//imagine the implementation is here
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages{}//imagine the implementation is here
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages{}//imagine the implementation is here
}
So, my question is, if I updated ViewModelB.ShowImages, how would I first inform the StateController which in turn updates all ViewModels.
Is this something the INotifyPropertyChanged can do automatically for me since they all share the same propertyName, or do I have to implement the logic manually, eg
public static class StateController
{
public bool ShowImages{get;set;}//imagine the implementation is here
}
public class ViewModelA : BaseViewModel
{
public bool ShowImages
{
get { return StateController.ShowImages; }
set { StateControllerShowImages = value;
OnPropertyChanged("ShowImages"); }
}
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages
{
get { return StateController.ShowImages; }
set { StateControllerShowImages = value;
OnPropertyChanged("ShowImages"); }
}
}
I hate the idea of the above implementation but it does show what I'm trying to achieve. I just hope there is a better way!
The PropertyChange notification is only raised for that one object model.
So raising a change notification of the "Name" property of ClassA will only update the UI in cases where it's bound to that specific ClassA.Name. It won't trigger a change notification for any ClassB.Name, or other instances of ClassA.Name.
I would suggest using a Singleton here for your StateModel, and having your other models subscribe to the StateModel.PropertyChanged event to know if it should update, like this answer.
public ViewModelA
{
public ViewModelA()
{
StateController.Instance.PropertyChanged += StateController_PropertyChanged;
}
void StateController_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
{
// if singleton's ShowImages property changed, raise change
// notification for this class's ShowImages property too
if (e.PropertyName == "ShowImages")
OnPropertyChanged("ShowImages");
}
public bool ShowImages
{
get { return StateController.Instance.ShowImages; }
set { StateController.Instance.ShowImages = value; }
}
}
If I understood you correctly, you are looking for a mechanism that allows your different ViewModels to communicate between each other.
One possible way would be to implement the Observer Pattern (a code example can be found here: "Observer pattern with C# 4"). In this way your ViewModel subscribe each other to receive change notifications from a "publisher", i.e. the ViewModel that had its value changed. You have a good control over who receives which notification from which publisher. The downside of this approach is a tight coupling between your models.
My approach would be this:
Use a message dispatcher. Your ViewModels can subscribe to a certain type of message, e.g. ShowImagesChanged. If any of your ViewModels changed the ShowImages property, that ViewModel calls the dispatcher to send out such a ShowImagesChanged message with your current values.
This way you can keep you ViewModels decoupled from each other. Still, although the ViewModels do not know each other this gives a way to exchange data between them.
Personally, I have used the Caliburn Micro MVVM framework several times for this, but there should be enough other MVVM frameworks that provide the same functionality to fit your taste.
The Calibiurn Micro documentation and how easily the dispatcher can be used is here: Event Aggregator
To avoid code repetition you can create a class derived from BaseViewModel that implements your property and have ViewModelA, ViewModelB extend it. However, this does not solve the problem of keeping each instance updated.
In order to do so, you may:
Use a static class (your current solution) or a Singleton as suggested in one of the comments. This is simple but has potential problems such as race conditions and coupling.
Have your ShowImages binding property repeated in each ViewModel and update it by subscribing to a ShowImagesChanged event. This could be published through a Command executed from the UI. I'd say this is the WPF approach and has the benefit of decoupling the ShowImages state management from its consumption.
Assign the ShowImagesupdate responsibility to a single ViewModel and subscribe to the its PropertyChanged in the other ViewModels so that they update accordingly. Better than the first option, but still huge coupling.
Why repeat properties at all? Just bind to StateController itself.
Say we have singleton StateController:
public class StateController : INotifyPropertyChanged
{
private static StateController instance;
public static StateController Instance {
get { return instance ?? (instance = new StateController()); }
}
//here`s our flag
private bool isSomething;
public bool IsSomething
{
get { return isSomething; }
set
{
isSomething = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsSomething"));
}
}
private StateController(){}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
Then in base VM class just make a reference to this controller:
public StateController Controller { get { return StateController.Instance; } }
And where needed bind like this:
<CheckBox IsChecked="{Binding Controller.IsSomething}">
Test
</CheckBox>
This way every binding will work with one property and react to one property. If you need some custom code to work you can subscribe to PropertyChanged of StateController where needed and take action.
I have core functionality encapsulated in ViewModelBase
Now I want to see when PropertyChanged event was raised by ViewModelBase and act on it. For example, when one property was changed on ViewModelBase - I want to change property on my ViewModel
How do I achieve this?
public class MaintainGroupViewModel : BaseViewModel<MEMGroup>
{
public abstract class BaseViewModel<T> : NotificationObject, INavigationAware
where T : Entity
{
Usually I use register to the PropertyChanged event in the class Constructor
public MyViewModel()
{
this.PropertyChanged += MyViewModel_PropertyChanged;
}
and my PropertyChanged event handler looks like this:
void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "SomeProperty":
// Do something
break;
}
}
I am concerned that you're effectively doing a 'manual binding' (bad) for a property in a derived class to a value on the base class (also bad). The whole point of using inheritance is that the derived class can access things in the base class. Use a protected modifier to indicate things should only be accessible to derived classes.
I would suggest this (potentially) more correct method:
Base class:
protected virtual void OnMyValueChanged() { }
Derived class:
protected override void OnMyValueChanged() { /* respond here */ }
Really, subscribing to an event in the base class of the very class you're writing just seems incredibly backwards - what's the point of using inheritance over composition if you're going to compose yourself around yourself? You're literally asking an object to tell itself when something happens. A method call is what you should use for that.
In terms of "when one property was changed on ViewModelBase - I want to change property on my ViewModel", ... they are the same object!
The direct way to subscribe to property changes is using INotifyPropertyChanged if your BaseViewModel implements it:
PropertyChanged += (obj, args) =>
{ System.Console.WriteLine("Property " + args.PropertyName + " changed"); }
If it doesn't, then it has to be a DependencyObject, and your properties have to be DependencyProperties (which is probably a more complicated way).
This article describes how to subscribe for DependencyProperty changes.