basti
basti

Reputation: 2689

Changing View on Buttonclick

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

Answers (5)

blindmeis
blindmeis

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:

  • 1 mainviewmodel
  • 1 viewmodel for UserControlMouse
  • 1 viewmodel for UserControlTouch
  • 1 view/usercontrol for UserControlMouse
  • 1 view/usercontrol for UserControlTouch

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

Steve Greatrex
Steve Greatrex

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

Andy
Andy

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

user128300
user128300

Reputation:

You need to implement INotifyPropertyChanged on MainWindowViewModel, such that the view is informed when the CurrentView property is changed.

Upvotes: 1

Fredrik Hedblad
Fredrik Hedblad

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

Related Questions