24

I'm relatively new to WPF and MVVM and the hardest thing I have found is how to simply switch a usercontrol or a view in an application.

In winforms, to have a control remove itself you would simple say this.Parent.Controls.Remove(this);

In WPF there is no generic Parent control, you would have to typecast it to the specific type (i.e. Grid) and then remove it.

This also seems to break the MVVM architecture. I have also tried data templates and content presenters, which work well, except for the fact that I can't change the datacontext from code, since the datacontext is always the viewmodellocator.

Are pages the acceptable way to do this in WPF now? What if I had a grid with a custom usecontrol and I wanted to switch it based on some variable in the viewModel? It seems like the simplest tasks cannot be accomplished easily in WPF.

JReed
  • 243
  • 1
  • 2
  • 5

1 Answers1

40

You would do so in your parent ViewModel.

For example, if your page (call it PageViewModel) had two views (ViewModelA and ViewModelB), you would have a property on PageViewModel called CurrentView, and this would determine which View is visible. When PageViewModel.CurrentView is set to an instance of ViewModelA, then ViewA's DataTemplate is used to draw the content. When it's set to an instance of ViewModelB, ViewB's DataTemplate is displayed.

<DataTemplate DataType="{x:Type local:PageViewModel}">
    <ContentControl Content="{Binding CurrentView}" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:ViewModelA}">
    <TextBlock Text="I'm ViewModelA" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:ViewModelB}">
    <TextBlock Text="I'm ViewModelB" />
</DataTemplate>

It would be ideal to call the switch views command from the parent view (in this case the DataTemplate for the PageViewModel), however if you wanted to switch views from within ViewModelA/B, you can either hook up the event manually when the objects get created (CurrentView.ChangeViewCommand = this.ChangeViewCommand) or look into a messaging system. MVVM Light has a simple Messenger class which I found was fairly easy to use, or Prism has a more advanced EventAggregator

If you want to switch Views for the same ViewModel, I would recommend a Mode property that gets used to determine which view to use. For example:

<DataTemplate x:Key="ViewA" DataType="{x:Type local:MyViewModel}">
    <TextBlock Text="I'm ViewModelA" />
</DataTemplate>

<DataTemplate x:Key="ViewB" DataType="{x:Type local:MyViewModel}">
    <TextBlock Text="I'm ViewModelB" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:MyViewModel}">
    <ContentControl Content="{Binding }">
        <ContentControl.Style>
            <Style TargetType="{x:Type ContentControl}">
                <Setter Property="ContentTemplate" Value="{StaticResource ViewA}" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Mode}" Value="2">
                        <Setter Property="ContentTemplate" Value="{StaticResource ViewB}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
    </ContentControl>
</DataTemplate>

EDIT

I actually see this kind of question come up a lot, so posted something about it here if anyone is interested

acidbabies
  • 97
  • 7
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • This is pretty much what I was looking for. If I understand correctly, you can use DataTriggers to bind to a value and then switch the content on the fly. Does this work for other controls such as ContentPresenter? – JReed May 25 '11 at 14:02
  • @JReed Sure, I don't see why not. In the 2nd example I'm switching the `ContentTemplate` property of a `ContentControl`, but you can switch out almost any property on any control based on a Trigger – Rachel May 25 '11 at 14:27
  • What if either viewModel needs some data before initialisation so they don't have a parameter-less constructor? – wingerse Jan 06 '16 at 20:49
  • 1
    @EmpereurAiman Then whatever code is responsible for creating the VM should pass it the correct parameters :) – Rachel Jan 06 '16 at 20:52
  • My bad, I meant the view (From your article). You associate the viewModels with views using a DataTemplate. But it only works if the view has a parameterless constructor. How can I fix it? – wingerse Jan 06 '16 at 21:00
  • 1
    @EmpereurAiman The `DataTemplate` tells WPF how to draw the ViewModel. It does not actually create it. That's the responsibility of something else... typically for me if it's the highest level in my application it will be the code behind, otherwise it will be another ViewModel. There's a more complete code sample [here](http://stackoverflow.com/a/9331487/302677) if you want. – Rachel Jan 07 '16 at 04:18
  • 1
    Thanks Rachel. I checked your blogs and the view-model first approach you use solved a lot of problems for me. :) – wingerse Jan 07 '16 at 15:14