WPF MVVM ViewModel containing ViewModels not updating - wpf

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?

Related

How can I trigger some code to run in the view from the view model?

What is the best way for the view model to "ask" the view to do something, (e.g. manipulate on of the controls)? I don't think that the view model sending events violates MVVM as after all the INotifyPropertyChanged interface is an example of view models sending events to views.
I realize that having code behind in the view is not considered ideal from a MVVM perspective, so I could use an attached behavior to manipulate a particular control for example but the problem remains - how do I ask the attached behavior to do something from my view model?
I could have Boolean dependency property on my view with property changed event handler which runs the code I need to run. I could then bind this property to a property on my view model and then toggle the value of this property whenever I want to trigger the code to run. However this seems like a hack.
I could have an event on the view model and then explicitly attach this event to an event handler in the view in code but this seems anti MVVM - I should be able to achieve the connection using binding.
This must be a fairly common scenario, are there any standard solutions?
You could for example use an event aggregator or a messenger to send an event or message from the view model that the view handles in a loosely coupled way. Both the view and the view model know only about the event aggregator but they don't know anything about each other. Please refer to this blog post for more information about the concept.
Another common approach is to implement an interface in the view and inject the view model with this interface, e.g.:
public interface IDoSomething
{
void DoSomething();
}
public partial class Window1 : Window, IDoSomething
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel(this);
}
public void DoSomething()
{
//do something...
}
}
public class ViewModel
{
public ViewModel(IDoSomething doSomething)
{
//...
}
}
This doesn't break the MVVM pattern as the view model knows about and is dependant only on an interface.

How can I tell when the ItemsSource of my Custom control has changed

I have a custom control in the early stages of development as I endeavour to learn about wpf custom control development. The custom control inherits from ItemsControls which gives me access to an ItemsSource property to which I am binding an enumerable collection.
Currently I have a simple two project solution comprising my custom control in one and a test project in the other to test the former. In my test project I have a simple mainwindow onto which I have put my custom control and bound its ItemsSource.
<WpfControls:VtlDataNavigator x:Name="MyDataNavigator"
ItemsSource="{Binding ElementName=MainWindow, Path=Orders}" />
In the loaded event of the main window (which implements INotifyPropertyChanged) I instantiate the Orders collection. The customcontrols gets initialised before the main window loads but I can see from examining the Live Visual Tree in visual studio that once the main form loads the custom controls Items Source property is indeed set to Orders. Now of course I'd actually like to count the orders and have my custom control display that (it's a simple data navigator so what I'm after is the record count). I know how to get the count but how do I know when the itemsSource has changed so that I can react to it and get the count. There's no itemsSourceChanged event that I can see.
I've seen this blog article, but I'm wondering if there is a more straightforward approach to this as it seems such an obvious thing to want to know about.
You can do that using OverrideMetaData.
Try this:
public class Class1 : ItemsControl
{
static Class1()
{
ItemsSourceProperty.OverrideMetadata(typeof(Class1),
new FrameworkPropertyMetadata(null, OnItemSourceChanged));
}
private static void OnItemSourceChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Why you haz Changed me!");
}
}
The ItemsSource is a DependencyProperty, and when creating DPs you can optionally specify a "property changed" event. Unfortunately ItemsSource is locked away in the base class, so I started wondering if there might be a way to add your own event to an existing DP. I came across this article that looks promising. Basically you would do something like this (untested so read the article!):-
var dpd = DependencyPropertyDescriptor.FromProperty(
VtlDataNavigator.ItemsSourceProperty,
typeof(VtlDataNavigator));
if (dpd != null)
{
dpd.AddValueChanged(
vtlDataNavigatorInstance,
delegate
{
var count = VtlDataNavigatorInstance.ItemsSource.Count; // Or whatever...
});
}

A Controller for MVVM

