如何在UWP XAML NavigationView 菜单项中混合动态和静态项?

8

我正在尝试制作一个NavigationViewMenu,需要按以下方式布置菜单:

  • 静态主页项

  • 静态标题

  • 从数据库中获取的动态元素作为菜单项

  • 静态标题

  • 静态一组项目

这是我尝试过的内容:

<NavigationView.MenuItems>

    <NavigationViewItem Icon="Home"  Content="Home" Tag="home" />

    <NavigationViewItemSeparator />

    <NavigationViewItemHeader Content="My Stuff"/>

    <NavigationViewList ItemsSource="{x:Bind MyStuff}">
        <NavigationViewList.ItemTemplate>
            <DataTemplate x:DataType="local:MyModel">
                <NavigationViewItem Icon="Pictures" Content="{x:Bind Name}" Tag="{x:Bind Tag}" />
            </DataTemplate>
        </NavigationViewList.ItemTemplate>
    </NavigationViewList>

    <!-- Static equivalent to the above:
    <NavigationViewItem Icon="Pictures" Content="Woop" Tag="foos"/>
    <NavigationViewItem Icon="Pictures" Content="Doop" Tag="foos"/>
    <NavigationViewItem Icon="Pictures" Content="Loop" Tag="foos"/>
    -->

    <NavigationViewItemHeader Content="Other Stuff"/>

    <NavigationViewItem Icon="Pictures" Content="Foos" Tag="foos"/>
    <NavigationViewItem Icon="ContactInfo" Content="Bars" Tag="bars"/>
    <NavigationViewItem Icon="SwitchApps" Content="Bazes" Tag="bazes"/>

</NavigationView.MenuItems>

我得到的是这个:

enter image description here

我想要的是这个:

enter image description here

在UWP的XAML中,是否有像Angular的*ngFor一样好用且实用的东西?


{btsdaf} - Kenneth Witham
相关内容:https://dev59.com/R6nka4cB1Zd3GeqPTMOf - binaryfunt
3个回答

5
我遇到了相同的问题,并设法找到了一个解决方法。在我的情况下,我有两个菜单项列表(动态数据绑定项),我想在两者之上都使用NavigationViewItemHeader(静态项)。我尝试使用NavigationViewList并遇到了您的问题。

简短回答:

在C#代码中创建一个菜单项列表。该列表的元素可以是视图模型和任何静态导航项(标题、分隔线等)的混合物。然后使用DataTemplateSelector将其绑定到视图模型或传递未更改的导航项。

更详细

在您的C#代码后台中,创建您的菜单项的可枚举(或可观察集合)。在我的情况下,SomeCollectionAnotherCollection表示我要绑定到我的NavigationView的数据源。我必须将其类型定义为object,因为它是我的视图模型和内置UWP导航项类型的混合物。

private IEnumerable<object> MenuItems()
{
    yield return new NavigationViewItemHeader { Content = "Some List" };
    foreach (var some in SomeCollection)
    {
        yield return some;
    }
    yield return new NavigationViewItemHeader { Content = "Another List" };
    foreach (var another in AnotherCollection)
    {
        yield return another;
    }
}

// somewhere else, like in your Page constructor or a CollectionChanged handler
this.NavigationList = MenuItems().ToList();

其次,创建一个数据模板选择器,以在您的模板和导航项之间切换:
class NavigationItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate ViewModelTemplate{ get; set; }
    public DataTemplate NavigationItemTemplate { get; set; }
    protected override DataTemplate SelectTemplateCore(object item)
    {
        return item is MyViewModel
            ? ViewModelTemplate
            : NavigationItemTemplate;
    }
}

最后,修改你的 NavigationView 引用模板选择器和菜单项源。 NavigationItemTemplate 仅作为传递,而你的 ViewModelTemplate 应该具有常规的视图模型项绑定逻辑。

<Page.Resources>
    <DataTemplate x:Key="ViewModelTemplate" x:DataType="local:MyViewModel">
        <TextBlock Text="{x:Bind SomeProperty}" />
    </DataTemplate>
    <DataTemplate x:Key="NavigationItemTemplate">
    </DataTemplate>
    <local:NavigationItemTemplateSelector x:Key="NavigationItemTemplateSelector"
        ViewModelTemplate="{StaticResource ViewModelTemplate}"
        NavigationItemTemplate="{StaticResource NavigationItemTemplate}" />
</Page.Resources>


<NavigationView
    MenuItemsSource="{x:Bind NavigationList, Mode=OneWay}"
    MenuItemTemplateSelector="{StaticResource NavigationItemTemplateSelector}">
    <Frame x:Name="ContentFrame"></Frame>
</NavigationView>

2

我可以重现这个问题。看起来NavigationViewList只占用一个项的空间,当它被放置在NavigationView.MenuItem中时。这就像将ListView放置在ListViewItem中一样。要更改此行为,我们需要自己更改项的行为。然而,在一些调查后,似乎目前定制NavigationViewList对我们来说是黑盒子。所以我能想到的唯一方法是利用splitview和acrylic构建我们自己的NavigationView。


我可能会选择这个方案,看起来像是一个带有列表视图和按钮的亚克力容器可以解决它,尽管有些麻烦,但失去了NavigationView的炫酷收缩和实现响应性。 - Felype

0

我认为没有必要像被接受的答案那样使用不同的模板,可能是因为在此期间底层Windows代码有所更改。由于我需要一个菜单的稳定部分和一个根据实际页面变化而动态变化的部分,所以我创建了一个接口:

interface IMenuProvider {
  IEnumerable<NavigationViewItemBase> GetMenuItems();
}

并确保所有页面都实现了它。我的MainPage返回固定部分:

public IEnumerable<NavigationViewItemBase> GetMenuItems() {
  yield return new NavigationViewItem {
    Tag = "home",
    Icon = new SymbolIcon(Symbol.Home),
    Content = "Home",
  };
  yield return new NavigationViewItemSeparator();
  yield return new NavigationViewItem {
    Tag = "xxx",
    Icon = new SymbolIcon(Symbol.XXX),
    Content = "XXX",
  };
}

其他页面同样提供它们自己的菜单头和项目。 当我浏览页面时,我也会更改菜单,将固定部分和变量部分连接起来。
ContentFrame.Navigate(PageType, null, transitionInfo);
if (ContentFrame.Content is IMenuProvider menuProvider)
  = GetMenuItems().Concat(menuProvider.GetMenuItems()).ToList();

(或者,您可以将菜单更改放入框架的Navigated处理程序中。)
虽然这些菜单(至少是固定部分)无法在XAML中声明,但这种方法仍然有效。

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