如何使用MVVM在WPF的主窗口中显示用户控件

时间:2017-03-10 02:58:09

标签: c# .net wpf xaml mvvm

我有一个WPF应用程序,刚开始学习MVVM模式。

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

这是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。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:8)

为什么不进行导航,就像这样。为将要注入的conetent创建部分,以及您期望将它放在DataTemplate中的Windows.Resources中的任何类型的对象。

在主要的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。但是如果你想使用PRISM,它已经可以导航到区域,可以解决许多问题。查看Brian Lagunas的视频。

修改 这是显示/隐藏网格。在您设置了EmergencyInfo的mainWindow中,您可以通过这种方式显示/隐藏该网格。

在MainViewViewModel中的

创建一个bool属性IsVisible

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

设置为BooleanToVisibilityConverter的键 类似的东西:

 <BooleanToVisibilityConverter x:Key="Show"/>

以及要显示/隐藏设置可见性的网格:

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

最后将IsVisible属性设置为某些ToggleButton,只是在true / false之间切换

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

这样,您可以显示/隐藏基于IsVisible的网格部分,并控制onClick的可见性。希望有所帮助。

答案 1 :(得分:2)

在你的Window xaml中:

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

通过这种方式,您的ViewModel必须包含Control属性。

将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();

         }
  }

答案 2 :(得分:0)

您可以做的是,将紧急用户控件放在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)作为您的模型,并且应在按钮单击命令中将此属性更改为true。

注意:我猜你熟悉转换器,所以我在我的解决方案中加入了转换器。