1

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.

dda
  • 6,030
  • 2
  • 25
  • 34
Shri
  • 63
  • 3
  • 9
  • You want to display the UserControl in the Window? – Joe Jan 16 '17 at 08:27
  • Yes.when am running the project , I need to see meainwindow above that my usercontrol need to be display. – Shri Jan 16 '17 at 08:56
  • In the same window, or a new window? – Joe Jan 16 '17 at 09:03
  • Yes same window – Shri Jan 16 '17 at 09:05
  • if you are using MVVM then you should probably be using templates not usercontrols, user controls are only required when you need a code behind file – MikeT Jan 16 '17 at 09:23
  • @MikeT I disagree. For a start almost all the MVVM frameworks I've seen use UserControls. Having a code-behind file has very little overhead, even if you don't use it. – Joe Jan 16 '17 at 10:19
  • Are you asking anything different from your last question: http://stackoverflow.com/questions/41633217/how-to-open-child-window-in-mainwindow-in-mmvm-wpf. I notice you didn't accept any answers from that, or even make any comment on any answers. – Joe Jan 16 '17 at 11:46

3 Answers3

2

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.

Shakra
  • 501
  • 2
  • 8
  • what I need to mention binding property "youtypehere" and "". – Shri Jan 16 '17 at 08:57
  • @Shri yep just some pseudo-cod, forget to describe :(, 'youtypehere' is property on main viewmodel and must be of type 'firstViewModelType' or 'secondViewModelType'. – Shakra Jan 16 '17 at 10:37
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.

1

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.

Joe
  • 6,773
  • 2
  • 47
  • 81
  • Good answer but you have missed a few bits like, templating and adding the namespace – MikeT Jan 16 '17 at 09:20
  • What do you mean by Templating? Something like @Shakra's answer? If so, I mentioned that in the answer. Not sure you need to Template if you just have a single view, seemed that was more complex than the asker wanted. I didn't say how to add the namespace as the question contains it - but I've added it in case someone else stumbles on this and gets confused. – Joe Jan 16 '17 at 10:16