I'm working on a WPF project that's a mishmash of code-behind xaml/xaml.cs and a few not-quite ViewModels as well.
(Disclaimer: Until recently I've had very little in the way of WPF experience. I can design and lay-out a Window or UserControl fairly proficiently, and I think I get the hang of separating an MVVM ViewModel from the View and doing binding wire-ups, but that's the limit of my experience with WPF at present.)
I've been tasked with adding some new features to the program, such that it looks like converting it to use MVVM properly first is going to be necessary.
I'll demonstrate a specific problem I'm facing:
There is a View called SettingsWindow.xaml that I'm working with. It's a set of textboxes, labels and whatnot. I've stripped-out all of the View data into a ViewModel class which resembles something like this:
class SettingsViewModel : ViewModelBase {
private String _outputDirectory;
public String OutputDirectory {
get { return _outputDirectory; }
set { SetValue( () => this.OutputDirectory, ref _outputDirectory, value) ); }
}
// `SetValue` calls `PropertyChanged` and does other common-tasks.
// Repeat for other properties, like "Int32 Timeout" and "Color FontColor"
}
In the original ViewModel class there were 2 methods: ReadFromRegistry and SaveToRegistry. The ReadFromRegistry method was called by the ViewModel's constructor, and the SaveToRegistry method was called by MainWindow.xaml.cs's code-behind like so:
private void Settings_Click(Object sender, RoutedEventArgs e) {
SettingsViewModel model = new SettingsViewModel(); // loads from registry via constructor
SettingsWindow window = new SettingsWindow();
window.Owner = this;
window.DataContext = model;
if( dialog.ShowDialog() == true ) {
model.SaveToRegistry();
}
}
...but this seems wrong to me. I thought a ViewModel should consist only of an observable data bag for binding purposes, it should not be responsible for self-population or persistence, which is the responsibility of the controller or some other orchestrator.
I've done a few days' worth of reading about MVVM, and none of the articles I've read mention a controller or where the logic for opening child-windows or saving state should go. I've seen some articles that do put that code in the ViewModels, others continue to use code-behind for this, others abstract away everything and use IService-based solutions, which is OTT for me.
Given this is a conversion project where I'll convert each Window/View individually over-time I can't really overhaul it, but where can I go from here? What does a Controller in MVVM look-like, exactly? (My apologies for the vague terminology, it's 3am :) ).
My aim with the refactoring is to separate concerns; testability is not an objective nor would it be implemented.
I personally disagree with putting much in my ViewModels beyond the stuff that is pertinent to the View (it is, after all, a model of a View!)
So I use a Controller paradigm whereby when the View tells the ViewModel to perform some action (via a Command usually) and the ViewModel uses a Command class to perfrom actions, such as saving the data, instantiating new View/Viewmodel pairs etc.
I also actually separate my ViewModel and ViewData (the ViewModel 'contains' the ViewData) so the ViewData is puirely dealing with the data, the ViewModel with some logic and command handling etc.
I wrote about it here
What you need is called Commanding in WPF.
Basically you bind Button.Command to a ICommand property in your ViewModel and when Button is clicked you get a notification in ViewModel without using code behind and casing DataContext or whathever hacks you tried.
http://msdn.microsoft.com/en-us/library/ms752308.aspx

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.

WPF/C# Forwarding events and DataProvider values within a UserControl

I have a UserControl which contains 4 ToggleButtons and I'd like to trigger a custom event that an interested object can listen for which provides a status, based on the ToggleButton Checked values and also value(s) from the DataContext object.
Getting the ToggleButton checked values and deriving a status is simple enough, however I can't work out how I access the DataContext object within the C# codebehind.
For example, if an interested object receives the RoutedEvent from the UserControl, I would like it to be able to access values from the UserControl's DataContext object.
Will I need to expose specific properties from the DataContext object or can I somehow expose the DataContext object from the UserControl's API?
Update.
To explain the problem a little more, I have a list of items which creates a set of UserControl instances in a container, I attach event listeners to each item as it's added to the container and send an event from one of the UserControls when it's child controls are clicked / checked etc.
Getting a reference to the UserControl that dispatched the event is straightforward enough, but I can't access the DataContext object, do I need to assign a public property to expose the DataContext object ...
e.g.
private ControlViewModel myControlViewModel;
public ControlViewModel MyControlViewModel {
get { return myControlViewModel; }
set
{
this.DataContext = value;
myControlViewModel = value;
}
}
or is there a better way?
Any tips would be appreciated, Thank you.
Well, it looks like I should've tried the simplest solution first...
...of course I can access the DataContext object like this:
(userControl.DataContext as ControlViewModel).requiredProperty;
Update
So I ended up passing the DataContext view model reference via a event/delegate pair like this...
public delegate void StatusChangedHandler(string status, UserControlViewModel model);
public event StatusChangedHandler StatusChanged;
And then just invoked the event like this...
StatusChanged.Invoke("message", DataContext as UserControlViewModel)
// or DataContext as IUserControlModelInterface
Which allowed me to adequately aggregate events from the UserContol's child controls, and access the DataContext model from an event handler.
I still wonder if there is a more best practice way to do this?

Resources