Shri
Shri

Reputation: 73

How to load usercontrol in Mainwindow in MVVM WPF?

I have a MainWindow.Xaml file. And one usercontrol PatientWindow.Xaml. How to load the patient window in mainwindow in MVVM architecture?

MainWindow.Xaml

<Window x:Class="PatientAdminTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:vm="clr-namespace:PatientAdminTool.ViewModel"
         xmlns:v="clr-namespace:PatientAdminTool.View"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        ResizeMode="CanResizeWithGrip" 
        WindowStyle="None"  
        WindowState="Normal"  
        Title="PatientAdmin Tools"           
        Height="750"
        Width="1400"
        AllowsTransparency="True" MouseLeftButtonDown="OnMouseLeftButtonDown" BorderThickness="1"  BorderBrush="#555252"  >  
    <WindowChrome.WindowChrome>
        <WindowChrome 
        CaptionHeight="0"     
          />
    </WindowChrome.WindowChrome>
</Window>

PatientWindow.xaml

Usercontrol window is mentioned in below

<UserControl x:Class="PatientAdminTool.View.PatientWindow"
             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" 
             xmlns:vm="clr-namespace:PatientAdminTool.ViewModel"
         xmlns:v="clr-namespace:PatientAdminTool.View"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" >

    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <DockPanel Grid.Row="0" LastChildFill="True" Height="40" Background="#646161" >
            <StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
                <TextBlock Margin=" 10,5,0,0" HorizontalAlignment="Center" Text="Patients" FontSize="16"  TextAlignment="Center" VerticalAlignment="Center" Foreground="#FFFFFF"/>
            </StackPanel>
            <StackPanel Margin="10,10,0,0" DockPanel.Dock="Right" Background="Transparent"  Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" >
                <StackPanel Background="Transparent"  Orientation="Horizontal" HorizontalAlignment="Right">
                    <Button Focusable="False" ToolTip="Close" VerticalAlignment="Center" Background="#646161" BorderThickness="0" BorderBrush="Transparent" Padding="-4" Click="Button_Click" >
                        <Button.Content>
                            <Grid Width="45" Height="23">
                                <TextBlock Foreground="White"   Text="r" FontFamily="Marlett" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </Grid>
                        </Button.Content>
                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <ContentPresenter Content="{TemplateBinding Content}"/>
                            </ControlTemplate>
                        </Button.Template>

                    </Button>
                </StackPanel>
            </StackPanel>
        </DockPanel>
    </Grid>
</UserControl>

So I need to load the patient window above of main window using MVVM. Here I need to write load event in corresponding View Model. Please help me to do this.

Upvotes: 1

Views: 4399

Answers (3)

Joe
Joe

Reputation: 7004

If it's the only thing you are going to be displaying in the same window, just put your view in there like any other control:

<Window x:Class="PatientAdminTool.MainWindow"
        xmlns:v="clr-namespace:PatientAdminTool.View"... >  
    <WindowChrome.WindowChrome>
        <WindowChrome 
        CaptionHeight="0"/>
    </WindowChrome.WindowChrome>
    <v:PatientWindow/>
</Window>

Do you have a root ViewModel for your window? If so, you can bind to a PatientViewModel:

<v:PatientWindow DataContext="{Binding PatientViewModel}"/>

If not, it's common to set the first DataContext to a ViewModel in the code-behind like this:

<v:PatientWindow Name="PatientWindowView"/>

and:

public partial class Window
{
    public MainWindow()
    {
        InitializeComponent();
        PatientWindowView.DataContext = new PatientWindowViewModel();

    }
}

If you want to display more than one View, use a ContentPresenter like Shakra has answered.

If you want to open a new window, use what Alberto Cardona López has suggested.

Upvotes: 1

So you want to open a new child window using MVVM? I assume you would open it from MainWindowViewModel.

Solution 1: without strict MVVM

Sometimes its fine to open it directly from the ViewModel:

private void OnOpenPatientWindowCommandExecute()
{
    var o = new PatientWindow();
    o.ShowDialog(); 
}

For this you would have to change PatientWindow from UserControl to a Window.

Solution 2: strict MVVM

The solutions following strict MVVM are a little more complex. In the solution I write here you would have to use a Service, add it to your MainWindowViewModel and bind a control from the view to a Command in the ViewModel. Also, its written as if you are using Dependency Injection, that's why you see the service injected in the constructor. You can avoid this by just instantiating the service in the constructor.

MainWindowViewModel.cs

using Prism.Wpf.Commands; // For easy commands
using PatientAdminTool.Services; // Where you put your new service

public class MainWindowViewModel
{
   private IShowDialogService _ShowDialogService;

   public MainWindowViewModel(IShowDialogService showDialogService)
   {
       _ShowDialogService = showDialogService;

       // Or do: _ShowDialogService = new ShowDialogService();
       // But that's not a good practice and won't let you test
       // this ViewModel properly.

       OpenPatientWindowCommand = new DelegateCommand(OnOpenPatientWindowCommandExecute);
   }

   public ICommand OpenPatientWindowCommand { get; private set; }

   private void OnOpenPatientWindowCommandExecute()
   {
       _ShowDialogService.ShowPatientWindow();
   }
}

Services\IShowDialogService.cs

public interface IShowDialogService
{
    void ShowPatientWindow();

    void ShowOtherWindow();

    // ...
}

Services\ShowDialogService.cs

public class ShowDialogService : IShowDialogService
{
    public void ShowPatientWindow()
    {
        var patientWindowViewModel = new PatientWindowViewModel();
        var patientWindow = new PatientWindow();

        patientWindow.DataContext = patientWindowViewModel;

        patientWindow.ShowDialog();
    }

    public void ShowOtherWindow()
    {
        // Other window ...
    }
}

Finally, you make the connection in the View like this:

MainWindow.xaml

<Window
    xmlns:vm="clr-namespace:PatientAdminTool.ViewModel">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Button Command="{Binding OpenPatientCommand}">Open Patient</Button>
    </Grid>
</Window>

Haven't tried it in Visual Studio yet but that's the idea.

Upvotes: 1

Shakra
Shakra

Reputation: 501

Just add a ControlPresenter at MainWindow.

<ContentPresenter Content="{Binding YouTypeHere}">
             <ContentPresenter.Resources>
                 <DataTemplate DataType="{x:Type fristViewModel1Type}">
                     <youControlForViewModel1 />
                 </DataTemplate>
                 <DataTemplate DataType="{x:Type secondViewModel2Type}">
                     <youControlForViewModel2 />
                 </DataTemplate>
            </ContentPresenter.Resources>
         </ContentPresenter>

And you could change Views via binding different VM to ContentPresenter.

Upvotes: 2

Related Questions