4

I've tried to use a NavigationView because it looks very useful, but I'm struggling to make it work with the MVVM pattern.

I've attached the MenuItemsSource property like in this snippet:

<Page x:Class="App5.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App5"
      ...>

    <Page.DataContext>
        <local:MainViewModel></local:MainViewModel>
    </Page.DataContext>

    <NavigationView MenuItemsSource="{Binding Items}">
        <NavigationView.MenuItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" />
            </DataTemplate>
        </NavigationView.MenuItemTemplate>
    </NavigationView>
</Page>

All I get is this:

enter image description here

That's cool, but NavigationViewItem has a Icon property to decorate the text with it.

How do I set the icon depending on the item bound to each NavigationViewItem?

NOTICE: I really don't want to manually add the icon as part of the MenuItemTemplate because it's not the way it's supposed to be. What I need is to BIND the Icon property of the implicitly generated NavigationViewItems.

The question is how?

I've tried with this (using the MenuItemContainerStyle), but it doesn't work:

<NavigationView MenuItemsSource="{Binding Items}">
    <NavigationView.MenuItemContainerStyle>
        <Style TargetType="NavigationViewItem">
            <Setter Property="Icon" Value="{Binding Converter={StaticResource ItemToIconConverter}}" />
        </Style>
    </NavigationView.MenuItemContainerStyle>
    <NavigationView.MenuItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </NavigationView.MenuItemTemplate>
</NavigationView>

NOTE: With the XAML suggested in the answer below (putting a NavigationViewItem inside the DataTemplate), the NavigationViewItem is duplicated in the visual tree (one nested into another):

enter image description here

It's not a solution. Moreover, it looks and behaved badly. Take a look to this snapshot of the suggested solution:

enter image description here

binaryfunt
  • 6,401
  • 5
  • 37
  • 59
SuperJMN
  • 13,110
  • 16
  • 86
  • 185

2 Answers2

5

This was driving me mad! Sadly, it seems the default behaviour is that the contents of MenuItemTemplate are placed inside the NavigationViewItem's ContentPresenter.

The way I got around this was to copy the relevant part of the default style for NavigationViewItem, and modify it slightly:

<NavigationView.MenuItemTemplate>
    <DataTemplate>
        <Grid HorizontalAlignment="Left"
              Height="40"
              Margin="-16,0,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="48" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Viewbox Grid.Column="0"
                     Margin="16,12">
                <SymbolIcon Symbol="{x:Bind Icon}" />
            </Viewbox>
            <ContentPresenter Content="{x:Bind Name}"
                              Grid.Column="1"
                              VerticalAlignment="Center"/>
        </Grid>
    </DataTemplate>
</NavigationView.MenuItemTemplate>

The modification has included setting negative margin for the Grid and adding the two Grid.Column properties.

I've opened an issue on the Docs GitHub so hopefully they will explain the behaviour better.

binaryfunt
  • 6,401
  • 5
  • 37
  • 59
1

You state that you dont want to manually add the icon as part of the MenuItemTemplate because it's not the way it's thought to be (which is absolutely correct), yet you actually do the exact same thing for setting the menu item's text (manually adding a TextBlock instead of setting the menu item's Content property).

The TextBlock overrides any NavigationItemView that is automatically created, so there is no icon element to be displayed (a TextBlock only contains text, no icon).

Try to simply use a NavigationViewItem as template:

<NavigationView MenuItemsSource="{Binding Items}">
    <NavigationView.MenuItemTemplate>
        <DataTemplate>
            <NavigationViewItem Icon="{Binding Icon}" Content="{Binding Name}"/>
        </DataTemplate>
    </NavigationView.MenuItemTemplate>
</NavigationView>
andreask
  • 4,248
  • 1
  • 20
  • 25
  • I second this approach, use NavigationViewMenuItem like he does. If you also plan on using separators, you can use a TemplateSelector and swap out the DataTemplate depending on whether you need a MenuItem or Separator – Lance McCarthy Mar 13 '18 at 15:03
  • 2
    Sorry, but this approach is a simplistic solution. What is does is to create a `NavigationViewItem` inside a `NavigationViewItem`. Try it and observe the effect: hover the mouse and click it. If your PC is using Fluent Design, you will appreciate the duplication. – SuperJMN Mar 13 '18 at 16:11
  • I need to access the implicit `NavigationViewItem`, the one that is generated automatically by the `ContainerGenerator`. The only way I can think of is settings a Style in the `MenuItemContainerStyle`. But I think Bindings aren't supported inside `Styles`. – SuperJMN Mar 13 '18 at 16:14
  • 1
    While I'm afraid I cannot reproduce your visual problems with the duplicate `NavigationViewItem`, research shows it is indeed not recommended to nest `NavigationViewItem`s (sorry for giving that advice in the first place), however the official solutions seems to "manually" specify icon and text: https://social.msdn.microsoft.com/Forums/en-US/5f377eb4-f9e6-4997-8ee4-520ea89eab28/rs31709uwp-xaml-how-to-use-navigationviewitem-in-datatemplate?forum=wpdevelop – andreask Mar 13 '18 at 16:57
  • Note, in 1809 with 17763 SDK, you could indeed use a NavigationViewItem within the data template. Down versions, however, only allowed NavigationViewItem within the Navigation view's MenuItems property (not MenuItemTemplate). – David Hollowell - MSFT Jan 29 '19 at 14:56