Reputation: 15
I am using the Hamburger Menu in a MVVM WPF application and have applied the Badge control to each menu item using a DataTemplate as follows: (taken from 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>`
This works fine, but this applies the same value to all menu items - in this example I am binding to a property named TestCount from MessagingMainViewModel (the view model for the view containing the markup for the HamburgerMenu control).
`<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>
...`
Each of the menu items is a view, with it's own view model and I would like to bind to an exposed property from the ChatViewModel and InboxViewModel (but not the ComposeViewModel), for example, a property named UnreadCount. I do not have insances of the child view models in the MessagingMainViewModel (as there has been no need thus far in the application).
I know how to bind to a property on the MessagingMainViewModel (as the DataTemplate code above shows) but cannot find a way to access the "child" view's viewmodel - never mind use this in the DataTemplate somehow. Is this possible at all? Thanks.
Upvotes: 1
Views: 964
Reputation: 15
Just in case anyone faces a similar problem, I used the Tag property to gain access to the relevent view model and property and also implemented a DataTemplateSelector in order to select between showing the badge/count control or not (i.e. not shown for the ComposeMessageView). All thanks to Tim U.
My final code:
<!-- 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;
}
}
}
Upvotes: 0
Reputation: 129
You have your Content
set in the Tag
-Property. We can use this in our DataTemplate
to access anything in the Tag
-object. You say that your Tag is a UserControl
, so we can access its DataContext
.
Here is an example, assuming that the Property is the same for every view you have. If not, you need to use a 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>
Upvotes: 1