Как смешать динамические и статические элементы в элементах меню UWP XAML NavigationView?
Я пытаюсь сделать 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>
Вот что у меня есть:
Это то, что я хотел:
Есть ли что-нибудь такое же хорошее и практичное, как в Angular? *ngFor
в XAML для UWP?
2 ответа
Я столкнулся с тем же поведением, и мне удалось найти работу вокруг. В моем случае у меня было два списка пунктов меню (динамически привязанных к данным), и я хотел использовать NavigationViewItemHeader
поверх обоих (статичные предметы). Я пытался с помощью NavigationViewList
и столкнулся с вашей проблемой.
TL; DR:
Создайте список пунктов меню в коде C#. Элементы этого списка могут быть комбинацией ваших моделей представления и любых статических элементов навигации (заголовков, разделителей и т. Д.). Затем используйте DataTemplateSelector для привязки данных к вашей модели представления или сквозных элементов навигации без изменений.
Более подробный
В вашем коде C# создайте перечисляемую (или наблюдаемую коллекцию) ваших пунктов меню. В моем случае SomeCollection
а также AnotherCollection
представляют мои источники данных, которые я хотел связать с моим 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>
Я могу воспроизвести это. Похоже, что NavigationViewList занимает только один элемент при помещении себя в NavigationView.MenuItem. Что похоже на помещение ListView в ListViewItem. Чтобы изменить это поведение, нам нужно изменить поведение элемента самостоятельно. Однако после некоторого изучения кажется, что в настоящее время настройка NavigationViewList является для нас черным ящиком. Таким образом, я мог думать только о том, чтобы создать собственный NavigationView с помощью splitview и acrylic.
Я не счел необходимым использовать другие шаблоны, как в принятом ответе, возможно, потому, что за это время были внесены некоторые изменения в базовый код 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
обработчик
Frame
.)
Хотя по-прежнему неприятно, что эти меню, по крайней мере, фиксированная часть, не могут быть объявлены в XAML, этот подход работает.