Max
Max

Reputation: 57

Button should open new ContentControl based on MVVM pattern in WPF C#

I wanted to create a project based on the MVVM pattern. Unfortunately, I didn't quite get it right and now I have numerous subsequent errors.

MainWindow.xaml

<Window x:Class="MyProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyProject"
        xmlns:viewmodels="clr-namespace:MyProject.ViewModels"
        xmlns:views="clr-namespace:MyProject.View"
        mc:Ignorable="d"
        Height="571.4" Width="730">

    <Window.Resources>
        <DataTemplate x:Name="Window1ViewTemplate" DataType="{x:Type viewmodels:Window1ViewModel}">
            <views:Window1View DataContext="{Binding}"/>
        </DataTemplate>
    </Window.Resources>

    <Grid>
        // Grid Definition not included - not relevant
        <DockPanel Background="Blue" Grid.Row="1" Grid.Column="0" Grid.RowSpan="3">
            <StackPanel>
                <Button Content="Window1" 
                        Height="45"
                        Click="Window1_click"/>
            </StackPanel>
        </DockPanel>

        <ContentControl Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="4" Content="{Binding}"/>

    </Grid>
</Window>

MainWindow.xaml.cs

namespace MyProject
{
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }


        private void Window1_click(object sender, RoutedEventArgs e)
        {
            DataContext = new Window1ViewModel();
        }

    }
}

I have a window which I named Window1. This one is not quite relevant I guess.

Now the Click Event button opens the window correctly. But this does not fit the MVVM pattern. This will start a new thread, which then leads to countless subsequent errors.

How can I adjust the button event so that the binding is correct again.

Upvotes: 0

Views: 329

Answers (1)

Peregrine
Peregrine

Reputation: 4556

Some suggestions for you.

  • Create a MainViewModel class, and set the DataContext of MainWindow to an instance of this.
  • If possible, don't use event handlers in MVVM, especially not for button click events. Instead, in MainViewModel, create a property of type ICommand, and bind the button's Command property to it.

Depending on what (library) you're using to implement the MVVM pattern, you may already have an ICommand implementation, such as MVVMLight's RelayCommand. If not, you can use a basic ICommand implementation such as

public class BasicCommand: ICommand
{
    private readonly Action _execute;

    public Command(Action execute)
    {
        _execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    public event EventHandler CanExecuteChanged;
}
  • In MainViewModel, create a property of type object ContentViewModel, and bind the ContenControl's Content property to it. This property should implement INotifyPropertyChanged as you're going to update it from within the ViewModel.

  • In the corresponding method for the command, set the ContentViewModel property to an appropriate value - e.g. a Window1ViewModel instance.

  • In the ContentControl resources, define a DataTemplate for each type you might want to display. This needs to be based on a something other than a Window - eg Grid or CustomControl or UserControl.

.

 <ContentControl
    Margin="8"
    Content="{Binding SelectedItem}">
    <ContentControl.Resources>
        <DataTemplate DataType="{x:Type vm:Window1ViewModel}">
            <ctrl:Window1Display DataContext="{Binding}" />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:Window2ViewModel}">
            <ctrl:Window2Display DataContext="{Binding}" />
        </DataTemplate>

        ...

    </ContentControl.Resources>
</ContentControl>

More details, and a working example on my blog post.

Upvotes: 1

Related Questions