2017-03-10 41 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(); 

     } 
    } 
相關問題