将不同的绑定应用到 Mahapps Metro HamburgerMenuIconItem 的 DataTemplate

Applying different bindings to a DataTemplate for Mahapps Metro HamburgerMenuIconItem

我在 MVVM WPF 应用程序中使用 Hamburger Menu,并使用 DataTemplate 将 Badge 控件应用到每个菜单项,如下所示:(摘自 https://github.com/MahApps/MahApps.Metro/issues/3800)。

`<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type Controls:HamburgerMenuIconItem}">
    <Grid Height="48">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Controls:Badged Grid.Column="0"
                         Badge="{Binding DataContext.TestCount, RelativeSource={RelativeSource AncestorType=UserControl}}"
                         BadgeBackground="Red"
                         HorizontalAlignment="Center"
                         VerticalAlignment="Center">
            <ContentControl Content="{Binding Icon}"
                            Margin="2"
                            Focusable="False"
                            IsTabStop="False" />
        </Controls:Badged>
        <TextBlock Grid.Column="1"
                   VerticalAlignment="Center"
                   FontSize="16"
                   Text="{Binding Label}" />
    </Grid>
</DataTemplate>`

这很好用,但这对所有菜单项应用了相同的值 - 在这个例子中,我绑定到来自 MessagingMainViewModel 的 属性 命名的 TestCount(包含 HamburgerMenu 标记的视图的视图模型控制)。

`<Controls:HamburgerMenu x:Name="HamburgerMenuControl"
                                    HamburgerWidth="48"
                                    IsPaneOpen="True"
                                    CanResizeOpenPane="True"
                                    ItemInvoked="HamburgerMenuControl_OnItemInvoked"
                                    ItemTemplate="{StaticResource MenuItemTemplate}"
                                    OptionsItemTemplate="{StaticResource MenuItemTemplate}"
                                    SelectedIndex="0"
                                    Style="{StaticResource MahApps.Styles.HamburgerMenu.Ripple}"
                                    VerticalScrollBarOnLeftSide="False">
                <!--  Items  -->
                <Controls:HamburgerMenu.ItemsSource>
                    <Controls:HamburgerMenuItemCollection>
                        <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=CommentAltSolid}" Label="Chat">
                            <Controls:HamburgerMenuIconItem.Tag>
                                <views:ChatView />
                            </Controls:HamburgerMenuIconItem.Tag>
                        </Controls:HamburgerMenuIconItem>
                        <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=PenSquareSolid}" Label="Compose">
                            <Controls:HamburgerMenuIconItem.Tag>
                                <views:ComposeMessageView />
                            </Controls:HamburgerMenuIconItem.Tag>
                        </Controls:HamburgerMenuIconItem>
                        <Controls:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=InboxArrowDown}" Label="Inbox">
                            <Controls:HamburgerMenuIconItem.Tag>
                                <views:InboxView />
                            </Controls:HamburgerMenuIconItem.Tag>
                        </Controls:HamburgerMenuIconItem>
                    </Controls:HamburgerMenuItemCollection>
                </Controls:HamburgerMenu.ItemsSource>
...`

每个菜单项都是一个视图,有自己的视图模型,我想绑定到来自 ChatViewModel 和 InboxViewModel(但不是 ComposeViewModel)的公开 属性,例如,属性 名为 UnreadCount。我在 MessagingMainViewModel 中没有子视图模型的实例(因为到目前为止在应用程序中不需要)。

我知道如何绑定到 MessagingMainViewModel 上的 属性(如上面的 DataTemplate 代码所示)但无法找到访问 "child" 视图的视图模型的方法 - 不要介意在DataTemplate 不知何故。这可能吗?谢谢。

您在 Tag-属性 中设置了 Content。我们可以在 DataTemplate 中使用它来访问 Tag 对象中的任何内容。你说你的 Tag 是一个 UserControl,所以我们可以访问它的 DataContext

这是一个示例,假设 属性 对于您拥有的每个视图都是相同的。如果没有,您需要使用 TemplateSelector:

<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type Controls:HamburgerMenuIconItem}">
    <Grid Height="48">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Controls:Badged Grid.Column="0"
                         Badge="{Binding Tag.DataContext.[YourBadgePropertyGoesHere]}"
                         BadgeBackground="Red"
                         HorizontalAlignment="Center"
                         VerticalAlignment="Center">
            <ContentControl Content="{Binding Icon}"
                            Margin="2"
                            Focusable="False"
                            IsTabStop="False" />
        </Controls:Badged>
        <TextBlock Grid.Column="1"
                   VerticalAlignment="Center"
                   FontSize="16"
                   Text="{Binding Label}" />
    </Grid>
</DataTemplate>

