C# 不同视图/视图模型的WPF主菜单
我想创建一个应用程序,包含主菜单(ribbonmenu)和不同的用户控件,每个控件都分配给自己的ViewModel 我被告知不要在代码隐藏中实现经典事件,而是使用命令。到目前为止,一切正常,实现了所需方法的命令。C# 不同视图/视图模型的WPF主菜单,c#,wpf,mvvm,C#,Wpf,Mvvm,我想创建一个应用程序,包含主菜单(ribbonmenu)和不同的用户控件,每个控件都分配给自己的ViewModel 我被告知不要在代码隐藏中实现经典事件,而是使用命令。到目前为止,一切正常,实现了所需方法的命令。 在我以前的方法中,我“加载”了UserControl,通过将相应的ViewModel分配给ContentControl,加载了UserControl,该控件在MainWindow.Resource中分配给了ViewModel 我的最后一种方法是通过按钮而不是菜单来简化: <Win
在我以前的方法中,我“加载”了
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));
被击中。我在视图中没有任何反应,也没有错误。主窗口显示MainViewModelWpfApp1.ViewModels.MainViewModel
的toString。因此我认为,我离解决方案不远。我如何调试它?哦,这样做对吗,每个命令都嵌套在MainViewModel中?是的,听起来像是logic本身正在工作。只有DataTemplate
未呈现。请确保将模板添加到ContentControl
或ContentPresenter范围内的ResourceDictionary