2

Edit: Although this question has been flagged up by @AbinMathew as a possible duplicate of this, the solution provided to that question didn't explain very well how to relay the command logic. I was able to resolve this with the help of John Smith's article as mentioned in my answer.

I've got this test project which I'm running in order to get my head around MVVM. What I'm trying to achieve: MainWindow has a back button and a ContentControl. On Window_loaded I want to display MainGadget in the ContentControl. When I click MyBtn in MainGadget, I want to then display MyGadget in the ContentControl.

ViewModelBase is a class which is used by MainGadgetVM, MainWindowVM and MyGadgetVM. It implements the INotifyPropertyChanged interface. RelayCommand implements the ICommand interface so I want to use it for executing MyBtn_Click and displaying other UserControls.

At the moment, when I run the program only the 'Back' button is displayed. I can't seem to figure out how to display the other UserControls. Any help will be much appreciated.

Solution Explorer

DataTemplates.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vm="clr-namespace:ExampleContentCtrl.VMs"
                    xmlns:view="clr-namespace:ExampleContentCtrl.Panels">
    <DataTemplate DataType="{x:Type vm:MainGadgetVM}">
        <view:MainGadget/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MyGadgetVM}">
        <view:MyGadget/>
    </DataTemplate>

</ResourceDictionary>

MainWindow.xaml

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1"/>
    </Grid>
</Window>

MainGadget.xaml

<UserControl x:Class="ExampleContentCtrl.Panels.MainGadget"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Button x:Name="MyBtn" Content="My Gadget" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="1" Command="{Binding MyBtn_Click}"/>
    </Grid>

</UserControl>

MainWindow.xaml.cs

namespace ExampleContentCtrl
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Load MainGadgetVM via MainWindowVM.Initialize()
        }

    }
}

MainWindowVM.cs

namespace ExampleContentCtrl.VMs
{
    public class MainWindowVM : ViewModelBase
    {
        private RelayCommand _ShowWorkSpace;
        private static MainWindowVM _Instance;
        public static MainWindowVM Instance { get { return _Instance; } }

        public MainWindowVM()
        {
            MainWindowVM._Instance = this;
        }

        public RelayCommand ShowWorkSpace
        {
            get
            {
                if (_ShowWorkSpace == null)
                    _ShowWorkSpace = new RelayCommand(param => { });
                return _ShowWorkSpace;
            }
        }

        public void Initialize()
        {
            //this.ShowWorkSpace.Execute("ExampleContentCtrl.VMs.MainGadgetVM");
        }
    }
}
Community
  • 1
  • 1
BeeLabeille
  • 174
  • 1
  • 4
  • 16
  • http://stackoverflow.com/a/32127548/2470362 – Abin Aug 26 '15 at 15:12
  • 1
    possible duplicate of [Transitioning from View to View in WPF](http://stackoverflow.com/questions/32127220/transitioning-from-view-to-view-in-wpf) – Abin Aug 26 '15 at 15:12
  • @AbinMathew thanks for suggesting the above links. I'll give them a try and see how i get on. – BeeLabeille Aug 28 '15 at 10:50

3 Answers3

2

Add a binding to your content control and change the bound value to the view model you want to show.

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1" Content={Binding Path=MyContent}/>
    </Grid>
</Window>


public class MainWindowVM  
{
    //...

    public MainViewModel
    {
        MyContent = new TheViewModelThatShouldBeShownAtStart();
    }

    public object MyContent
    {
        get; private set; // add Notification!
    }


    void FunctionCalledWhenButtonIsPressed()
    {
        if (...) // add your logic here
            MyContent = new VM1();
        else
            MyContent = new VM2();
    }

}
Onur
  • 5,017
  • 5
  • 38
  • 54
  • thanks for this simple yet effective answer. I'll still go ahead and select my answer as the solution because I solved this problem over the weekend using John Smith's article. – BeeLabeille Sep 01 '15 at 10:14
0

You can use a style inside your content control that will switch its content based on a common bound property within your main ViewModel.

Something like:

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1">
        <ContentControl.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding UserControlToShow}" Value="MainGadget">
                        <Setter Property="ContentControl.Content" Value="{StaticResource MainGadget}"/>
                    </DataTrigger>
                    <DataTrigger Biniding="{Binding UserControlToShow}" Value="MyGadget">
                        <Setter Property="ContentControl.Content" Value="{StaticResource MyGadget}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
        </ContentControl>
    </Grid>
</Window>

Then, update your ResourceDictionary so that the DateTemplates have keys:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vm="clr-namespace:ExampleContentCtrl.VMs"
                    xmlns:view="clr-namespace:ExampleContentCtrl.Panels">
    <DataTemplate DataType="{x:Type vm:MainGadgetVM}" x:Key="MainGadget">
        <view:MainGadget/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MyGadgetVM}" x:Key="MyGadget">
        <view:MyGadget/>
    </DataTemplate>
</ResourceDictionary>

Now, add the property that is used as the trigger to switch content:

   public class MainWindowVM : ViewModelBase
    {
        private RelayCommand _ShowWorkSpace;
        private static MainWindowVM _Instance;
        public static MainWindowVM Instance { get { return _Instance; } }

        private string _userControlToShow;
        public string UserControlToShow 
        {
            get { return _userControlToShow; }
            set
            {
                _userControlToShow = value;
                RaisePropertyChanged("UserControlToShow");
            }
        }

        public MainWindowVM()
        {
            MainWindowVM._Instance = this;
        }

        public RelayCommand ShowWorkSpace
        {
            get
            {
                if (_ShowWorkSpace == null)
                    _ShowWorkSpace = new RelayCommand(param => { });
                return _ShowWorkSpace;
            }
        }
    }
d.moncada
  • 16,900
  • 5
  • 53
  • 82
  • Well, that's correct but using a style to choose a data template is completely unnecessary. `` will pick up the correct DataTemplate without having to mess with styles and all that other guff. You may have to remove the x:Key from the template resources in order to get it to work, however. –  Aug 26 '15 at 15:55
  • @d.moncada thanks for putting this up. Unfortunately this doesn't do anything. When running the program, only the back button is displayed as before. – BeeLabeille Aug 26 '15 at 16:04
  • @Will do you mind elaborating a bit more? I have just replaced the ContentControl.Style which was suggested by d.moncada with your suggestion but still can't get it to work. Thanks – BeeLabeille Aug 26 '15 at 16:09
  • @BeeLabeille only the back button is displayed as before because you will need to add the mechanism to change UserControlToShow to whatever content you like. Regarding Will, if the Content is bound to the Window's data context, how would the content control know which datatemplate to use? – d.moncada Aug 26 '15 at 17:10
  • @breeLabeille Follow these directions and see how it works in a new project https://gist.github.com/WillSullivan/f535ede65eda4a9d3632 –  Aug 26 '15 at 17:24
  • @Will sorry for the late reply, it's been a mad couple of days. I'll give this a try and let you know how i get on – BeeLabeille Aug 28 '15 at 10:50
0
Grid.Children.Clear();
Grid.Children.Add(new NextUserControl());
Mahmoud Salah Eldin
  • 1,739
  • 16
  • 21