Cannot bind Icon property in NavigationView MenuIt

2020-02-29 10:25发布

问题:

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:

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):

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

回答1:

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.



回答2:

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>