Как смешать динамические и статические элементы в элементах меню 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, этот подход работает.

Другие вопросы по тегам