如何在 UWP XAML NavigationView MenuItems 中混合动态和静态项?
How to mix dynamic and static items in UWP XAML NavigationView MenuItems?
我正在尝试制作一个 NavigationViewMenu,我需要一个布局如下的菜单
静态主页项目
静态Header
数据库中的动态元素作为项目
静态Header
静态项目集
这是我试过的:
<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>
这是我得到的:
这就是我想要的:
在XAML中有没有像Angular的*ngFor
一样好的和实用的UWP?
我可以复制它。看起来 NavigationViewList 在将自己放入 NavigationView.MenuItem 时只采用一项的 space。这就像将 ListView 放入 ListViewItem 中一样。要改变这种行为,我们需要自己改变项目的行为。然而,经过一些调查后,目前 NavigationViewList 的定制似乎对我们来说是黑盒。所以我能想到的唯一方法就是借助 splitview 和 acrylic 构建我们自己的 NavigationView。
我 运行 有同样的行为,并设法找到了解决方法。在我的例子中,我有两个菜单项列表(动态 data-bound 项),我想在两个(静态项)之上使用 NavigationViewItemHeader
。我尝试在您的问题中使用 NavigationViewList
和 运行。
长话短说:
用 C# 代码创建菜单项列表。此列表的元素可以是您的视图模型和任何静态导航项(headers、分隔符等)的混合。然后使用 DataTemplateSelector 将数据绑定到您的视图模型或 pass-through 导航项不变。
更详细
在您的 C# code-behind 中,创建菜单项的可枚举(或可观察 collection)。在我的例子中,SomeCollection
和 AnotherCollection
代表我想要绑定到我的 NavigationView 的数据源。我必须将其键入 object
,因为它混合了我的视图模型和 built-in 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
只是一个 pass-through,而您的 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>
我发现没有必要像接受的答案那样使用不同的模板,可能是因为同时底层 Windows 代码发生了一些变化。由于我需要菜单的稳定部分,然后是取决于实际页面的动态部分,因此我创建了一个界面:
interface IMenuProvider {
IEnumerable<NavigationViewItemBase> GetMenuItems();
}
并确保我的所有页面都实现了它。我的MainPage
returns固定部分:
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",
};
}
其他页面类似地提供了自己的菜单 headers 和项目。
当我浏览页面时,我也更改了菜单,连接了固定部分和可变部分:
ContentFrame.Navigate(PageType, null, transitionInfo);
if (ContentFrame.Content is IMenuProvider menuProvider)
= GetMenuItems().Concat(menuProvider.GetMenuItems()).ToList();
(或者,您可以将菜单更改放入 Frame
的 Navigated
处理程序中。)
虽然这些菜单(至少是固定部分)不能在 XAML 中声明仍然很麻烦,但这种方法有效。
我正在尝试制作一个 NavigationViewMenu,我需要一个布局如下的菜单
静态主页项目
静态Header
数据库中的动态元素作为项目
静态Header
静态项目集
这是我试过的:
<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>
这是我得到的:
这就是我想要的:
在XAML中有没有像Angular的*ngFor
一样好的和实用的UWP?
我可以复制它。看起来 NavigationViewList 在将自己放入 NavigationView.MenuItem 时只采用一项的 space。这就像将 ListView 放入 ListViewItem 中一样。要改变这种行为,我们需要自己改变项目的行为。然而,经过一些调查后,目前 NavigationViewList 的定制似乎对我们来说是黑盒。所以我能想到的唯一方法就是借助 splitview 和 acrylic 构建我们自己的 NavigationView。
我 运行 有同样的行为,并设法找到了解决方法。在我的例子中,我有两个菜单项列表(动态 data-bound 项),我想在两个(静态项)之上使用 NavigationViewItemHeader
。我尝试在您的问题中使用 NavigationViewList
和 运行。
长话短说:
用 C# 代码创建菜单项列表。此列表的元素可以是您的视图模型和任何静态导航项(headers、分隔符等)的混合。然后使用 DataTemplateSelector 将数据绑定到您的视图模型或 pass-through 导航项不变。
更详细
在您的 C# code-behind 中,创建菜单项的可枚举(或可观察 collection)。在我的例子中,SomeCollection
和 AnotherCollection
代表我想要绑定到我的 NavigationView 的数据源。我必须将其键入 object
,因为它混合了我的视图模型和 built-in 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
只是一个 pass-through,而您的 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>
我发现没有必要像接受的答案那样使用不同的模板,可能是因为同时底层 Windows 代码发生了一些变化。由于我需要菜单的稳定部分,然后是取决于实际页面的动态部分,因此我创建了一个界面:
interface IMenuProvider {
IEnumerable<NavigationViewItemBase> GetMenuItems();
}
并确保我的所有页面都实现了它。我的MainPage
returns固定部分:
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",
};
}
其他页面类似地提供了自己的菜单 headers 和项目。
当我浏览页面时,我也更改了菜单,连接了固定部分和可变部分:
ContentFrame.Navigate(PageType, null, transitionInfo);
if (ContentFrame.Content is IMenuProvider menuProvider)
= GetMenuItems().Concat(menuProvider.GetMenuItems()).ToList();
(或者,您可以将菜单更改放入 Frame
的 Navigated
处理程序中。)
虽然这些菜单(至少是固定部分)不能在 XAML 中声明仍然很麻烦,但这种方法有效。