laserman
laserman

Reputation: 233

How to set datacontext to a different viewmodel

I have two viewmodels for the MainWindow. I want to switch between the viewmodels. Therefore I have a main viewmodel that allows me to switch between my first and my second viewmodel:

class MainViewModel : ViewModelBase
{
    private ViewModelBase _currentViewModel;

    public ViewModelBase CurrentViewModel
    {
        get
        {
            return _currentViewModel;
        }
        set
        {
            if (_currentViewModel == value)
                return;
            _currentViewModel = value;
            OnPropertyChanged("CurrentViewModel");
        }
    }

    public ICommand FirstViewCommand { get; private set; }
    public ICommand SecondViewCommand { get; private set; }
    private bool _canExecuteFirstViewCommand;
    private bool _canExecuteSecondViewCommand;

    public MainViewModel(Vokabel model)
    {
        VokabelViewModelDeutschLatein _vokabelViewModelDeutschLatein = new VokabelViewModelDeutschLatein(model);
        VokabelViewModelLateinDeutsch _vokabelViewModelLateinDeutsch = new VokabelViewModelLateinDeutsch(model);
        _canExecuteFirstViewCommand = true;
        _canExecuteSecondViewCommand = true;

        CurrentViewModel = _vokabelViewModelDeutschLatein;
        FirstViewCommand = new CommandHandler(() => ExecuteFirstViewCommand(_vokabelViewModelDeutschLatein),_canExecuteFirstViewCommand);
        SecondViewCommand = new CommandHandler(() => ExecuteSecondViewCommand(_vokabelViewModelLateinDeutsch), _canExecuteSecondViewCommand);
    }

    private void ExecuteFirstViewCommand(VokabelViewModelDeutschLatein _vokabelViewModelDeutschLatein)
    {
        CurrentViewModel = _vokabelViewModelDeutschLatein;
    }

    private void ExecuteSecondViewCommand(VokabelViewModelLateinDeutsch _vokabelViewModelLateinDeutsch)
    {
        CurrentViewModel = _vokabelViewModelLateinDeutsch;
    }
}

In the code behind of the MainWindow - how do I set the datacontext to the new viewmodel?

    public MainWindow()
    {
        Vokabel _Vokabel;
        MainViewModel _MainViewModel;

        InitializeComponent();
        _Vokabel = new Vokabel();
        _MainViewModel = new MainViewModel(_Vokabel.Initialize());
        this.DataContext = _MainViewModel;

    }

In the MainWindow.xaml I have a button that allows me to switch to the second viewmodel:

        <Button Grid.Column="1" Content="Deutsch => Latein" Command="{Binding SecondViewCommand}" />

If I run the application, the datacontext of the second (or the first) viewmodel is not present.

Upvotes: 1

Views: 1581

Answers (1)

Ilan
Ilan

Reputation: 2782

As I can understand you need to switch the data context on the fly, when the CurrentViewModel property is changed. I can suggest you the next solution. Use the ContentControl as the main content presenter and change its datatemplate that presents the current content defined by the CurrentViewModel property based on the data ot that property. Here is the working solution based on you code. 1. Xaml code:

<Window x:Class="SoContentControlBinding.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:soContentControlBinding="clr-namespace:SoContentControlBinding"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <soContentControlBinding:MainViewModel></soContentControlBinding:MainViewModel>
</Window.DataContext>
<Window.Resources>
    <DataTemplate DataType="{x:Type soContentControlBinding:ViewModelBase}">
        <TextBlock Background="Tomato" Text="{Binding Title}"></TextBlock>
    </DataTemplate>
    <DataTemplate DataType="{x:Type soContentControlBinding:VokabelViewModelDeutschLatein}">
        <TextBlock Background="GreenYellow" Text="{Binding Title}"></TextBlock>
    </DataTemplate>
    <DataTemplate DataType="{x:Type soContentControlBinding:VokabelViewModelLateinDeutsch}">
        <TextBlock Background="DeepSkyBlue" Text="{Binding Title}"></TextBlock>
    </DataTemplate>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="8*"></RowDefinition>
        <RowDefinition Height="2*"></RowDefinition>
    </Grid.RowDefinitions>
    <ContentControl Content="{Binding CurrentViewModel}"></ContentControl>
    <Button Grid.Row="1" Content="Deutsch => Latein" Command="{Binding SecondViewCommand}"></Button> 
</Grid>

2. View model code (chanhges are relates to my base observable object implementation):

public class MainViewModel : BaseObservableObject
{
    private ViewModelBase _currentViewModel;

    public ViewModelBase CurrentViewModel
    {
        get
        {
            return _currentViewModel;
        }
        set
        {
            if (_currentViewModel == value)
                return;
            _currentViewModel = value;
            OnPropertyChanged("CurrentViewModel");
        }
    }
    public ICommand FirstViewCommand { get; private set; }
    public ICommand SecondViewCommand { get; private set; }
    private bool _canExecuteFirstViewCommand;
    private bool _canExecuteSecondViewCommand;

    public MainViewModel(Vokabel model)
    {
        VokabelViewModelDeutschLatein vokabelViewModelDeutschLatein = new VokabelViewModelDeutschLatein(model);
        VokabelViewModelLateinDeutsch vokabelViewModelLateinDeutsch = new VokabelViewModelLateinDeutsch(model);
        _canExecuteFirstViewCommand = true;
        _canExecuteSecondViewCommand = true;

        CurrentViewModel = vokabelViewModelDeutschLatein;
        FirstViewCommand = new RelayCommand(() => ExecuteFirstViewCommand(vokabelViewModelDeutschLatein));
        SecondViewCommand = new RelayCommand(() => ExecuteSecondViewCommand(vokabelViewModelLateinDeutsch));
    }

    public MainViewModel():this(new Vokabel())
    {

    }

    private void ExecuteFirstViewCommand(VokabelViewModelDeutschLatein vokabelViewModelDeutschLatein)
    {
        CurrentViewModel = vokabelViewModelDeutschLatein;
    }

    private void ExecuteSecondViewCommand(VokabelViewModelLateinDeutsch vokabelViewModelLateinDeutsch)
    {
        CurrentViewModel = vokabelViewModelLateinDeutsch;
    }
}

3. Models code:

    public class VokabelViewModelLateinDeutsch:ViewModelBase
{
    public VokabelViewModelLateinDeutsch(Vokabel model)
    {
        Title = "VokabelViewModelLateinDeutsch";
    }
}

public class VokabelViewModelDeutschLatein : ViewModelBase
{
    public VokabelViewModelDeutschLatein(Vokabel model)
    {
        Title = "VokabelViewModelDeutschLatein";
    }
}

public class Vokabel
{
}

public class ViewModelBase:BaseObservableObject
{
    private string _title;

    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            OnPropertyChanged();
        }
    }
}

4. BaseObservableObject implementation (and othere mvvm parts):

    public class BaseObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
    {
        var propName = ((MemberExpression)raiser.Body).Member.Name;
        OnPropertyChanged(propName);
    }

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(name);
            return true;
        }
        return false;
    }
}

public class RelayCommand<T> : ICommand
{
    readonly Action<T> _execute;
    readonly Func<T, bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public void RefreshCommand()
    {
        var cec = CanExecuteChanged;
        if (cec != null)
            cec(this, EventArgs.Empty);
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null) return true;
        return _canExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
}

public class RelayCommand : RelayCommand<object>
{
    public RelayCommand(Action execute, Func<bool> canExecute = null)
        : base(_ => execute(),
            _ => canExecute == null || canExecute())
    {

    }
}
  1. View when is running: on running

    regards,

Upvotes: 1

Related Questions