UWP - 在NavigationViewItems上设置IsEnabled

7
我有一个使用NavigationView控件的UWP应用程序。导航项是通过在XAML中将MenuItemsSource设置为类型为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"
>

我希望将创建的NavigationViewItemsIsEnabled属性绑定到NavigationViewElement上的一个属性。我该如何实现?
我之前对于ListBox有过类似的问题。在那种情况下,我能够从ListBox派生出一个新类,并重写PrepareContainerForItemOverride()方法,根据其所绑定的类(在此示例中为OptionItem)中的数据设置ListBoxItemIsEnabled标志。
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);
}

是否有NavigationView的等效物?或者是否有其他方法来指示NavigationViewItemIsEnabled标志应绑定到NavigationViewElement.IsItemEnabled

更新 我查看了Nico Zhu提出的解决方案,但不确定如何将其应用于我的要求。这可能是由于我对XAML的经验不足。

在我的实现中,我从选择器对象引用的DataTemplates不包括NavigationViewItem元素,因为我的布局要求。而不是简单地设置NavigationViewItem.Content和.Glyph,我使用一堆控件填充Grid。这是一个示例:

<DataTemplate x:Key="MainPageNavigationViewItem1LineTemplate">
    <Grid Margin="{StaticResource MainPageNavigationViewItemTopGridMargin}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <FontIcon Glyph="{Binding Glyph, FallbackValue=&#xE14E;}" 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>

结果如下所示,该项的内容设置为与网格相等:

enter image description here

我需要的就是这个,但我不知道如何将该项的IsEnabled属性绑定到NavigationViewElement.IsItemEnabled。

当我尝试按照建议模型操作,像这样在DataTemplate中添加一个NavigationViewItem:

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

然后我可以按照需要绑定IsEnabled属性,但UI不会正确绘制。该项的内容似乎会在我已有的NavigationViewItem之上添加第二个NavigationViewItem。单击文本区域不起作用 - 必须单击灰色区域外的项才能导航。

enter image description here

任何关于我做错了什么的见解?总之,我希望找到一种既可以自定义NavigationViewItem的显示内容,又可以将IsEnabled属性绑定到我的模型中NavigationViewElement上的属性的方法。

你的 NavigationViewElement 模型中是否包含 IsEnabled 属性? - Dishant
不。你认为这有关系吗? - Rich S
根据您的帖子,我猜想您想将“IsEnabled”属性与项目的“IsEnabled”属性绑定,对吗? - Dishant
2个回答

5

如果要将MenuItemsSource与模型绑定,您可以参考官方代码示例。在最新版本中,我们添加了有关为MenuItemsSource设置数据源的新功能。如果您想启用或禁用NavigationViewItem,您可以在模型中设置IsEnabled属性,然后进行绑定。请检查以下代码。

Xaml 代码

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

代码后端

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>");
}

输入图像描述



更新一

问题的关键在于我们不能在1803中使用 DataTemplate。一般来说,在1803中创建 NavigationViewItem,我们经常使用以下解决方案。

Xaml

<NavigationView x:Name="nvSample" MenuItemsSource="{x:Bind NavItems}" >

</NavigationView>

代码后置

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,
               })
        );
    }
}

简而言之,我们需要将数据模型转换为NavigationViewItem。但在1803中不使用DataTemplate。请尝试这个方法。更多细节可以参考此案例

这看起来很有前途。我下周会尝试并回报结果。 - Rich S
@RichS,我已经查看了您的更新,这是在1803操作系统中发生的已知问题。请查看此案例回复 - Nico Zhu
谢谢您提供的信息。我们的软件必须在1803中运行 - 我们处理这个问题的选择是什么?是否有NavigationView的版本可以在1803中正确地运行? - Rich S
我使用的是1803版本,尽管构建成功了,但它从未触发选择更改或项调用事件。因此看起来很好,但无法使用。 - Mark W
嗨@RichS,我已经升级了你的案件。你能告诉我为什么需要停留在1803吗? - Nico Zhu
显示剩余5条评论

0
如果您可以使用1809,那么其中的行为允许NavigationViewItems在DataTemplate中使用,就像您目前正在做的那样。
然而,在旧版本中,只允许在MenuItems属性中使用NavigationViewItem,而不能在MenuItemTemplate属性中使用。因此,您可以将项目移动到MenuItems属性中,而无需DataTemplate。
如果您在旧版本上并且想要使用带有DataTemplate的MenuItemTemplate属性,则需要使用NavigationViewItem以外的其他控件。例如,您可以使用Button,并将Button.Content设置为网格。在网格中,您可以包含类似于SymbolIcon和TextBlock的内容。这需要一些工作才能正确格式化。
我建议的是,如果您需要使用1803,请将NavigationViewItems移动到NavigationView本身的MenuItems属性中。
有关文档,请参见:https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitem

感谢您提供的信息和建议。在我们的情况下,我们正在动态绑定导航项到在运行时确定的对象集合。我相信这排除了将项目移动到MenuItems的能力。此外,我们的项目布局需要多行文本,这就是为什么我们决定使用DataTemplate的原因。这个解决方案对我们非常有效 - 除了我一直无法将项目的IsEnabled属性绑定到我的模型属性。这就是我在这里发布的原因。 - Rich S
我们最希望的用户体验是在相关页面上的功能不可用或不相关时禁用导航项。对于1803版本,看起来我们将不得不放弃这一点。相反,我们将允许用户导航到该页面,但页面上的控件将被禁用。我们可以发布这个版本,但这并不是很好的UI设计。 - Rich S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接