What would be the best way to raise a MVVM property from a different ViewModel? - wpf

On my MainViewModel, I have a property that looks like this:
private bool _IsSkinNight = true;
public bool IsSkinNight
{
get { return _IsSkinNight; }
set
{
_IsSkinNight = value;
RaisePropertyChanged("IsSkinNight");
RaisePropertyChanged("WindowBackColor");
RaisePropertyChanged("StyleImage");
}
}
As you can see, I use this one property to raise other properties. It makes changing the UI much easier. Especially as I am going to add more items to it.
However, I have a property on a WPF page that I need to update as well. But since it's on a different page, its ViewModel is separate as well. So this property can't call RaisePropertyChanged on it and the property in the page can't check the state of IsSkinNight.
So what would be the best way to cross between the different ViewModels? I'll be adding more pages. So is there a way to make like a universal property that all ViewModels can access?
Thanks

Even if I don't like using it, you probably need to look up for EventAgregators.
Basically you will be able to fire an event in your MainViewModel, and in your other view models you will be able to register to one or more event of this agregator.
However, I strongly recommend that you use it very lightly, because it can become extremely difficult to Debug, since there is no call stack when you fire an event like that.

Thanks for the help everyone. I did a bit more research, based on the suggestions here, and came across MVVM Messages.
Using this blog post, I was able to use MVVM messages to achieve my goal. Thanks again.

