在 MVVM 中构建显示各种页面的菜单的最佳方法是什么?
-
06-07-2019 - |
题
我想使用 MVVM 模式构建一个简单的应用程序。
该应用程序将有两个主要部分:
- 菜单 在上面
- 内容 以下
导航将很简单:
- 每个 菜单项 (例如。“管理客户”或“查看报告”)将 填充内容区域 具有某些特定功能的新页面
我有 之前用代码完成过此操作 其中菜单项的代码隐藏事件处理程序已加载所有页面,并且应显示的页面作为 StackPanel 的子级加载。然而,这在 MVVM 中不起作用,因为您不想手动填充 StackPanel 但显示例如带有 DataTemplate 等的“PageItem”对象
那么,那些使用 MVVM 制作过类似这样的简单点击菜单应用程序的人,你们的基本应用程序结构是什么? 我沿着这些思路思考:
MainView.xaml:
<DockPanel LastChildFill="False">
<Menu
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
其中菜单填充了“PageItems”的集合,并且 DataTemplate 将每个“PageItem 对象”的标题显示为每个 MenuItem 的标题。
ContentControl 将填充具有完整功能的 View/ViewModel 对,但我对此不确定。
解决方案
首先,我认为您应该保留代码隐藏事件处理程序,没有任何实际原因将简单的 2 行事件处理程序更改为复杂的命令驱动怪物(并且不要说可测试性,这是主菜单,它每次运行应用程序时都会进行测试)。
现在,如果您确实想走纯 MVVM 路线,您所要做的就是让菜单触发命令,首先,在某些资源部分添加以下样式:
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding DataContext.SwitchViewCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter"
Value="{Binding}"/>
</Style>
此样式将使菜单项在附加视图模型上触发 SwitchViewCommand,并将 MenuItem 的 DataContext 作为命令参数。
实际视图与您的代码相同,但附加了对该样式作为 ItemContainerStyle 的引用(因此它适用于菜单项,而不适用于 DataTemplate 的内容):
<DockPanel LastChildFill="False">
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
ItemContainerStyle="{StaticResource MenuItemStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
现在在您需要的视图模型中(我使用字符串,因为我没有您的 PageItem 代码):
private string _selectedViewItem;
public List<string> PageItemsMainMenu { get; set; }
public string SelectedPageItem
{
get { return _selectedViewItem; }
set { _selectedViewItem = value; OnNotifyPropertyChanged("SelectedPageItem"); }
}
public ICommand SwitchViewCommand { get; set; }
并使用您使用的任何命令类来使命令调用以下代码:
private void DoSwitchViewCommand(object parameter)
{
SelectedPageItem = (string)parameter;
}
现在,当用户单击菜单项时,菜单项将调用 SwitchViewCommand,并将页面项作为参数。
该命令将调用 DoSwitchViewCommand 来设置 SelectedPageItem 属性
该属性将引发 NotifyPropertyChanged,这将通过数据绑定更新 UI。
或者,您可以编写一个 2 行事件处理程序,您可以选择
其他提示
我可以想象虚拟机中的 ObservableCollection,它包含可从菜单调用的所有页面。然后将 ItemsControl 和 ContentControl 绑定到它,使 ContentControl 始终显示该列表中的 CurrentItem。当然,菜单只会绑定到一些 Title 属性 而 ContentControl 将采用整个项目并根据类型插入一些适当的视图。
另一种选择是使用列表框而不是菜单,将列表框设置为看起来像菜单,然后您可以绑定到选定的值,如下所示:
<DockPanel LastChildFill="False">
<ListBox
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl
Content="{Binding PageItemsMainMenu/}"/>
</DockPanel>
请注意 IsSynchronizedWithCurrentItem="True" 以设置所选项目,并使用带有尾部斜杠的 {Binding PageItemsMainMenu/} 来使用它。