问题
I have a UWP app with a NavigationView
control. The navigation items are created by setting MenuItemsSource
in the XAML to a collection of objects of type NavigationViewElement
.
<NavigationView
Style="{StaticResource MainPageNavControlStyle}"
HeaderTemplate="{StaticResource MainPageNavHeaderTemplate}"
MenuItemsSource="{Binding NavigationViewElements}"
MenuItemContainerStyleSelector="{StaticResource NavStyleSelector}"
MenuItemTemplateSelector="{StaticResource NavItemTemplateSelector}"
x:Name="NavigationViewControl"
CompactModeThresholdWidth="480"
ExpandedModeThresholdWidth="635"
OpenPaneLength="324"
Loaded="OnControlLoaded"
ItemInvoked="OnItemInvoked"
IsTabStop="False"
IsSettingsVisible="False"
>
I would like to bind the IsEnabled
property of the NavigationViewItems
that get created to a property on NavigationViewElement
. How might I do that?
I had a similar question for a ListBox
. In that case, I was able to derive a new class from ListBox that overrides PrepareContainerForItemOverride()
and sets the IsEnabled
flag of the ListBoxItem
based on the data in the class to which it is bound (OptionItem, in this case)
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
ListBoxItem lItem = element as ListBoxItem;
OptionItem oItem = item as OptionItem;
if (lItem != null && oItem != null)
{
lItem.IsEnabled = oItem.IsEnabled;
}
base.PrepareContainerForItemOverride(element, item);
}
Is there an equivalent for NavigationView?
Or is there some other way to indicate that the IsEnabled
flag for the NavigationViewItem
should be bound to NavigationViewElement.IsItemEnabled
?
Update I looked at the solution proposed by Nico Zhu but I am not sure how to apply it to my requirements. That could be due to my inexperience with XAML.
In my implementation, the DataTemplates that I reference from my Selector object do not include a NavigationViewItem element, due to my layout requirements. Rather than simply setting NavigationViewItem.Content and .Glyph, I populate a Grid with a bunch of controls. Here is a sample:
<DataTemplate x:Key="MainPageNavigationViewItem1LineTemplate">
<Grid Margin="{StaticResource MainPageNavigationViewItemTopGridMargin}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<FontIcon Glyph="{Binding Glyph, FallbackValue=}" FontFamily="{Binding FontFamily, FallbackValue=xGlyph}" FontSize="{StaticResource MainPageNavigationViewItemGlyphFontSize}" VerticalAlignment="Center" Margin="{StaticResource MainPageNavigationViewItemGlyphMargin}"/>
<StackPanel Grid.Column="1" Margin="{StaticResource MainPageNavigationViewItemTextMargin}" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBlock x:Name="Header"
Text="{Binding TheSummaryHelper.SummaryHeaderLabel, FallbackValue=TheHeader}"
Style="{StaticResource DefaultFontStyle}"
/>
<TextBlock x:Name="Line1"
Text="{Binding TheSummaryHelper.Line1.DisplayString, FallbackValue=TheFirstLine}"
Visibility="{Binding TheSummaryHelper.Line1.ItemVisibility, FallbackValue=Visible}"
Style="{StaticResource SmallSummaryTextStyle}"
/>
</StackPanel>
</Grid>
</DataTemplate>
The result looks like this, with the item's contents set equal to those of the Grid:
It's exactly what I need, but I couldn't figure out how to bind the IsEnabled property of the item to NavigationViewElement.IsItemEnabled.
When I tried to follow the model suggested, adding a NavigationViewItem to the DataTemplate like this:
<misc:MainPageNavigationViewItemTemplateSelector
x:Key="NavItemTemplateSelector"
>
<misc:MainPageNavigationViewItemTemplateSelector.ItemTemplate>
<DataTemplate x:DataType="vm_misc:NavigationViewElement">
<NavigationViewItem IsEnabled="{x:Bind IsItemEnabled}" Content="{x:Bind TheSummaryHelper.SummaryHeaderLabel}">
</NavigationViewItem>
</DataTemplate>
</misc:MainPageNavigationViewItemTemplateSelector.ItemTemplate>
</misc:MainPageNavigationViewItemTemplateSelector>
then I am able to bind the IsEnabled property as desired, but the UI does not draw properly. The Content for the item seems to add a second NavigationViewItem on top of the one I already had. Clicking the area with the text does nothing - I have to click on the item outside the light gray area to cause navigation to occur.
Any insights into what I'm doing wrong? In summary, I am hoping to find a way to both customize the display contents of the NavigationViewItem while also binding the IsEnabled property to a property on the NavigationViewElement in my model.
回答1:
For binding MenuItemsSource
with model, you could refer official code sample. In latest version we have added new feature about setting datasource for MenuItemsSource
. If you want to enable or disable NavigationViewItem
, you could make IsEnabled
property in the model then bind it. Please check the following code.
Xaml Code
<Page.Resources>
<local:MenuItemTemplateSelector x:Key="selector">
<local:MenuItemTemplateSelector.ItemTemplate>
<DataTemplate x:DataType="local:Category" >
<NavigationViewItem Content="{x:Bind Name}"
ToolTipService.ToolTip="{x:Bind Tooltip}"
IsEnabled="{x:Bind IsEnabled}" >
<NavigationViewItem.Icon>
<SymbolIcon Symbol="{x:Bind Glyph}" />
</NavigationViewItem.Icon>
</NavigationViewItem>
</DataTemplate>
</local:MenuItemTemplateSelector.ItemTemplate >
</local:MenuItemTemplateSelector>
</Page.Resources>
<Grid>
<NavigationView x:Name="nvSample"
MenuItemTemplateSelector="{StaticResource selector}"
MenuItemsSource="{x:Bind Categories, Mode=OneWay}" />
</Grid>
Code Behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Categories = new ObservableCollection<CategoryBase>();
Categories.Add(new Category { Name = "Category 1", Glyph = Symbol.Home, Tooltip = "This is category 1", IsEnabled = false });
Categories.Add(new Category { Name = "Category 2", Glyph = Symbol.Keyboard, Tooltip = "This is category 2", IsEnabled = true });
Categories.Add(new Category { Name = "Category 3", Glyph = Symbol.Library, Tooltip = "This is category 3" , IsEnabled = false });
Categories.Add(new Category { Name = "Category 4", Glyph = Symbol.Mail, Tooltip = "This is category 4", IsEnabled = true });
}
public ObservableCollection<CategoryBase> Categories { get; set; }
}
public class CategoryBase { }
public class Category : CategoryBase
{
public string Name { get; set; }
public string Tooltip { get; set; }
public Symbol Glyph { get; set; }
public bool IsEnabled { get; set; }
}
public class Separator : CategoryBase { }
public class Header : CategoryBase
{
public string Name { get; set; }
}
[ContentProperty(Name = "ItemTemplate")]
class MenuItemTemplateSelector : DataTemplateSelector
{
public DataTemplate ItemTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
return item is Separator ? SeparatorTemplate : item is Header ? HeaderTemplate : ItemTemplate;
}
internal DataTemplate HeaderTemplate = (DataTemplate)XamlReader.Load(
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<NavigationViewItemHeader Content='{Binding Name}' />
</DataTemplate>");
internal DataTemplate SeparatorTemplate = (DataTemplate)XamlReader.Load(
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<NavigationViewItemSeparator />
</DataTemplate>");
}
Update One
The key to the problem is that we can’t use DataTemplate
in 1803. In general, for creating NavigationViewItem
in 1803 we often use the following solution.
Xaml
<NavigationView x:Name="nvSample" MenuItemsSource="{x:Bind NavItems}" >
</NavigationView>
Code behind
public ObservableCollection<Category> Categories { get; set; }
public MainPage()
{
this.InitializeComponent();
Categories = new ObservableCollection<Category>();
Categories.Add(new Category { Name = "Category 1", Glyph = Symbol.Home, Tooltip = "This is category 1", IsEnabled = false });
Categories.Add(new Category { Name = "Category 2", Glyph = Symbol.Keyboard, Tooltip = "This is category 2", IsEnabled = true });
Categories.Add(new Category { Name = "Category 3", Glyph = Symbol.Library, Tooltip = "This is category 3", IsEnabled = true });
Categories.Add(new Category { Name = "Category 4", Glyph = Symbol.Mail, Tooltip = "This is category 4", IsEnabled = true });
}
public IEnumerable<NavigationViewItemBase> NavItems
{
get
{
return Categories.Select(
b => (new NavigationViewItem
{
Content = b.Name,
Icon = new SymbolIcon(b.Glyph),
IsEnabled = b.IsEnabled,
})
);
}
}
In short, we need convert Data Model to NavigationViewItem
. But not using DataTemplate within 1803. Please try this. For more detail you could also refer this case.
回答2:
If you can use 1809, the behavior there allows NavigationViewItems in a DataTemplate as you are currently doing.
In down-versions however, NavigationViewItem was only allowed in the MenuItems property and could not be used in the MenuItemTemplate property. So, you could move your items to the MenuItems property without a DataTemplate.
If you're on a down-version and you want to use the MenuItemTemplate property with a DataTemplate, you'll need to use something other than NavigationViewItem. For example, you could use a button with the Button.Content set to a grid. Within the grid, you could include something like a SymbolIcon and TextBlock. It would take some work to get the formatting right.
My suggestion is, if you need to use 1803, move the NavigationViewItems to the MenuItems property of the NavigationView itself.
For documentation, please see: https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitem
来源:https://stackoverflow.com/questions/53951958/uwp-setting-isenabled-on-navigationviewitems