Как в WPF создавать контекстное меню в стиле MVP? - CodeHelper

Как в WPF создавать контекстное меню в стиле MVP?

3

Имеется в виду создание иерархической модели и презентера для меню, а так же байндинг и написание стиля отображения меню. Неясно, как присоединять обработчики событий клика на пункты меню. Могу предположить, что при помощи команд. Что-то типа этого: у модели пункта меню DataMenuItem вводим свойство типа ICommand, а затем байндим свойство MenuItem.Command на это свойство модели. Непонятно, как передать параметр в команду. В моем случае мне в качестве параметра нужно передать DataContext того визуального элемента, от которого вызывается контекстное меню.

Лучший ответ:

1

В статье Using MVVM with Menus in WPF рассказывается о том, как реализовать контекстное меню в рамках паттерна MVVM. Идея такая — создается собственный класс для хранения параметров пункта меню:

public class MenuItem
{
   public string Text { get; set; }
   public List<MenuItem> Children { get; private set; }
   public ICommand Command { get; set; }

   public MenuItem(string item)
   {
       Text = item;
       Children = new List<MenuItem>();
   }
}

Используя этот класс, создается дерево данных меню для определенного элемента:

public List<MenuItem> MenuOptions
{
    get  {

       var menu = new List<MenuItem>();
       if (SupportedFileFormats.Count > 0)
      {
          var mi = new MenuItem("O_pen");
          foreach(var fl in SupportedFileFormats)
          {
               var sff = fl;
               mi.Children.Add(new MenuItem(fl.Attributes.Description) 
                      { Command = new DelegatingCommand(() => { LoadFromFormat(sff); })});
          }
          menu.Add(mi);
       }

       menu.Add(new MenuItem("Close _All") { Command = new DelegatingCommand(OnCloseAll, () => FileList.Count > 0)});
       return menu;
    }

}

На стороне xaml создается стиль, который связывает элемент управления пункта меню и наш собственный класс данных:

<Style x:Key="ContextMenuItemStyle">
   <Setter Property="MenuItem.Header" Value="{Binding Text}"/>
   <Setter Property="MenuItem.ItemsSource" Value="{Binding Children}"/>
   <Setter Property="MenuItem.Command" Value="{Binding Command}" />
</Style>

Далее, стиль присваивается конкретному контролу и все работает:

<StackPanel Orientation="Horizontal">
    <Image Source="{Binding Image}" Width="16" Height="16" />
    <TextBlock Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Header}" />
    <StackPanel.ContextMenu>
        <!-- Собственно меню -->
        <ContextMenu ItemContainerStyle="{StaticResource ContextMenuItemStyle}" ItemsSource="{Binding MenuOptions}" />
    </StackPanel.ContextMenu>
</StackPanel>
Alexander

Вот это отлично! Я примерно так и делал, но были проблемы с поставщиком команд и параметрами команд.

Новые ответы


1

Есть мнение, что для передачи параметра в команду меню можно использовать такой код:

<MenuItem CommandParameter="{Binding}" .../>

При этом, если сам элемент MenuItem построен без помощи binding, то в команду передастся именно DataContext родительского элемента контекстного меню.

Если же само контекстное меню генерируется с помощью binding, то его DataContext отличается от родительского. Тогда, для получения родительского DataContext нужно использовать код типа

<MenuItem CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=DataContext}" />
Alexander

Похоже на правду, надо попробовать...


v1.7.123.556
© 2009—2010 CodeHelper FAQ | О сайте | Обратная связь | История изменений | Статьи
Creative Commons LicenseМатериалы сайта распространяются под лицензией Creative Commons Attribution-Share Alike 3.0 Unported.