将不同的绑定应用到 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;
}
}
}
我在 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;
}
}
}