【问题标题】:How can I switch between user controls in my MVVM project如何在我的 MVVM 项目中的用户控件之间切换
【发布时间】:2015-11-20 16:44:06
【问题描述】:

编辑:尽管@AbinMathew 已将此问题标记为this 的可能重复项,但为该问题提供的解决方案并未很好地解释如何传递命令逻辑。正如我的回答中提到的,我能够在 John Smith 的文章的帮助下解决这个问题。

我正在运行这个测试项目,以便了解 MVVM。我想要实现的目标:MainWindow 有一个后退按钮和一个 ContentControl。在 Window_loaded 上,我想在 ContentControl 中显示 MainGadget。当我在 MainGadget 中单击 MyBtn 时,我想在 ContentControl 中显示 MyGadget。

ViewModelBase 是 MainGadgetVM、MainWindowVM 和 MyGadgetVM 使用的类。它实现了 INotifyPropertyChanged 接口。 RelayCommand 实现了 ICommand 接口,所以我想用它来执行 MyBtn_Click 并显示其他 UserControl。

目前,当我运行程序时,只显示“返回”按钮。我似乎无法弄清楚如何显示其他用户控件。任何帮助将不胜感激。

DataTemplates.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vm="clr-namespace:ExampleContentCtrl.VMs"
                    xmlns:view="clr-namespace:ExampleContentCtrl.Panels">
    <DataTemplate DataType="{x:Type vm:MainGadgetVM}">
        <view:MainGadget/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MyGadgetVM}">
        <view:MyGadget/>
    </DataTemplate>

</ResourceDictionary>

MainWindow.xaml

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1"/>
    </Grid>
</Window>

MainGadget.xaml

<UserControl x:Class="ExampleContentCtrl.Panels.MainGadget"
             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" 
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Button x:Name="MyBtn" Content="My Gadget" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="1" Command="{Binding MyBtn_Click}"/>
    </Grid>

</UserControl>

MainWindow.xaml.cs

namespace ExampleContentCtrl
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Load MainGadgetVM via MainWindowVM.Initialize()
        }

    }
}

MainWindowVM.cs

namespace ExampleContentCtrl.VMs
{
    public class MainWindowVM : ViewModelBase
    {
        private RelayCommand _ShowWorkSpace;
        private static MainWindowVM _Instance;
        public static MainWindowVM Instance { get { return _Instance; } }

        public MainWindowVM()
        {
            MainWindowVM._Instance = this;
        }

        public RelayCommand ShowWorkSpace
        {
            get
            {
                if (_ShowWorkSpace == null)
                    _ShowWorkSpace = new RelayCommand(param => { });
                return _ShowWorkSpace;
            }
        }

        public void Initialize()
        {
            //this.ShowWorkSpace.Execute("ExampleContentCtrl.VMs.MainGadgetVM");
        }
    }
}

【问题讨论】:

标签: c# wpf mvvm binding inotifypropertychanged


【解决方案1】:

您可以在内容控件中使用样式,该样式将根据主 ViewModel 中的公共绑定属性切换其内容。

类似:

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1">
        <ContentControl.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding UserControlToShow}" Value="MainGadget">
                        <Setter Property="ContentControl.Content" Value="{StaticResource MainGadget}"/>
                    </DataTrigger>
                    <DataTrigger Biniding="{Binding UserControlToShow}" Value="MyGadget">
                        <Setter Property="ContentControl.Content" Value="{StaticResource MyGadget}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
        </ContentControl>
    </Grid>
</Window>

然后,更新您的 ResourceDictionary 以使 DateTemplates 具有键:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:vm="clr-namespace:ExampleContentCtrl.VMs"
                    xmlns:view="clr-namespace:ExampleContentCtrl.Panels">
    <DataTemplate DataType="{x:Type vm:MainGadgetVM}" x:Key="MainGadget">
        <view:MainGadget/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MyGadgetVM}" x:Key="MyGadget">
        <view:MyGadget/>
    </DataTemplate>
</ResourceDictionary>

现在,添加用作切换内容的触发器的属性:

   public class MainWindowVM : ViewModelBase
    {
        private RelayCommand _ShowWorkSpace;
        private static MainWindowVM _Instance;
        public static MainWindowVM Instance { get { return _Instance; } }

        private string _userControlToShow;
        public string UserControlToShow 
        {
            get { return _userControlToShow; }
            set
            {
                _userControlToShow = value;
                RaisePropertyChanged("UserControlToShow");
            }
        }

        public MainWindowVM()
        {
            MainWindowVM._Instance = this;
        }

        public RelayCommand ShowWorkSpace
        {
            get
            {
                if (_ShowWorkSpace == null)
                    _ShowWorkSpace = new RelayCommand(param => { });
                return _ShowWorkSpace;
            }
        }
    }

【讨论】:

  • 嗯,这是正确的,但使用样式来选择数据模板是完全没有必要的。 &lt;ContentControl Content="{Binding}" /&gt; 将选择正确的 DataTemplate,而不必弄乱样式和所有其他废话。但是,您可能必须从模板资源中删除 x:Key 才能使其正常工作。
  • @d.moncada 感谢您提出这个问题。不幸的是,这没有任何作用。运行程序时,和以前一样只显示返回按钮。
  • @您介意再详细说明一下吗?我刚刚将 d.moncada 建议的 ContentControl.Style 替换为您的建议,但仍然无法正常工作。谢谢
  • @BeeLabeille 只显示后退按钮,因为您需要添加机制以将 UserControlToShow 更改为您喜欢的任何内容。关于Will,如果Content绑定到Window的数据上下文,内容控件怎么知道使用哪个datatemplate?
  • @breeLabeille 按照这些指示,看看它在新项目中的工作原理gist.github.com/WillSullivan/f535ede65eda4a9d3632
【解决方案2】:

为您的内容控件添加绑定并将绑定值更改为您要显示的视图模型。

<Window x:Class="ExampleContentCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="375" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <ResourceDictionary Source="DataTemplates.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <Button x:Name="BckSpace" Content="Back" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"/>
        <ContentControl Grid.Row="1" Content={Binding Path=MyContent}/>
    </Grid>
</Window>


public class MainWindowVM  
{
    //...

    public MainViewModel
    {
        MyContent = new TheViewModelThatShouldBeShownAtStart();
    }

    public object MyContent
    {
        get; private set; // add Notification!
    }


    void FunctionCalledWhenButtonIsPressed()
    {
        if (...) // add your logic here
            MyContent = new VM1();
        else
            MyContent = new VM2();
    }

}

【讨论】:

  • 感谢这个简单而有效的答案。我仍然会继续选择我的答案作为解决方案,因为我在周末使用 John Smith 的文章解决了这个问题。
【解决方案3】:
Grid.Children.Clear();
Grid.Children.Add(new NextUserControl());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多