以防万一有人遇到类似的问题,我使用了标签 属性 来访问相关的视图模型和 属性 并且还实现了 DataTemplateSelector 以便 select在是否显示 badge/count 控件之间(即不显示 ComposeMessageView)。感谢 Tim U.

我的最终代码:

<!-- Add reference to the custom DataTemplateSelector namespace -->
xmlns:helpers="clr-namespace:MyCompany.Wpf.Modules.Messaging.Helpers"

<!-- Include the template selector within Resouces (in my case within UserControl.Resources) -->
<helpers:HamburgerMenuIconItemTemplateSelector x:Key="MenuDataTemplateSelector"/> 

<!--  This is the template for the menu items (no badge/count control).  -->
<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type Controls:HamburgerMenuIconItem}">
    <Grid Height="48">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.Column="0"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Content="{Binding Icon}"
                        Focusable="False"
                        IsTabStop="False" />
        <TextBlock Grid.Column="1"
                   VerticalAlignment="Center"
                   FontSize="16"
                   Text="{Binding Label}" />
    </Grid>
</DataTemplate>

<!--  This is the template for the menu items (with badge/count control).  -->
<DataTemplate x:Key="MenuItemTemplateBadged" DataType="{x:Type Controls:HamburgerMenuIconItem}">
    <Grid Height="48">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Controls:Badged Grid.Column="0"
                         Badge="{Binding Tag.DataContext.UnreadMessagesCount}"
                         BadgeBackground="Red"
                         HorizontalAlignment="Center"
                         VerticalAlignment="Center">
            <ContentControl Content="{Binding Icon}"
                            Margin="2"
                            Focusable="False"
                            IsTabStop="False" />
        </Controls:Badged>
        <TextBlock Grid.Column="1"
                   VerticalAlignment="Center"
                   FontSize="16"
                   Text="{Binding Label}" />
    </Grid>
</DataTemplate> 

<Controls:HamburgerMenu x:Name="HamburgerMenuControl"
                    HamburgerWidth="48"
                    IsPaneOpen="True"
                    CanResizeOpenPane="True"
                    ItemInvoked="HamburgerMenuControl_OnItemInvoked"
                    ItemTemplateSelector="{StaticResource MenuDataTemplateSelector}"
                    SelectedIndex="0"
                    Style="{StaticResource MahApps.Styles.HamburgerMenu.Ripple}"
                    VerticalScrollBarOnLeftSide="False">
    <!--  Items  -->
    <Controls:HamburgerMenu.ItemsSource>
        <Controls:HamburgerMenuItemCollection>
            <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=CommentAltSolid}" Label="Chat">
                <Controls:HamburgerMenuIconItem.Tag>
                    <views:ChatView />
                </Controls:HamburgerMenuIconItem.Tag>
            </Controls:HamburgerMenuIconItem>
            <Controls:HamburgerMenuIconItem Icon="{iconPacks:FontAwesome Kind=PenSquareSolid}" Label="Compose">
                <Controls:HamburgerMenuIconItem.Tag>
                    <views:ComposeMessageView />
                </Controls:HamburgerMenuIconItem.Tag>
            </Controls:HamburgerMenuIconItem>
            <Controls:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=InboxArrowDown}" Label="Inbox">
                <Controls:HamburgerMenuIconItem.Tag>
                    <views:InboxView />
                </Controls:HamburgerMenuIconItem.Tag>
            </Controls:HamburgerMenuIconItem>
        </Controls:HamburgerMenuItemCollection>
    </Controls:HamburgerMenu.ItemsSource>
    ...


<!-- The custom data template selector class. -->
namespace MyCompany.Wpf.Modules.Messaging.Helpers
{
    using System.Windows;
    using System.Windows.Controls;

    using MyCompany.Wpf.Modules.Messaging.ViewModels;
    using MyCompany.Wpf.Modules.Messaging.Views;

    using MahApps.Metro.Controls;

    public class HamburgerMenuIconItemTemplateSelector: DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;

            if (element != null && item != null && item is HamburgerMenuIconItem)
            {
                var count = GetUnreadMessagesCount((HamburgerMenuIconItem)item);

                if (count == -1)
                {
                    return element.FindResource("MenuItemTemplate") as DataTemplate;
                }

                return element.FindResource("MenuItemTemplateBadged") as DataTemplate;
            }    

            return null;
        }

        private int GetUnreadMessagesCount(HamburgerMenuIconItem item)
        {
            // Get the view/user control
            var viewUserControl = (UserControl)item.Tag;

            // Get the data context. NOTE: All view models in the example
            // inherit from BaseMessageViewModel that exposes property 
            // UnreadMessagesCount - and this property is set to -1 when 
            // the view model is constructed.
            var dataContext = (BaseMessageViewModel)viewUserControl.DataContext;

            return dataContext.UnreadMessagesCount;
        }
    }
}