Pergunta

problema interessante relacionado aos comandos disparando a partir de itens de menu de contexto ...

Eu quero disparar um comando para inserir uma linha no meu controle, InsertRowCmd. Este comando precisa saber onde inserir a linha.

Eu poderia usar Mouse.GetPosition (), mas que me obter a posição do mouse atualmente, o que seria sobre o item do menu. Eu quero começar a origem do menu de contexto, em vez.

Será que qualquer um tem alguma sugestão sobre como passar a origem do menu de contexto como um parâmetro para o comando?

Exemplo de código:

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

As minhas idéias atuais são os seguintes:

-Use clique manipulador em vez de modo que eu possa encontrar a origem no código. O problema é que eu teria, então, para lidar com a ativação / desativação.

-Identificador clique evento e salvar a origem do menu de contexto. Passar essa informação guardada no comando. Tenho verificado que eventos de clique de incêndio antes que o comando é executado.

Todas as idéias?

EDIT:

Eu estou usando de Josh Smith CommandSinkBinding para encaminhar a manipulação de comando em minha classe ViewModel. Assim, o código que lida com a execução do comando nada sobre a visão sabe.

Foi útil?

Solução

Você vai precisar usar TranslatePoint para traduzir o superior esquerdo (0, 0) do ContextMenu para uma coordenada na grade contendo. Você poderia fazê-lo através da ligação a CommandParameter ao ContextMenu e usar um conversor:

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

Outra abordagem seria um comportamento anexado que atualiza automaticamente uma propriedade somente leitura anexado do tipo Point sempre que o ContextMenu é aberto. Uso seria algo parecido com isto:

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

Assim, a propriedade TrackOpenLocation anexado faz o trabalho de anexar ao ContextMenu e atualizar uma propriedade segunda anexo (OpenLocation) sempre que o ContextMenu é aberto. Em seguida, a MenuItem pode simplesmente ligar a OpenLocation para obter o local em que o ContextMenu foi aberto pela última vez.

Outras dicas

Na sequência da resposta de Kent, eu usei a sugestão propriedade anexada e acabou com isso (usando de Josh Smith exemplo para comportamentos anexados ):

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));
  }
 }
}

e, em seguida, para uso no XAML, você só precisa:

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

No entanto, eu também precisava para adicionar:

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

para o construtor do meu ponto de vista, caso contrário, a ligação para o CommandParameter não poderia lookup ElementName=menu.

Além de resposta de Kent, pensar em uma "forma padrão". F. E. quando um ListBox tem um ContextMenu, você não precisa a posição de menus, porque o item selecionado é definido antes do menu apareceu. Assim, se seu controle teria algo que fica "selecionado" no clique direito ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top