In such a case, I sometimes create a kind of Context class, to keep track of "global" properties. This class has to be registered as a Singleton.
public class SkinContext : ISkinContext {
private bool _IsSkinNight = true;
public bool IsSkinNight
{
get { return _IsSkinNight; }
set
{
_IsSkinNight = value;
RaisePropertyChanged("IsSkinNight");
RaisePropertyChanged("WindowBackColor");
RaisePropertyChanged("StyleImage");
}
}
then I inject this class in each ViewModel that needs to be aware of the context and subscribe to the NotifyPropertyChanged event (Or a custom event that you created):
public class SomeViewModel {
public SomeViewModel(ISkinContext context){
context.OnPropertyChanged += (s,e) => { /*Raise other notification and whatnot*/ }
}
}

Related

WPF MVVM ViewModel containing ViewModels not updating

I'm very new to WPF and MVVM, and it's been causing me a lot of headaches. Due to issues with navigation, I decided to just have all my content visible at once. I thought I would create a new ViewModel (MainViewModel) to contain my two other ViewModels (StudentViewModel and AddStudentsViewModel).
MainViewModel contains something like this:
private StudentViewModel _studentVM;
private AddStudentsViewModel _addStudentsVM;
public StudentViewModel StudentVM
{
get { return _studentVM; }
set
{
if (_studentVM != value)
{
_studentVM = value;
NotifyPropertyChanged("StudentVM");
}
}
}
(public AddStudentsViewModel AddStudentsVM exists as well, I'm just trying to keep this short)
I have successfully bound StudentVM and AddStudentsVM to my main View, as I can programmatically set values during the initialization phase and when debugging, I can see my button clicks are being redirected to the correct methods. It even seems like I am successfully adding students to objects, however my main View isn't reflecting these changes.
Am I missing something in MainViewModel? Or is it not possible for a ViewModel to see the changes in any other ViewModels inside it?
If your view is bound to some property inside StudentViewModel through nested navigation into the property StudentVM like StudentVM.Property, then to reflect changes on StudentVM.Property unto your view would require that you notify the view that StudentVM (not StudentVM.Property) has changed.
So it depends on how you defined your binding and on which property you're raising the PropertyChanged event.
A viewmodel which contains two other viewmodels. Just think about it for a second. It's not a good idea.
Anyway, you have to implement INotifyPropertyChanged in your containing viewmodels as well. Not only in the containing MainViewModel.
Maybe that is the fault?

How to discern whether Model has been changed by user or code

My Model implements INotifyPropertyChanged, and I have a WPF window bound to it (two way bindings).
I need to know when the model is being changed through the bound UI, so I could call an Update method from another module (which then copies My model to it's internal structures).
Model can also be changed by another module.
How to tell (in my PropertyChanged event handler) if the change originated in my UI, and not in that other module?
I do not want to call Update method if it was the other module that triggered the PropertyChanged event.
I'm fairly new to WPF myself, but the only immediately obvious way I can think of to do this would be to add extra set methods to the model, that modify the backing store without directly changing the property and thus firing the PropertyChanged event. To remove duplication, the property setter should probably call those methods as well and there should be a boolean argument fireChangedEvent. Something like this:
public string SomeThing
{
get { return _someThing; }
set { SetSomeThing(value, true); }
}
public void SetSomeThing(string value, bool fireChangedEvent)
{
_someThing = value;
if(fireChangedEvent)
{
NotifyPropertyChanged("SomeThing");
}
}
Then, in the other module, it would be
public void DoStuff
{
// ...
model.SetSomeThing("foo",false);
// ...
}
It's not an elegant method I know, and I hope someone else can think of something smarter, but I can't think of a good way of finding out from inside a property setter what exactly is setting that property.
Hopefully this is at least a workaround suggestion.
There is another way: using Binding.SourceUpdated
Every binding on the window would have to be set NotifyOnSourceUpdated=true, and the common handler for SourceUpdated event would do the rest (raise the Window.ModelEdited event that would trigger Update on another module).

WPF: DependencyProperty of custom control fails when using several instances of the control

I've built a custom control in WPF that inherits from ListBox. In this I have implementet my own property that is a BindingList. To make this property bindable I've implemeneted it as a DependencyProperty:
public BindingList<CheckableListItem> CheckedItems
{
get
{
return (BindingList<CheckableListItem>)GetValue(MultiComboBox.CheckedItemsProperty);
}
set
{
SetValue(MultiComboBox.CheckedItemsProperty, value);
}
}
public static readonly DependencyProperty CheckedItemsProperty;
I register this DependencyProperty in a static constructor inside my custom control:
CheckedItemsProperty = DependencyProperty.Register("CheckedItems",
typeof(BindingList<CheckableListItem>),
typeof(MultiComboBox),
new FrameworkPropertyMetadata(new BindingList<CheckableListItem>()));
(MultiComboBox is the name of my custom control. CheckableListItem is a simple class I've written just for this purpose).
This BindingList is then updated inside the custom control (never outside) as the user interacts with it.
When I use my custom control in XAML I bind to the CheckItems property with the mode "OneWayToSource". I'm using the MVVM pattern and the property in the ViewModel that I'm binding to is also a BindingList. The ViewModel never affects this list, it just reacts at the changes that the custom control make to the list. The property in the ViewModel looks like this:
private BindingList<CheckableListItem> _selectedItems;
public BindingList<CheckableListItem> SelectedItems
{
get
{
return _selectedItems;
}
set
{
if (value != _selectedItems)
{
if (_selectedItems != null)
{
_selectedItems.ListChanged -= SelectedItemsChanged;
}
_selectedItems = value;
if (_selectedItems != null)
{
_selectedItems.ListChanged += SelectedItemsChanged;
}
OnPropertyChanged("SelectedItems");
}
}
}
As you can see I'm listening to changes made to the list (these changes always occur inside my custom control), and in the "SelectedItemsChanged"-method I update my Model accordingly.
Now...this works great when I have one of these controls in my View. However, if I put two (or more) of them in the same View strange things start to happen. This will of course mean that I'll have two lists with selected items in my ViewModel. But if do something in the View that changes one of the lists, both lists are affected! That is, the event handlers for the event ListChanged is triggered for both list if changes are made to any one of them!
Does anyone recognize this problem and/or have a solution to it? What is wrong with my implementation?
My first though is that the DependencyProperty is static. Normally that means shared between all instances. But I guess DependencyProperties work in some other "magical" way so that might not be the problem.
Any tips or hints is appreciated!
I had a similar problem with a collection-type dependency property. My solution was taken from the MSDN article on Collection-Type Dependency Properties. It was adding the following line
SetValue(OperatorsPropertyKey, new List<ListBoxItem>()); //replace key and type
in the constructor of my control because it seems that a collection-type dependency property constructor is being called only once no matter how many instances your control containing this collection has (static eh).
This sounds like you bound both/all the Views to the same ViewModel. That would explain that changes to one cause changes in the other.

Why ObservableCollection is not updated on items change?

I noticed that ObservableCollection in WPF reflects changes in GUI only by adding or removing an item in the list, but not by editing it.
That means that I have to write my custom class MyObservableCollection instead.
What is the reason for this behaviour?
Thanks
The ObservableCollection has no way of knowing if you make changes to the objects it contains - if you want to be notified when those objects change then you have to make those objects observable as well (for example by having those objects implement INotifyPropertyChanged)
another way of achieving this would be that you implement a new XXXViewModel class that derives from DependencyObject and you put this one in the ObservableCollection.
for this look at this very good MVVM introduction: http://blog.lab49.com/archives/2650
an example for such a class would be:
public class EntryViewModel : DependencyObject
{
private Entry _entry;
public EntryViewModel(Entry e)
{
_entry = e;
SetProperties(e);
}
private void SetProperties(Entry value)
{
this.Id = value.Id;
this.Title = value.Title;
this.CreationTimestamp = value.CreationTimestamp;
this.LastUpdateTimestamp = value.LastUpdateTimestamp;
this.Flag = value.Flag;
this.Body = value.Body;
}
public Entry Entry
{
get {
SyncBackProperties();
return this._entry;
}
}
public Int64 Id
{
get { return (Int64)GetValue(IdProperty); }
set { SetValue(IdProperty, value); }
}
// Using a DependencyProperty as the backing store for Id. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IdProperty =
DependencyProperty.Register("Id", typeof(Int64), typeof(EntryViewModel), new UIPropertyMetadata(new Int64()));
}}
important things here:
- it derives from DependencyObject
- it operates with DependencyProperties to support WPFs databinding
br
sargola
You can register a method in the view model class aginst the PropertyChanged event of data class objects and listen to them in View model when any change in the property of the data objects happen. This is very easy and straight way to have the control in View model when the items of an observable collection changes. Hope this helps...
Probably because items have no way to alert the collection when they are edited - i.e. they might not be observable. Other classes would have similar behavior - no way to alert you to a every change in the graph of referenced classes.
As a work-around, you could extract the object from the collection and then reinsert it after you are done processing. Depending on your requirements and concurrency model, this could just make the program ugly, though. This is a quick hack, and not suitable for anything that requires quality.
Instead, you could implement the collection with an update method that specifically triggers the ContentChanged (not sure about the name) event. It's not pretty, but it is at least quite easy to deal with.
Ideally, as kragen2uk says, it would be best to make the objects observable and keep your client code clean and simple.
see also this question.

WPF: how to signal an event from ViewModel to View without code in codebehind? [duplicate]

This question already has answers here:
How can I Have a WPF EventTrigger on a View trigger when the underlying Viewmodel dictates it should?
(4 answers)
Closed 8 years ago.
I have quite simple (I hope :)) problem:
In MVVM, View usually listens on changes of ViewModel's properties. However, I would sometimes like to listen on event, so that, for example, View could start animation, or close window, when VM signals.
Doing it via bool property with NotifyPropertyChanged (and starting animation only when it changes from false to true) is possible, but it feels like a hack, I'd much prefer to expose event, as it is semantically correct.
Also, I'd like to do it without code in codebehind, as doing viewModel.myEvent += handler there would mean that I'd have manually unregister the event in order to allow View to be GC'd - WPF Views are already able to listen on properties 'weakly', and I'd much prefer to program only declaratively in View.
The standard strong event subscription is also bad, because I need to switch multiple ViewModels for one View (because creating View every time takes too much CPU time).
Thank you for ideas (if there is a standard solution, a link to msdn will suffice)!
Some comments:
You can use the weak event pattern to ensure that the view can be GC'd even if it is still attached to the view model's event
If you're already switching multiple VMs in for the one view, wouldn't that be the ideal place to attach/detach the handler?
Depending on your exact scenario, you could just have the VM expose a state property which the view uses as a trigger for animations, transitions, and other visual changes. Visual state manager is great for this kind of thing.
This is something that I wrestled with as well...
Similar to what others are saying, but here is an example with some code snippets... This example shows how to use pub/sub to have a View subscribe to an event fired by the VM - in this case I do a GridView. Rebind to ensure the gv is in sync with the VM...
View (Sub):
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Events;
private SubscriptionToken getRequiresRebindToken = null;
private void SubscribeToRequiresRebindEvents()
{
this.getRequiresRebindToken =
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Subscribe(this.OnRequiresRebindEventReceived,
ThreadOption.PublisherThread, false,
MemoryLeakHelper.DummyPredicate);
}
public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload)
{
if (payload != null)
{
if (payload.RequiresRebind)
{
using (this.gridView.DeferRefresh())
{
this.gridView.Rebind();
}
}
}
}
private void UnsubscribeFromRequiresRebindEvents()
{
if (this.getRequiresRebindToken != null)
{
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Unsubscribe(this.getRequiresRebindToken);
this.getRequiresRebindToken = null;
}
}
Call unsub from the close method to prevent memory leaks.
ViewModel (Pub):
private void PublishRequiresRebindEvent()
{
var payload = new RequiresRebindEventPayload();
payload.SetRequiresRebind();
EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload);
}
Payload class
using System;
using Microsoft.Practices.Composite.Presentation.Events;
public class RequiresRebindEvent
: CompositePresentationEvent<RequiresRebindEventPayload>
{
}
public class RequiresRebindEventPayload
{
public RequiresRebindEventPayload()
{
this.RequiresRebind = false;
}
public bool RequiresRebind { get; private set; }
public void SetRequiresRebind()
{
this.RequiresRebind = true;
}
}
Note that you can also set the constructor up to pass in a Guid, or some identified in, which can be set on Pub and checked on sub to be sure pub/sub is in sync.
imho yYand separated
state - to be able to move data back/forth between view <-> vm
actions - to be able to call onto view model functions/commands
notifications - to be able to signal to the view that something has happened and you want it to take a viewy action like make an element glow, switch styles, change layout, focus another element etc.
while is true that you can do this with a property binding, its more of a hack as tomas mentioned; always has felt like this to me.
my solution to be able to listen for 'events' from a view model aka notifications is to simple listen for data-context changes and when it does change i verify the type is the vm i'm looking for and connect the events. crude but simple.
what i would really like is a simple way to define some 'view model event' triggers and then provide some kind of handler for it that would react on the view side of things all in the xaml and only drop to code behind for stuff thats not do-able in xaml
Like adrianm said, when you trigger your animation off a bool property you are actually responding to an event. Specifically the event PropertyChanged which the WPF subsystem. Which is designed to attach/detach correctly to/from so that you don't leak memory (you may forget to do this when wiring an event yourself and cause a memory leak by having a reference active to an object which otherwise should be GCed).
This allows you to expose your ViewModel as the DataContext for the control and respond correctly to the changing of properties on the datacontext through databinding.
MVVM is a pattern that works particularly well with WPF because of all these things that WPF gives you, and triggering off a property change is actually an elegant way to use the entire WPF subsystem to accomplish your goals :)
A more general question to ask is: "Why am I trying to deal with this event in my ViewModel?"
If the answer has anything to do with view-only things like animations, I'd argue the ViewModel needs not know about it: code behind (when appropriate), Data/Event/PropertyTriggers, and the newer VisualStateManager constructs will serve you much better, and maintain the clean separation between View and ViewModel.
If something needs to "happen" as a result of the event, then what you really want to use is a Command pattern - either by using the CommandManger, handling the event in code behind and invoking the command on the view model, or by using attached behaviors in the System.Interactivity libs.
Either way, you want to keep your ViewModel as "pure" as you can - if you see anything View-specific in there, you're probably doing it wrong. :)

Resources