Search code examples
c#wpfdata-bindingmahapps.metro

Applying different bindings to a DataTemplate for Mahapps Metro HamburgerMenuIconItem


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.


Solution

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