2010-02-21 15 views
7

Estoy usando MVVM para vincular vistas a objetos en un árbol. Tengo una clase base que implementa los artículos en mi árbol, y que la clase base tiene una propiedad ContextMenu:Usando MVVM, ¿cómo puede un ContextMenu ViewModel encontrar el ViewModel que abrió el ContextMenu?

public IEnumerable<IMenuItem> ContextMenu 
    { 
     get 
     { 
      return m_ContextMenu; 
     } 
     protected set 
     { 
      if (m_ContextMenu != value) 
      { 
       m_ContextMenu = value; 
       NotifyPropertyChanged(m_ContextMenuArgs); 
      } 
     } 
    } 
    private IEnumerable<IMenuItem> m_ContextMenu = null; 
    static readonly PropertyChangedEventArgs m_ContextMenuArgs = 
     NotifyPropertyChangedHelper.CreateArgs<AbstractSolutionItem>(o => o.ContextMenu); 

La vista que se une a la clase base (y todas las clases derivadas) implementa un ContextMenu que se une a ese propiedad:

<ContextMenu x:Name="contextMenu" ItemsSource="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}" 
      IsEnabled="{Binding Path=(local:AbstractSolutionItem.ContextMenuEnabled)}" 
      ItemContainerStyle="{StaticResource contextMenuStyle}"/> 

Cada elemento del menú se une a un IMenuItem objeto (un modelo de vista de los elementos de menú). Cuando hace clic en el elemento de menú, usa Comandos para ejecutar un comando en el objeto base. Todo esto funciona genial

Sin embargo, una vez que el comando se ejecuta en la clase IMenuItem, a veces necesita obtener una referencia al objeto que el usuario hizo clic derecho para mostrar el menú contextual (o el ViewModel de ese objeto, al menos). Ese es el objetivo de un contexto menú. ¿Cómo debo pasar la referencia del elemento de árbol ViewModel al MenuItem ViewModel? Tenga en cuenta que algunos menús contextuales son compartidos por muchos objetos en el árbol.

Respuesta

4

Lo resolví manejando el evento ContextMenuOpening en el control principal (el que tenía el ContextMenu en la Vista). También agregué una propiedad de contexto a IMenuItem. El controlador es el siguiente:

private void stackPanel_ContextMenuOpening(
     object sender, ContextMenuEventArgs e) 
    { 
     StackPanel sp = sender as StackPanel; 
     if (sp != null) 
     { 
      // solutionItem is the "context" 
      ISolutionItem solutionItem = 
       sp.DataContext as ISolutionItem; 
      if (solutionItem != null) 
      { 
       IEnumerable<IMenuItem> items = 
        solutionItem.ContextMenu as IEnumerable<IMenuItem>; 
       if (items != null) 
       { 
        foreach (IMenuItem item in items) 
        { 
         // will automatically set all 
         // child menu items' context as well 
         item.Context = solutionItem; 
        } 
       } 
       else 
       { 
        e.Handled = true; 
       } 
      } 
      else 
      { 
       e.Handled = true; 
      } 
     } 
     else 
     { 
      e.Handled = true; 
     } 
    } 

Esto se aprovecha del hecho de que sólo puede haber un ContextMenu abierto a la vez.

10

Hay un DP en el objeto ContextMenu llamado "PlacementTarget" - esto se establecerá en el elemento de la interfaz de usuario al que se adjunta el menú contextual - incluso puede usarlo como fuente de enlace, por lo que podría pasarlo a su comando a través de CommandParameter:

http://msdn.microsoft.com/en-us/library/system.windows.controls.contextmenu.placementtarget.aspx

edición: en su caso, que querría la máquina virtual de la PlacementTarget, por lo que su unión probablemente se vería más como:

{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}} 
+0

-1 Fuente no se puede especificar si usando RelativeSource. Excepción en tiempo de ejecución. –

+0

DataContext = "{Binding PlacementTarget.DataContext, RelativeSource = {RelativeSource Self}}" – JoanComasFdz

Cuestiones relacionadas