【问题标题】:Applying different bindings to a DataTemplate for Mahapps Metro HamburgerMenuIconItem对 Mahapps Metro HamburgerMenuIconItem 的 DataTemplate 应用不同的绑定
【发布时间】:2020-06-10 23:31:34
【问题描述】:

我在 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 代码所示),但找不到访问“子”视图的视图模型的方法 - 没关系以某种方式在 DataTemplate 中使用它。这可能吗?谢谢。

【问题讨论】:

    标签: c# wpf data-binding mahapps.metro


    【解决方案1】:

    您在Tag-属性中设置了Content。我们可以在DataTemplate 中使用它来访问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>
    

    【讨论】:

    • 如果您不介意另一个相关问题,您使用 TemplateSelector 声明。我在汉堡菜单中有三个视图(选项),不希望显示其中之一的徽章控件(即视图:ComposeMessageView)。我在网上搜索过,可以看到在代码中使用选择器的示例,以及在 XAML 中使用样式/数据触发器的示例。我试图将代码保留在代码后面(MVVM 方法)。您建议的 ItemSelector 方法是仅使用 XAML 还是涉及一些代码?如果您能提供一个链接,将不胜感激。谢谢。
    • 我认为你需要一些代码。我也是 XAML 的粉丝,但这就像用叉子吃饭一样。大多数时候是一个不错的选择,但如果你有汤,你最好用勺子:-)。回到主题:这里是一个示例用法的链接:docs.microsoft.com/en-us/dotnet/api/…
    • 我已经按照链接的建议实现了一个模板选择器,并且一切正常。再次感谢@Tim。
    【解决方案2】:

    以防万一有人遇到类似的问题,我使用 Tag 属性来访问相关视图模型和属性,还实现了 DataTemplateSelector 以选择是否显示徽章/计数控件(即不显示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;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-04-27
      • 1970-01-01
      • 2014-02-27
      • 2021-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      相关资源
      最近更新 更多