Вопрос по xaml, wpf, mvvm – Как я могу привязать ObservableCollection ViewModels к MenuItem?

15

Когда я связываю пункты меню с ObservableCollection, только «внутренняя» Область MenuItem является интерактивной:

альтернативный текст http://tanguay.info/web/external/mvvmMenuItems.png

В моемView У меня есть это меню:

<Menu>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

Тогда я связываю это с этимDataTemplate:

<DataTemplate x:Key="MainMenuTemplate">
    <MenuItem
        Header="{Binding Title}" 
        Command="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}" 
        Background="Red"
        CommandParameter="{Binding IdCode}"/>
</DataTemplate>

Поскольку каждая ViewModel в ObservableCollectionManageMenuPageItemViewModels имеет свойствоTitle а такжеIdCodeприведенный выше код отлично работает с первого взгляда.

HOWEVERпроблема в том, чтоMenuItem в DataTemplate на самом делеinside другой пункт меню (as if it is being bound twice) так что в приведенном выше DataTemplate сBackground="Red" EстьRed box inside each menu item и только эта область может быть нажата, но не вся область элемента меню (например, если пользователь нажимает на область, где находится флажок, или справа или слева от внутренней области с возможностью нажатия, тогда ничего не происходит, что, если вы не & & a; Отдельный цвет очень сбивает с толку.)

What is the correct way to bind MenuItems to an ObservableCollection of ViewModels so that the whole area inside each MenuItem is clickable?

UPDATE:

Поэтому я сделал следующие изменения, основываясь на приведенных ниже советах, и теперь получил следующее:

альтернативный текст http://tanguay.info/web/external/mvvmMenuItemsYellow.png

У меня есть только TextBlock внутри моего DataTemplate, но я все еще не могу "раскрасить весь MenuItem" но только текстовый блок:

<DataTemplate x:Key="MainMenuTemplate">
    <TextBlock Text="{Binding Title}"/>
</DataTemplate>

И я поместил привязку Command в Menu.ItemContainerStyle, но теперь они не запускаются:

<Menu DockPanel.Dock="Top">
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Background" Value="Yellow"/>
            <Setter Property="Command" Value="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
            <Setter Property="CommandParameter" Value="{Binding IdCode}"/>
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="MVVM" ItemsSource="{Binding MvvmMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Application" ItemsSource="{Binding ApplicationMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Manage" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

Ваш Ответ

4   ответа
2

возможно, панель стека со значком и TextBlock).

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Edward Tanguay
Error: User Rate Limit Exceeded Edward Tanguay
35

ой части моего приложения используются DataTemplates для сопряжения View с ViewModel, но это, похоже, не работает с Menus по точным причинам, которые вы описали. Вот как я в конце концов решил это. Мой взгляд выглядит так:

<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=(local:MainViewModel.MainMenu)}">
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=(contracts:IMenuItem.Header)}"/>
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=(contracts:IMenuItem.Items)}"/>
            <Setter Property="MenuItem.Icon" Value="{Binding Path=(contracts:IMenuItem.Icon)}"/>
            <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=(contracts:IMenuItem.IsCheckable)}"/>
            <Setter Property="MenuItem.IsChecked" Value="{Binding Path=(contracts:IMenuItem.IsChecked)}"/>
            <Setter Property="MenuItem.Command" Value="{Binding}"/>
            <Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IMenuItem.Visible), 
                Converter={StaticResource BooleanToVisibilityConverter}}"/>
            <Setter Property="MenuItem.ToolTip" Value="{Binding Path=(contracts:IMenuItem.ToolTip)}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(contracts:IMenuItem.IsSeparator)}" Value="true">
                    <Setter Property="MenuItem.Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type MenuItem}">
                                <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>
</DockPanel>

Если вы заметили, я определил интерфейс IMenuItem, который является ViewModel для MenuItem. Вот код для этого:

public interface IMenuItem : ICommand
{
    string Header { get; }
    IEnumerable<IMenuItem> Items { get; }
    object Icon { get; }
    bool IsCheckable { get; }
    bool IsChecked { get; set; }
    bool Visible { get; }
    bool IsSeparator { get; }
    string ToolTip { get; }
}

Обратите внимание, что IMenuItem определяет элементы IEnumerable, и именно так вы получаете подменю. Кроме того, IsSeparator - это способ определения разделителей в меню (еще один сложный трюк). В xaml вы можете увидеть, как он использует DataTrigger для изменения стиля на существующий стиль разделителя, если IsSeparator имеет значение true. Вот как MainViewModel определяет свойство MainMenu (к которому привязывается представление):

public IEnumerable<IMenuItem> MainMenu { get; set; }

Кажется, это хорошо работает. Я предполагаю, что вы могли бы использовать ObservableCollection для MainMenu. Я на самом деле использую MEF для составления меню из частей, но после этого сами элементы являются статическими (даже если свойства каждого элемента меню не являются статичными). Я также использую класс AbstractMenuItem, который реализует IMenuItem и является вспомогательным классом для создания экземпляров пунктов меню в различных частях.

UPDATE:

Что касается вашей цветовой проблемы, делает лиэта тема Помогите?

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
2

что вам нужно, но я думаю, что это довольно близко.

  <Style x:Key="SubmenuItemStyle" TargetType="MenuItem">
    <Setter Property="Header" Value="{Binding MenuName}"></Setter>
    <Setter Property="Command" Value="{Binding Path=MenuCommand}"/>
    <Setter Property="ItemsSource" Value="{Binding SubmenuItems}"></Setter>
  </Style>

  <DataTemplate DataType="{x:Type systemVM:TopMenuViewModel}" >
    <Menu>
      <MenuItem Header="{Binding MenuName}"         
                    ItemsSource="{Binding SubmenuItems}" 
                    ItemContainerStyle="{DynamicResource SubmenuItemStyle}" />
    </Menu>
  </DataTemplate>

    <Menu DockPanel.Dock="Top" ItemsSource="{Binding Menus}" />

TopMenuViewModel представляет собой набор меню, которые появятся в строке меню. Каждый из них содержит MenuName, которое будет отображаться, и коллекцию под названием SubMenuItems, которую я установил в качестве ItemsSource.

Я контролирую способ отображения SubMenuItems с помощью стиля SumMenuItemStyle. Каждый SubMenuItem имеет свое собственное свойство MenuName, свойство Command типа ICommand и, возможно, другую коллекцию SubMenuItems.

В результате я могу хранить всю информацию о моем меню в базе данных и динамически переключать то, что меню отображаются во время выполнения. Вся область меню является интерактивной и отображается правильно.

Надеюсь это поможет.

14

MenuItem вDataTemplate,DataTemplate определяетcontent изMenuItem, Вместо этого укажите сторонние свойства дляMenuItem черезItemContainerStyle:

<Menu>
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding Title}"/>
            ...
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

Кроме того, взгляните наHierarchicalDataTemplates.

Error: User Rate Limit Exceeded Edward Tanguay
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы