Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the View for a ViewModel

Tags:

c#

mvvm

wpf

I am trying to implement the MVVM design patern for mt WPF application. In order to connect the view to the viewmodels, I use a ResourceDictionary (used in Application.Resources), that looks like

<DataTemplate DataType={x:Type viewmodel:SampleViewModel}>
    <view:SampleView1 />
</DataTemplate>

The view models are then simply put into content presenters to display them.

Now, when the user presses a button, I'd like to display SampleViewModel using a different view. How do I change the data template used for SampleViewModel?

like image 211
Jens Avatar asked Mar 15 '11 08:03

Jens


2 Answers

Less words more code. As far as you said, you have the class SampleViewModel. I added the property Title for demonstration and ViewType for identifying the correct view:

public enum ItemViewType { View1, View2 };

public class SampleViewModel 
{
    public string Title { get; set; }
    public ItemViewType ViewType { get; set; }
}

The DataTemplateSelector for two views depending on the ViewType property:

class ItemViewTemplateSelector : DataTemplateSelector
{
    public DataTemplate View1Template { get; set; }
    public DataTemplate View2Template { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var vm = item as SampleViewModel;
        if (vm == null)
            return null;

        switch (vm.ViewType)
        {
            case ItemViewType.View1:
                return View1Template;
            case ItemViewType.View2:
                return View2Template;
        }

        return null;
    }
}

Xaml code:

<Window.Resources>
    <DataTemplate x:Key="view1Template">
        <TextBlock Text="{Binding Title}" Foreground="Red"/>
    </DataTemplate>
    <DataTemplate x:Key="view2Template">
        <TextBox Text="{Binding Title}" />
    </DataTemplate>
    <local:ItemViewTemplateSelector x:Key="viewTemplateSelector"
                                    View1Template="{StaticResource view1Template}"
                                    View2Template="{StaticResource view2Template}"/>
</Window.Resources>

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<StackPanel>
    <Button Content="ChangeView" HorizontalAlignment="Center" Command="{Binding SwitchViewCommand}"/>
    <ContentControl  Content="{Binding ItemViewModel}" ContentTemplateSelector="{StaticResource viewTemplateSelector}"/>
</StackPanel>

The main part is in the class MainViewModel where I've put the logic for switching views:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        this.ItemViewModel = new SampleViewModel { Title = "Some title", ViewType = ItemViewType.View1 };

        this.SwitchViewCommand = new RelayCommand(() =>
        {
            this.ItemViewModel.ViewType = this.ItemViewModel.ViewType == ItemViewType.View1
                                            ? ItemViewType.View2
                                            : ItemViewType.View1;
            //The magic senquence of actions which forces a contentcontrol to change the content template
            var copy = this.ItemViewModel;
            this.ItemViewModel = null;
            this.ItemViewModel = copy;
        });
    }

    public RelayCommand SwitchViewCommand { get; set; }

    private SampleViewModel itemViewModel;

    public SampleViewModel ItemViewModel
    {
        get { return itemViewModel; }
        set
        {
            itemViewModel = value;
            RaisePropertyChanged("ItemViewModel");
        }
    }
}

The SwitchViewCommand can be any type of command, I use the command from the mvvmlight library.

Inside the handler of the command I change the type of viewmodel and update the property ItemViewModel in a tricky way because a ContentControl refreshes a view only if to change the Content property, and this property will not be changed unless you set a reference to different object.

I mean, even the code this.ItemViewModel = this.itemViewModel will not change the view. It's strange, but the workaround doesn't require much work.

like image 194
vortexwolf Avatar answered Oct 15 '22 11:10

vortexwolf


You can achieve this in many different ways depends upon the architecture you want.

  • You can write a custom DataTemplateSelector and use it on ContentControl.ContentTemplateSelector and choose those two templates appropriately
  • If this pattern of changing the view occures in many different places and more frequent UX, I would also recommend those two views toggled using a DataTemplate.DataTrigger based on a property in SampleViewModel [I am guessing you might have a distinguishing property in the ViewModel to know that state]
like image 40
Jobi Joy Avatar answered Oct 15 '22 10:10

Jobi Joy