Reputation: 2689
I am learning WPF and MVVM at the moment (or at least I am trying to...).
I created a little sample-app, that shows a Window with 2 buttons, each of it should show a new View on Click. So I created 3 UserControls (DecisonMaker with the 2 Buttons, and one Usercontrol for each "clicktarget").
So I bound the CotentControl of the MainWindow to a property called "CurrentView" in my MainWindowViewModel
Code of MainWindow.xaml:
<Window x:Class="WpfTestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTestApplication"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding CurrentView, Mode=OneWay}" />
</Grid>
</Window>
Code of MainWindowViewModel:
class MainWindowViewModel
{
private UserControl _currentView = new DecisionMaker();
public UserControl CurrentView
{
get { return _currentView; }
set { _currentView = value; }
}
public ICommand MausCommand
{
get { return new RelayCommand(LoadMouseView); }
}
public ICommand TouchCommand
{
get { return new RelayCommand(LoadTouchView); }
}
private void LoadMouseView()
{
CurrentView = new UserControlMouse();
}
private void LoadTouchView()
{
CurrentView = new UserControlTouch();
}
}
The initial UserControl (DecisionMaker) shows up as supposed. Also the method LoadMouseView
is called. But the View doesn't change. What am I missing?
UPDATE: Thanks so much! I missed the INotifyPropertyChanged-interface. All of your answers were just great and very accurate and helpful! I don't know which one to accept - I think it's the most fair way to accept the "first" answer?
I accepted blindmeis answer, as it solved the problem and helped me understand MVVM better. But every answer was really great thanks to all of you!
Upvotes: 9
Views: 12771
Reputation: 22445
if you wanna do mvvm - then you should have no references to your view/usercontrols in your viewmodel. you have to implement INotifyPropertyChanged! ps: if you need System.Windows namespace in your Viewmodel - then something is wrong.
in your case what you need:
your mainviewmodel should have at least 2commands to switch your view and 1 property for CurrentView. in your command you simply set your CurrentView to the right viewmodel instance. at least you need two datatemplates for each viewmodel which define the right view.
public object CurrentView
{
get { return _currentView; }
set {
_currentView = value; this.RaiseNotifyPropertyChanged("CurrentView");}
}
xaml
<Window x:Class="WpfTestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTestApplication"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyMouseViewModel}">
<local:MyMouseUserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyTouchViewModel}">
<local:MyTouchUserControlView/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<!-- here your buttons with command binding, i'm too lazy to write this. -->
<!-- you content control -->
<ContentControl Content="{Binding CurrentView, Mode=OneWay}" />
</Grid>
</Window>
Upvotes: 7
Reputation: 15999
It sounds like the behaviour you want is pretty much what you get with a [TabControl][1]
- why not use this built in control and just bind the DataContext
of both tabs to the same view model.
This also has the advantage that your view model wouldn't know about the view classes (I am assuming that UserControlMouse
etc are user controls).
Note: this will not be applicable if you need the view model to be aware of whether it is in touch or mouse mode.
Upvotes: 1
Reputation: 6466
I would do something like this to select the input style that you want, to MainWindow I've added a property that lets me select the mode of input.
public enum UserInterfaceModes
{
Mouse,
Touch,
}
public UserInterfaceModes UserInterfaceMode
{
get { return (UserInterfaceModes)GetValue(UserInterfaceModeProperty); }
set { SetValue(UserInterfaceModeProperty, value); }
}
public static readonly DependencyProperty UserInterfaceModeProperty = DependencyProperty.Register("UserInterfaceMode", typeof(UserInterfaceModes), typeof(MainWindow), new UIPropertyMetadata(UserInterfaceModes.Mouse));
then for the xaml view part you can select the correct template with a trigger.
<Style TargetType="{x:Type local:MainWindow}">
<Style.Triggers>
<DataTrigger Binding="{Binding UserInterfaceMode}" Value="Mouse">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainWindow}">
<Grid Background="Red"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding UserInterfaceMode}" Value="Touch">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainWindow}">
<Grid Background="Blue"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Upvotes: 3
Reputation:
You need to implement INotifyPropertyChanged
on MainWindowViewModel
, such that the view is informed when the CurrentView
property is changed.
Upvotes: 1
Reputation: 84656
The viewmodel need to implement INotifyPropertyChanged
. Otherwise the view won't be notified when a property changes in the viewmodel.
class MainWindowViewModel : INotifyPropertyChanged
{
private UserControl _currentView = new DecisionMaker();
public UserControl CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 2