Reputation: 575
I'm trying to implement the MVVM, so i dont know the following is correct. It seems that ViewModel is some kind of model of the view, so associations in view shall be shown in ViewModel, in that case there shall be some associations between ViewModels. so by creating some templates for ViewModel Types, it seems the application can work, here is some example code:
ViewModels:
public class SomeVm : INotifyPropertyChanged
{
public SomeVm()
{
SomeOtherVm = new SomeOtherVm();
}
public INotifyPropertyChanged SomeOtherVm { set; get; }
private int _a;
public int A
{
set {
_a= value;
B = value;
}
get { return _a; }
}
private int _b;
public int B
{
set
{
_b = value;
OnPropertyChanged("B");
}
get { return _b; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SomeOtherVm : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _c;
public int C
{
set
{
_c = value;
D = value;
}
get { return _c; }
}
private int _d;
public int D
{
set
{
_d = value;
OnPropertyChanged("D");
}
get { return _d; }
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And the View:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<wpfApplication1:SomeVm x:Key="SomeVm"/>
<DataTemplate DataType="{x:Type wpfApplication1:SomeVm}">
<StackPanel d:DesignWidth="339" d:DesignHeight="54">
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding A}" VerticalAlignment="Stretch"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding B}" VerticalAlignment="Stretch"/>
<ContentPresenter Content="{Binding SomeOtherVm}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type wpfApplication1:SomeOtherVm}">
<StackPanel d:DesignWidth="339" d:DesignHeight="54">
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding C}" VerticalAlignment="Stretch"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding D}" VerticalAlignment="Stretch"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentPresenter Content="{DynamicResource SomeVm}" />
</Grid>
</Window>
in this way all the views can be created in some Resource Dictionaries, so the question is: is it right to use MVVM like this? And if it is, what is the drawbacks?
Upvotes: 0
Views: 167
Reputation: 2068
Speaking in terms of ViewModel
nesting, this code looks correct at a glance. Bindings you set up in XAML are also correct.
Concerning drawbacks, I would refrain from creating wpfApplication1:SomeVm
in window resources. Usually DataContext
of the Window
is set to an instance of a WindowViewModel
, which would in turn hold a reference to SomeVm
. Imagine a class like this:
public class WindowViewModel
{
public SomeVM SomeVM{get; set;}
public string Title {get; set;} //other data to bind by window
//...
}
Then, while the window is initialized, DataContext
must be set to a ViewModel instance, e.g:
MainWindow.DataContext = new WindowViewModel();
In XAML you'd use bindings again:
<Grid>
<ContentPresenter Content="{Binding SomeVm}" />
</Grid>
I'd also recommend putting your implicit DataTemplates
in generic.xaml
dictionary, rather then within a window. This way you can reuse these templates in your whole app.
Moreover, it is far better to use a ViewModelBase class implementing common event handling, so that you don't need to reimplement INotifyPropertyChanged
. Also try to avoid "magic strings" in property change notification. Be better off using lambda based approach or the new Caller Info Attributes. I'm aware of that your example code is probably simplified, but I'm commenting on it as it is.
Upvotes: 1
Reputation: 18578
Usually ViewModel is supposed to be the DataContext for the whole view i.e it should be the entity incharge of providing Data to view to render itself and to listen to UI command, events and property change to interact with the Business Layer (model).
The way you implemented it is you have your VM as a resource and set it as content not DataContext for one contentpresented and for the scenerio you have mentioned it might work well. But you should set VM as the DataContext for the whole view so that all the elements in the view can bind to the properties in the VM to render their state.
In your scenerio, if you have to add one more UI element in you view apart from the ContentPresenter then again you will have to access your resource VM.
So if you set you VM instance as DataContext (like this.DataContext = new ViewModel()) and bind your contentpresenter Content to DataContext of view like Content={Binding} that will be more correct and will help you if you ever want to extend your view. Here is a nice msdn article regarding mvvm implementation http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
Thanks
Upvotes: 1