在 MVVM 中构建显示各种页面的菜单的最佳方法是什么?

发布于 2024-07-25 10:48:33 字数 984 浏览 12 评论 0原文

我想使用 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 对,但我对此不确定。

I want to build a simple application with the MVVM pattern.

This application will have two main parts:

  • menu on top
  • content below

The navigation will be simple:

  • each menu item (e.g. "Manage Customers" or "View Reports") will fill the content area with a new page that has some particular functionality

I have done this before with code behind where the code-behind event-handler for menu items had all pages loaded and the one that should be displayed was loaded in as a child of a StackPanel. This, however, will not work in MVVM since you don't want to be manually filling a StackPanel but displaying e.g. a "PageItem" object with a DataTemplate, etc.

So those of you who have made a simple click-menu application like this with MVVM, what was your basic application structure? I'm thinking along these lines:

MainView.xaml:

<DockPanel LastChildFill="False">

    <Menu 
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"/>

    <ContentControl 
        Content="{Binding SelectedPageItem}"/>        

</DockPanel>

where the Menu is filled with a collection of "PageItems" and the DataTemplate displays the Title of each "PageItem object" as the Header of each MenuItem.

And the ContentControl will be filled with a View/ViewModel pair which has full functionality, but am not sure on this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深海夜未眠 2024-08-01 10:48:33

首先,我认为您应该保留代码隐藏事件处理程序,没有任何实际原因将简单的 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>

此样式将使菜单项在使用 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 行事件处理程序,您可以选择

First, I think you should keep the code-behind event handler, there's no point in changing a simple 2 line event handler to a complex command driven monster for no practical reason (and don't say testebility, this is the main menu, it will be tested every time you run the app).

Now, if you do want to go the pure MVVM route, all you have to do it to make your menu fire a command, first, in some resource section add this style:

<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>

This style will make the menu item fire a the SwitchViewCommand on the attached view model with the MenuItem's DataContext as the command parameter.

The actual view is the same as your code with an additional reference to that style as the ItemContainerStyle (so it applies to the menu item and not the content of the DataTemplate):

<DockPanel LastChildFill="False">

    <Menu DockPanel.Dock="Top"
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"
        ItemContainerStyle="{StaticResource MenuItemStyle}"/>
    <ContentControl 
    Content="{Binding SelectedPageItem}"/>
</DockPanel>

Now in the view model you need (I used strings because I don't have your PageItem code):

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; }

And use whatever command class you use to make the command call this code:

private void DoSwitchViewCommand(object parameter)
{
    SelectedPageItem = (string)parameter;
}

Now, when the user clicks a menu item the menu item will call the SwitchViewCommand with the page item as the parameter.

The command will call the DoSwitchViewCommand that will set the SelectedPageItem property

The property will raise the NotifyPropertyChanged that will make the UI update via data binding.

Or, you can write a 2 line event handler, your choice

别把无礼当个性 2024-08-01 10:48:33

我可以想象虚拟机中的 ObservableCollection,它包含可从菜单调用的所有页面。
然后将 ItemsControl 和 ContentControl 绑定到它,使 ContentControl 始终显示该列表中的 CurrentItem。
当然,菜单只会绑定到某些 Title 属性
而 ContentControl 将采用整个项目并根据类型插入一些适当的视图。

i could imagine an ObservableCollection in the VM, that holds all the pages to be callable from the menu.
Then bind an ItemsControl And the ContentControl to it to make the ContentControl always show the CurrentItem from that List.
Of course, the menu will only bind to some Title property
whereas the ContentControl will adopt the whole item and plug in some appropriate view according to the type.

野侃 2024-08-01 10:48:33

另一种选择是使用 ListBox 而不是菜单,将 ListBox 设置为看起来像菜单,然后您可以绑定到所选值,如下所示:

<DockPanel LastChildFill="False">

    <ListBox 
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"
        IsSynchronizedWithCurrentItem="True"/>

    <ContentControl 
        Content="{Binding PageItemsMainMenu/}"/>        

</DockPanel>

注意 IsSynchronizedWithCurrentItem="True" 以设置所选项目和 {Binding PageItemsMainMenu /} 加上尾部斜杠即可使用它。

Another option is to use a ListBox instead of a menu, style the ListBox to look like a menu and then you can bind to the selected value, like this:

<DockPanel LastChildFill="False">

    <ListBox 
        ItemsSource="{Binding PageItemsMainMenu}" 
        ItemTemplate="{StaticResource MainMenuStyle}"
        IsSynchronizedWithCurrentItem="True"/>

    <ContentControl 
        Content="{Binding PageItemsMainMenu/}"/>        

</DockPanel>

Note the IsSynchronizedWithCurrentItem="True" to set the selected item and the {Binding PageItemsMainMenu/} with the trailing slash to use it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文