Смешивание динамических и статических пунктов меню XAML - PullRequest
5 голосов
/ 26 мая 2009

У меня есть сценарий, в котором мне нужно иметь как статические, так и динамические пункты меню. Статические элементы будут определены в XAML, а динамические - в модели представления. Каждый динамический элемент будет представлен VieModel, назовем его CommandViewModel. CommandViewModel имеет среди прочего отображаемое имя, оно также может содержать другие CommandViewModel.

MainViewModel, который используется как текстовый текст для меню, выглядит следующим образом:

public class MainMenuViewModel : INotifyPropertyChanged
{

  private ObservableCollection<CommandViewModel> m_CommandVMList;


  public MainMenuViewModel()
  {
    m_ CommandVMList = new ObservableCollection<CommandViewModel>();

    CommandViewModel cmv = new CommandViewModel();
    cmv.DisplayName = "Dynamic Menu 1";
    m_CommandVMList.Add(cmv);

    cmv = new CommandViewModel();
    cmv.DisplayName = "Dynamic Menu 2";
    m_CommandVMList.Add(cmv);

    cmv = new CommandViewModel();
    cmv.DisplayName = "Dynamic Menu 3";
    m_CommandVMList.Add(cmv);

  }

  public ObservableCollection<CommandViewModel> CommandList
  {
    get { return m_CommandVMList; }
    set
    {
      m_CommandVMList = value;
      OnPropertyChanged("CommandList");
    }
  }

... ... ...

Меню XAML:

<Grid>
  <Grid.Resources>
    <HierarchicalDataTemplate DataType="{x:Type Fwf:CommandViewModel}" ItemsSource="{Binding Path=CommandViewModels}">
      <MenuItem Header="{Binding Path=DisplayName}"/>
    </HierarchicalDataTemplate>
  </Grid.Resources>

  <Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
    <MenuItem Header="Static Top Menu Item 1">
      <MenuItem Header="Static Menu Item 1"/>
        <MenuItem Header="Static Menu Item 2"/>
        <MenuItem Header="Static Menu Item 3"/>
        <ItemsControl ItemsSource="{Binding Path= CommandList}"/>
        <MenuItem Header="Static Menu Item 4"/>
      </MenuItem>
  </Menu>
</Grid>

Все работает нормально, за исключением того, что независимо от того, что я пытаюсь представить в списке динамических меню, в данном случае ItemsControl отображается в пользовательском интерфейсе как ОДИН пункт меню, содержащий больше элементов меню, поэтому выбирается весь набор динамических пунктов меню когда вы нажимаете на элемент. Коллекция корректно представлена ​​в том смысле, что каждый динамический элемент меню отображается как сам элемент меню, но внутри этого большего элемента меню. Я думаю, я понимаю, почему, поскольку Меню просто создает пункт меню для каждого из содержащихся в нем элементов, статических или динамических, ему все равно. Есть ли способ, чтобы каждый динамический элемент меню создавался на том же уровне и принадлежал к родительскому элементу меню, как статические элементы в примере?

1 Ответ

5 голосов
/ 26 мая 2009

Вместо жесткого кодирования ваших «статических» пунктов меню на стороне XAML, я бы жестко закодировал их на стороне виртуальной машины как объекты CommandViewModel.

Поскольку вы жестко программируете его в любом случае, вы не потеряете гибкость и получите дополнительное преимущество от синхронизации ваших статических пунктов меню с вашим HierarchicalDataTemplate, если вы решите визуализировать их по-другому в будущем.

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

РЕДАКТИРОВАТЬ: Пример кода

Мне удалось довольно быстро разобраться в этом, и большинство определений классов неполные (например, INotifyPropertyChanged), но это должно дать вам представление о том, что вы можете сделать. Я добавил несколько вложений команд в третью команду, чтобы убедиться, что иерархический шаблон данных работает.

Вот XAML

<Window
    x:Class="WPFDynamicMenuItems.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPFDynamicMenuItems"
    Title="Window1" Height="300" Width="600">
    <Grid>
        <Grid.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:CommandViewModel}" ItemsSource="{Binding Path=CommandList}">
                <ContentPresenter
                    Content="{Binding Path=DisplayName}"
                    RecognizesAccessKey="True" />
            </HierarchicalDataTemplate>
        </Grid.Resources>
        <ToolBarTray>
            <ToolBar>
            <Menu>
                <Menu.ItemsSource>
                    <CompositeCollection>
                        <MenuItem Header="A"></MenuItem>
                        <MenuItem Header="B"></MenuItem>
                        <MenuItem Header="C"></MenuItem>

                        <CollectionContainer x:Name="dynamicMenuItems">
                        </CollectionContainer>

                        <MenuItem Header="D"></MenuItem>

                    </CompositeCollection>
                </Menu.ItemsSource>

            </Menu>
                </ToolBar>
        </ToolBarTray>
    </Grid>
</Window>

А вот код позади:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace WPFDynamicMenuItems
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        private MainMenuViewModel _mainMenuVM = new MainMenuViewModel();

        public Window1()
        {
            InitializeComponent();

            this.dynamicMenuItems.Collection = this._mainMenuVM.CommandList;
        }
    }


    public class MainMenuViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<CommandViewModel> m_CommandVMList;

        public MainMenuViewModel()
        {
            m_CommandVMList = new ObservableCollection<CommandViewModel>();
            CommandViewModel cmv = new CommandViewModel();
            cmv.DisplayName = "Dynamic Menu 1";
            m_CommandVMList.Add(cmv);
            cmv = new CommandViewModel();
            cmv.DisplayName = "Dynamic Menu 2";
            m_CommandVMList.Add(cmv);
            cmv = new CommandViewModel();
            cmv.DisplayName = "Dynamic Menu 3";
            m_CommandVMList.Add(cmv);

            CommandViewModel nestedCMV = new CommandViewModel();
            nestedCMV.DisplayName = "Nested Menu 1";
            cmv.CommandList.Add(nestedCMV);

            nestedCMV = new CommandViewModel();
            nestedCMV.DisplayName = "Nested Menu 2";
            cmv.CommandList.Add(nestedCMV);
        }
        public ObservableCollection<CommandViewModel> CommandList
        {
            get { return m_CommandVMList; }
            set { m_CommandVMList = value; OnPropertyChanged("CommandList"); }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            //  Hook up event...
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class CommandViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<CommandViewModel> m_CommandVMList;

        public CommandViewModel()
        {
            this.m_CommandVMList = new ObservableCollection<CommandViewModel>();
        }

        public string DisplayName { get; set; }

        public ObservableCollection<CommandViewModel> CommandList
        {
            get { return m_CommandVMList; }
            set { m_CommandVMList = value; OnPropertyChanged("CommandList"); }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            //  Hook up event...
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...