2017-03-10 89 views
1

我有一个WPF应用程序,并开始学习MVVM模式。如何使用MVVM在WPF主窗口中显示用户控件

我的目标是在我的应用程序中,主窗口有一个按钮。点击此按钮后,主窗口顶部会出现另一个窗口(或用户控件)。

这是MainWindow.xaml的代码

<Window x:Class="SmartPole1080.View.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core" 
    xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core" 
    xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb" 
    xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl" 
    xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View" 
    xmlns:view1="clr-namespace:SmartPole1080.View" 
    xmlns:behaviors="clr-namespace:SoltaLabs.Avalon.Core.Behaviors;assembly=SoltaLabs.Avalon.Core" 
    Title="Smart Pole" 
    WindowStartupLocation="CenterScreen" 
    Name="mainWindow" 
    behaviors:IdleBehavior.IsAutoReset="True" WindowState="Maximized" WindowStyle="None"> 
<Canvas Background="DarkGray"> 
    <!--Main Grid--> 
    <Grid Width="1080" Height="1920" Background="White" Name="MainGrid" 
      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <StackPanel Background="Black">     
      <Grid Background="#253341"> 
       <Grid.RowDefinitions> 
        <RowDefinition Height="5"/> 
        <RowDefinition Height="*"/> 
        <RowDefinition Height="5"/> 
       </Grid.RowDefinitions> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="5"/> 
        <ColumnDefinition Width="264"/> 
       </Grid.ColumnDefinitions> 

       <Grid Grid.Row="1" Grid.Column="1"> 
        <Button Tag="{StaticResource EmergencyImg}" Name="EmergencyButton" 
          Command="{Binding ShowEmergencyPanel}"> 
         <Image Source="{StaticResource EmergencyImg}" /> 
        </Button> 
       </Grid> 

       <!--Emergency Window Dialog--> 
       <Grid Name="EmergencyPanel"> 
        <view1:EmergencyInfo x:Name="EmergencyInfoPanel"/> 
       </Grid> 
      </Grid> 
     </StackPanel> 
    </Grid> 
    <!--Main Grid--> 
</Canvas> 

这是其它窗口(用户控制 - EmergencyInfo.xaml)

<UserControl x:Class="SmartPole1080.View.EmergencyInfo" 
     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:local="clr-namespace:SmartPole1080.View" 
     mc:Ignorable="d" 
     d:DesignHeight="1920" d:DesignWidth="1050" 
     x:Name="EmergencyInfoPanel"> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
    </Grid.ColumnDefinitions> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="50"/> 
     <RowDefinition Height="*"/> 
    </Grid.RowDefinitions> 

    <Border Grid.Row="0" BorderBrush="White" Background="White"> 
     <Button Background="White" BorderThickness="0" FontWeight="Bold" Foreground="Red" 
       HorizontalAlignment="Right" FontSize="25" Margin="0,0,15,0" 
       Command="{Binding HideEmergencyPanel}"> 
      close X 
     </Button> 
    </Border> 
    <Image Grid.Row="1" Source="{StaticResource EdenParkInfoImg}" HorizontalAlignment="Left" /> 
</Grid> 

欲使用MVVM模式实现此行为。我已经在按钮EmergencyButton中设置了绑定ShowEmergencyPanel,以便在单击此按钮时显示EmergencyInfo。

任何帮助,非常感谢。

+0

'EmergencyImg'的类型是什么? –

+0

@LeiYang'EmergencyImg'的类型是BitmapImage – Juniuz

回答

5

为什么不做导航,就像这样。为要注入的内容制作部分,以及您期望的任何类型的对象将其放入Windows.Resources中的DataTemplate中。

在主WINDO XAML

<Window.DataContext> 
     <local:MainWindowViewModel /> 
    </Window.DataContext> 

    <Window.Resources> 
     <DataTemplate DataType="{x:Type home:HomeViewModel}"> 
      <home:HomeView /> 
     </DataTemplate> 

     <DataTemplate DataType="{x:Type other:OtherViewModel}"> 
      <other:OtherView /> 
     </DataTemplate> 
    </Window.Resources> 

    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="auto" /> 
      <RowDefinition Height="*" /> 
     </Grid.RowDefinitions> 
     <Grid x:Name="Navigation"> 
      <StackPanel Orientation="Horizontal"> 
       <Button x:Name="HomeView" 
         Content="Home" 
         Margin="5" 
         Command="{Binding NavigationCommand}" 
         CommandParameter="home" /> 
       <Button x:Name="Menu" 
         Content="OtherView" 
         Margin="5" 
         Command="{Binding NavigationCommand}" 
         CommandParameter="Other" /> 
      </StackPanel> 
     </Grid> 
     <Grid x:Name="MainContent" 
       Grid.Row="1"> 
      <ContentControl Content="{Binding CurrentViewModel}" /> 
     </Grid> 

    </Grid> 

MainWindowViewModel可以是这个样子。

public class MainWindowViewModel : INotifyPropertyChanged 
    { 
     private OtherViewModel otherVM; 
     private HomeViewModel homeVM; 

     public DelegateCommand<string> NavigationCommand { get; private set; } 

     public MainWindowViewModel() 
     { 
      otherVM = new OtherViewModel(); 
      homeVM = new HomeViewModel(); 

      // Setting default: homeViewModela. 
      CurrentViewModel = homeVM; 

      NavigationCommand = new DelegateCommand<string>(OnNavigate); 
     } 

     private void OnNavigate(string navPath) 
     { 
      switch (navPath) 
      { 
       case "other": 
        CurrentViewModel = otherVM; 
        break; 
       case "home": 
        CurrentViewModel = homeVM; 
        break; 
      } 
     } 

     private object _currentViewModel; 
     public object CurrentViewModel 
     { 
      get { return _currentViewModel; } 
      set 
      { 
       if (_currentViewModel != value) 
       { 
        _currentViewModel = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 


     #region INotifyPropertyChanged 
     public event PropertyChangedEventHandler PropertyChanged = delegate { }; 
     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      PropertyChanged(this, new propertyChangedEventArgs(propertyName)); 
     } 
     #endregion 
    } 

哪里DelegateCommand你可以让你的,检查如何让RelayCommand(和通用的),或者使用棱镜有它自己的DelegateCommand。但是如果你想使用PRISM,它已经可以导航到地区,可以解决很多问题。检查Brian Lagunas的视频。

编辑: 这是显示/隐藏网格。在你的mainWindow中,你设置EmergencyInfo你可以用这种方式显示/隐藏网格。

在MainViewViewModel

使布尔属性IsVisible

private bool _isVisible; 
     public bool IsVisible 
     { 
      get { return _isVisible; } 
      set 
      { 
       _isVisible = value; 
       OnPropertyChanged(); 
      } 
     } 
在MainView.Resources

SET键BooleanToVisibilityConverter 类似:

<BooleanToVisibilityConverter x:Key="Show"/> 
要显示/隐藏组

和网格能见度:

<Grid x:Name="SomeGridName" 
       Grid.Row="1" 
       Grid.Colum="1" 
       Visibility="{Binding IsVisible,Converter={StaticResource Show}}"> 

A ND最后设置IsVisible属性一些切换按钮,只为真正之间

<ToggleButton IsChecked="{Binding IsVisible}" 
           Content="ShowGrid" /> 

这样开关/ false,那么显示/隐藏基于ISVISIBLE该网格的一部分,您可以控制可见性的onClick。希望有所帮助。

+0

导航是一个好主意,但是我正在使用的应用程序不需要导航,因为它以1080 x 1920的屏幕尺寸显示。 – Juniuz

+0

我已经为您提供了一些关于如何在显示/隐藏窗口的某些部分之间切换的基本知识。希望帮助:) –

+0

感谢您的洞察力。我尝试了你的建议。不幸的是,EmergencyInfo面板仍然没有出现。我在'MainWindow.xaml'中创建了一个ToggleButton,并连接了它设置面板可见性的click事件处理程序。在'MainWindow.xaml.cs'中,'EmergencyButton_Click'事件调用一个公共方法'SetPanelVisibility',它将一个布尔值作为参数。这个方法在我的'MainViewModel'里面被声明,其中'IsEmergencyPanelVisibile'属性被改变了。但是,'EmergencyInfo.xaml'用户控件仍然没有显示。有什么可能是错的? – Juniuz

0

您可以做的是,将Emergency usercontrol放置在MainGrid中,并通过Model属性控制其可见性。

<Canvas Background="DarkGray"> 
    <!--Main Grid--> 
    <Grid Width="1080" Height="1920" Background="White" Name="MainGrid" 
      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <StackPanel Background="Black">     
      <Grid Background="#253341"> 
       <Grid.RowDefinitions> 
        <RowDefinition Height="5"/> 
        <RowDefinition Height="*"/> 
        <RowDefinition Height="5"/> 
       </Grid.RowDefinitions> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="5"/> 
        <ColumnDefinition Width="264"/> 
       </Grid.ColumnDefinitions> 

       <Grid Grid.Row="1" Grid.Column="1"> 
        <Button Tag="{StaticResource EmergencyImg}" Name="EmergencyButton" 
          Command="{Binding ShowEmergencyPanel}"> 
         <Image Source="{StaticResource EmergencyImg}" /> 
        </Button> 
       </Grid> 


      </Grid> 
     </StackPanel> 

     <Grid Name="EmergencyPanel"> 
      <view1:EmergencyInfo x:Name="EmergencyInfoPanel" Visibility={Binding IsEmergencyPanelVisible,Converter={StaticResource BoolToVisibilityConverter}} DataContext={Binding} /> 
     </Grid> 
    </Grid> 
    <!--Main Grid--> 
</Canvas> 

通过为持有usercontrol的Grid设置适当的背景,可以实现模态窗口效果。 而您需要拥有IsEmergencyPanelVisible(默认值为false)是您的Model,并且此属性应在您的按钮单击命令中更改为true。

注:我想你对转换器很熟悉,所以我在我的解决方案中包含了转换器。

+0

我试过了你的建议,但'EmergencyInfo'视图在'IsEmergencyPanelVisible'设置为true时没有显示。我有一个'MainViewModel'类,在构造函数'EmergencyCommand'中初始化。像这样,'EmergencyCommand = new DelegateCommand(()=> _window.ToggleEmergency());'哪个_window的类型是'IMainWindow',它实现'ToggleEmergency'。在'MainWindow.xaml.cs'中,'ToggleEmergency'就像这样被调用'_mainVm.EmergencyInfoViewModel.IsEmergencyPanelVisible = true;'你知道可能丢失了什么吗? – Juniuz

+0

@Juniuz你确定,只有一个数据上下文存在? _mainVm是usercontrol的datacontext吗?你有没有实现INotifyPropertyChanged? – WPFUser

+0

我有一堆'DataContext',其中EmergencyInfo视图有它自己的视图模型'EmergencyInfoViewModel'。变量'_mainVm'的类型为'MainViewModel',它在MainWindow.xaml.cs构造函数中初始化,并且包含在应用程序中使用的许多其他视图模型。 'INotifyPropertyChanged'已经通过一个'ObservableObject'实现,该'ObservableObject'采用泛型类型T.并且'_mainVm'不是用户控件(EmergencyInfo.xaml)的datacontext。我有没有想到在绑定方面非常重要的事情? – Juniuz

2

内,您的窗口XAML:

<ContentPresenter Content="{Binding Control}"></ContentPresenter> 

这样,您的视图模型必须包含控件属性。

将您的ViewModel添加到Window的DataContext。 (例如,在窗口构造,this.Datacontext = new ViewModel();

或者另一种方式与接口:(?这样一来,当你想使用初始化窗口服务)

public interface IWindowView 
{ 
     IUserControlKeeper ViewModel { get; set; } 
} 

public interface IUserControlKeeper 
{ 
     UserControl Control { get; set; } 
} 


public partial class YourWindow : IViewWindow 
{ 
     public YourWindow() 
     { 
      InitializeComponent(); 
     } 

     public IUserControlKeeper ViewModel 
     { 
      get 
      { 
       return (IUserControlKeeper)DataContext; 
      } 

      set 
      { 
       DataContext = value; 
      } 
     } 
    } 

private IViewWindow _window; 
private IViewWindow Window //or public... 
{ 
    get{ 
     if(_window==null) 
     { 
      _window = new YourWindow(); 
      _window.ViewModel = new YourViewModel(); 
     } 
     return _window; 
    } 
} 

用你的一个UserControl打开你的窗口:

void ShowWindowWithControl(object ControlView, INotifyPropertyChanged ControlViewModel, bool ShowAsDialog) 
    { 
     if(ControlView is UserControl) 
     {  //with interface: Window.ViewModel.Control = ... 
       (Window.DataContext as YourViewModel).Control = (UserControl)ControlView; 

       if (ControlViewModel != null) 
        (Window.DataContext as YourViewModel).Control.DataContext = ControlViewModel; 

       if (ShowAsDialog) //with interface use cast to call the show... 
        Window.ShowDialog(); 
       else 
        Window.Show(); 

     } 
    }