Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 不同视图/视图模型的WPF主菜单_C#_Wpf_Mvvm - Fatal编程技术网

C# 不同视图/视图模型的WPF主菜单

C# 不同视图/视图模型的WPF主菜单,c#,wpf,mvvm,C#,Wpf,Mvvm,我想创建一个应用程序,包含主菜单(ribbonmenu)和不同的用户控件,每个控件都分配给自己的ViewModel 我被告知不要在代码隐藏中实现经典事件,而是使用命令。到目前为止,一切正常,实现了所需方法的命令。 在我以前的方法中,我“加载”了UserControl,通过将相应的ViewModel分配给ContentControl,加载了UserControl,该控件在MainWindow.Resource中分配给了ViewModel 我的最后一种方法是通过按钮而不是菜单来简化: <Win

我想创建一个应用程序,包含主菜单(ribbonmenu)和不同的用户控件,每个控件都分配给自己的ViewModel

我被告知不要在代码隐藏中实现经典事件,而是使用命令。到目前为止,一切正常,实现了所需方法的命令。
在我以前的方法中,我“加载”了
UserControl
,通过将相应的ViewModel分配给
ContentControl
,加载了
UserControl
,该控件在
MainWindow.Resource
中分配给了ViewModel

我的最后一种方法是通过按钮而不是菜单来简化:

<Window.Resources>
    <DataTemplate x:Name="settingsViewTemplate" DataType="{x:Type viewmodels:SettingsViewModel}">
        <views:SettingsView DataContext="{Binding SettingsVM, Source={StaticResource Locator}}"/>
    </DataTemplate>
    <DataTemplate x:Name="projectsViewTemplate" DataType="{x:Type viewmodels:ProjectViewModel}">
        <views:ProjectView DataContext="{Binding ProjectVM, Source={StaticResource Locator}}"/>
    </DataTemplate>
</Window.Resources>

<StackPanel>
    <Button Content="Load Settings" Height="20" Margin="20 20 20 0" Click="ShowSettings"/>
    <ContentControl Margin="5" Height="100" Content="{Binding}"/>
</StackPanel>

如何使用ViewModel命令加载用户控件?

不要使用代码隐藏来处理视图模型。视图模型应处理视图模型。通常与实现这些命令的视图模型相同

首先为
main窗口
创建一个主视图模型作为数据源。此视图模型还将处理视图之间的切换。建议让所有页面视图模型实现一个通用的基本类型,例如
IPage

此外,在这种情况下,您不需要任何定位器。
DataTemplate
中的视图将自动将其
DataContext
设置为映射到
DataTemplate
的数据类型<代码>设置视图将自动将
设置视图模型
作为
数据上下文
。如果这是错误的上下文,那么您的模型设计是错误的

IPage.cs

    interface IPage : INotifyPropertyChanged
    {
      string PageTitel { get; set; }
    }
    class SettingsViewModel : IPage
    {
      ...
    }
    class ProjectViewModel : IPage
    {
      ...
    }
    public enum PageName
    {
      Undefined = 0, SettingsPage, ProjectPage
    }
设置viewmodel.cs

    interface IPage : INotifyPropertyChanged
    {
      string PageTitel { get; set; }
    }
    class SettingsViewModel : IPage
    {
      ...
    }
    class ProjectViewModel : IPage
    {
      ...
    }
    public enum PageName
    {
      Undefined = 0, SettingsPage, ProjectPage
    }
ProjectViewModel.cs

    interface IPage : INotifyPropertyChanged
    {
      string PageTitel { get; set; }
    }
    class SettingsViewModel : IPage
    {
      ...
    }
    class ProjectViewModel : IPage
    {
      ...
    }
    public enum PageName
    {
      Undefined = 0, SettingsPage, ProjectPage
    }
PageName.cs

    interface IPage : INotifyPropertyChanged
    {
      string PageTitel { get; set; }
    }
    class SettingsViewModel : IPage
    {
      ...
    }
    class ProjectViewModel : IPage
    {
      ...
    }
    public enum PageName
    {
      Undefined = 0, SettingsPage, ProjectPage
    }
MainViewModel.cs
可以在
Microsoft文档:模式-具有模型视图模型设计模式的WPF应用程序-

