Передача источника ContextMenu в WPF Command - PullRequest
6 голосов
/ 18 июня 2009

Интересная проблема, связанная с запуском команд из пунктов контекстного меню ...

Я хочу запустить команду для вставки строки в мой элемент управления, InsertRowCmd. Эта команда должна знать, куда вставить строку.

Я мог бы использовать Mouse.GetPosition (), но это позволило бы получить текущее положение мыши, которое будет над элементом меню. Я хочу получить источник контекстного меню.

Есть ли у кого-нибудь предложения о том, как передать происхождение контекстного меню в качестве параметра команде?

Пример кода:

<UserControl x:Name="MyControl">
<!--...-->
        <ContextMenu x:Name="menu">
            <MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/>
        </ContextMenu>
</UserControl>

Мои нынешние идеи таковы:

-Используйте обработчик кликов, чтобы я мог найти источник в коде. Проблема в том, что мне придется обрабатывать включение / отключение.

-Отключите событие клика и сохраните источник контекстного меню. Передайте эту сохраненную информацию в команду. Я убедился, что события щелчка запускаются до выполнения команды.

Есть идеи?

EDIT:

Я использую CommandSinkBinding Джоша Смита для направления обработки команды в мой класс ViewModel. Поэтому код, который обрабатывает выполнение команды, ничего не знает о представлении.

Ответы [ 3 ]

5 голосов
/ 23 декабря 2009

Исходя из ответа Кента, я воспользовался предложением о его прикрепленном свойстве и закончил этим (используя пример Джоша Смита для прикрепленного поведения ):

public static class TrackBehavior
{
 public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged));

 public static bool GetTrackOpenLocation(ContextMenu item)
 {
  return (bool)item.GetValue(TrackOpenLocationProperty);
 }

 public static void SetTrackOpenLocation(ContextMenu item, bool value)
 {
  item.SetValue(TrackOpenLocationProperty, value);
 }

 public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point()));

 public static Point GetOpenLocation(ContextMenu item)
 {
  return (Point)item.GetValue(OpenLocationProperty);
 }

 public static void SetOpenLocation(ContextMenu item, Point value)
 {
  item.SetValue(OpenLocationProperty, value);
 }

 static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
 {
  var menu = dependencyObject as ContextMenu;
  if (menu == null)
  {
   return;
  }

  if (!(e.NewValue is bool))
  {
   return;
  }

  if ((bool)e.NewValue)
  {
   menu.Opened += menu_Opened;

  }
  else
  {
   menu.Opened -= menu_Opened;
  }
 }

 static void menu_Opened(object sender, RoutedEventArgs e)
 {
  if (!ReferenceEquals(sender, e.OriginalSource))
  {
   return;
  }

  var menu = e.OriginalSource as ContextMenu;
  if (menu != null)
  {
   SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget));
  }
 }
}

и затем для использования в Xaml, вам просто нужно:

<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True">
 <MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/>
</ContextMenu>

Однако мне также нужно было добавить:

NameScope.SetNameScope(menu, NameScope.GetNameScope(this));

конструктору моего представления, в противном случае привязка для CommandParameter не могла бы искать ElementName=menu.

5 голосов
/ 18 июня 2009

Вам нужно будет использовать TranslatePoint, чтобы перевести верхний левый угол (0, 0) ContextMenu в координату в содержащей сетке. Вы можете сделать это, связав CommandParameter с ContextMenu и использовать конвертер:

CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}"

Другим подходом может быть присоединенное поведение, которое автоматически обновляет присоединенное свойство только для чтения типа Point всякий раз, когда открывается ContextMenu. Использование будет выглядеть примерно так:

<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True">
    <MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/>
</ContextMenu>

Таким образом, прикрепленное свойство TrackOpenLocation выполняет присоединение к ContextMenu и обновляет второе присоединенное свойство (OpenLocation) при каждом открытии ContextMenu. Затем MenuItem можно просто привязать к OpenLocation, чтобы получить местоположение, в котором ContextMenu последний раз был открыт.

1 голос
/ 18 июня 2009

В дополнение к ответу Кента, подумайте о «стандартном пути». F.E. когда ListBox имеет ContextMenu, вам не нужно положение меню, потому что выбранный пункт устанавливается до того, как меню появится. Так что, если у вашего элемента управления будет что-то, что будет "выделено" по правому клику ...

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