class MainViewModel:INotifyPropertyChanged
{
公共ICommand SelectPageCommand=>newrelayCommand(SelectPage);
公共词典页面{get;}
私人IPage selectedPage;
公共IPage Selected页面
{
get=>this.selectedPage;
设置
{ 
this.selectedPage=值;
OnPropertyChanged();
}
}
公共主视图模型()
{
this.Pages=新字典
{
{PageName.SettingsPage,新建SettingsViewModel()},
{PageName.ProjectPage,新建ProjectViewModel()}
};
this.SelectedPage=this.Pages.First().Value;
}
公共无效选择页(对象参数)
{
if(param是PageName PageName
&&this.Pages.TryGetValue(pageName,out-IPage selectedPage))
{
this.SelectedPage=SelectedPage;
}
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
this.PropertyChanged?.Invoke(this,newpropertychangedeventargs(propertyName));
}
}
main window.xaml

<Window>
  <Window.DataContext>
    <MainViewModel />
  </Window.DataContext>

  <Window.Resources>
    <DataTemplate x:Name="settingsViewTemplate" DataType="{x:Type viewmodels:SettingsViewModel}">
      <views:SettingsView />
    </DataTemplate>
    <DataTemplate x:Name="projectsViewTemplate" DataType="{x:Type viewmodels:ProjectViewModel}">
      <views:ProjectView />
    </DataTemplate>
  </Window.Resources>

  <StackPanel>

    <!-- Content navigation -->
    <StackPanel Orientation="Horizontal">
      <Button Content="Load Settings" 
              Command="{Binding SelectPageCommand}" 
              CommandParameter="{x:Static PageName.SettingsPage}" />
      <Button Content="Load Projects" 
              Command="{Binding SelectPageCommand}" 
              CommandParameter="{x:Static PageName.ProjectPage}" />
    </StackPanel>

    <ContentControl Content="{Binding SelectedPage}" />
  <StackPanel>
</Window>

短版本:

public class MyViewModel : ViewModel

      public MyViewModel()
      {
           View = new MyUserControlView();
           View.DataContext = this; // allow the view to bind to the viewModel.
      } 

      ....

      public UIElement View {
            get; private set;
      }
  }
然后在XAML中:

 <ContentControl Content={Binding View} />
然后使用绑定到Frame.View的ContentControl将其绑定到主机XAML中

更纯粹的方法是使用
DataTemplateSelector
类在DataTemplate中实例化用户控件。这可能是WPF设计者在WPF中连接视图和ViewModel时想到的方法。但它最终将视图和ViewModel的映射扩展到三个单独的文件中(自定义C#DataTemplateSelector实现;托管
窗口中广泛分离的静态资源声明和ContentControl包装器
/
页面
;以及DataTemplate资源本身,如果只有少量的ViewModel/视图绑定,它们最终会出现在资源文件中


我想,纯粹主义者会争辩说,让viewmodel创建一个视图有点不好。但是,让
DataTemplateSelector的工作分散到五个文件中的代码更不好,并且在试图通过
DataTemplate

只是一个简单的问题,为什么
SelectPage
是一个
对象而不是一个具体的
PageName
?它实际上是这样的。要使用命令,您需要实现定义
ICommand.Execute(对象)的
ICommand
方法。这意味着默认情况下,
CommandParameter
的类型为òbject
。您必须将参数强制转换为其真实类型。
RelayCommand`包装了
ICommand
的可重用实现。它将委托作为构造函数参数,在框架调用
ICommand.Exec时在内部调用ute
例如,当单击
按钮时。我在这里使用的
RelayCommand
是Microsoft Docs中的一个示例,它不是通用的。如果您按照我回答中的链接进行操作,您可以复制实现并轻松地将其转换为通用的
RelayCommand
,其中
T
命令参数的类型.Hi@BionicCode,谢谢你的示例。我想我理解这里发生的事情,但我无法让它工作。会触发RelayCommand的执行,并
this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
被击中。我在视图中没有任何反应,也没有错误。主窗口显示MainViewModel
WpfApp1.ViewModels.MainViewModel
的toString。因此我认为,我离解决方案不远。我如何调试它?哦,这样做对吗,每个命令都嵌套在MainViewModel中?是的,听起来像是logic本身正在工作。只有
DataTemplate
未呈现。请确保将模板添加到
ContentControl
ContentPresenter范围内的
ResourceDictionary