2012-04-03 19 views
17

Estoy intentando vincular un comando a un elemento de menú en WPF. Estoy usando el mismo método que he estado trabajando para todos mis otros enlaces de comando, pero no puedo entender por qué no funciona aquí.Mandato de enlace de MVVM al elemento de elemento contextual

estoy actualmente vinculante mis comandos de la siguiente manera:

Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.MyCommand}" 

Aquí es donde las cosas van mal (esto está dentro de un control de usuario)

<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}" 
         Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}"> 

    <Button.ContextMenu> 
     <ContextMenu> 
      <MenuItem Header="Remove" CommandParameter="{Binding Name}" 
             Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.RemoveCommand}"/> 
     </ContextMenu> 
    </Button.ContextMenu> 
    ... 

La primera funciona como es debido la unión de comandos, pero el segundo se niega a hacer nada. He intentado cambiar el nivel de ancestro y nombrar mi control para acceder a él a través de ElementName en lugar de RelativeSource, pero aún no hay cambios. Sigue diciendo "No se puede encontrar la fuente de enlace con referencia ..."

¿Qué me falta?

+1

que tendría que comprobar, pero el Menultem, esté en un árbol diferente, por lo que no puede encontrar el control de usuario ya que técnicamente es no un antepasado (Snoop podría confirmar si recuerdo este derecho o no). Para los otros enlaces de comando (como el comando para el control Button), ¿por qué no puedes simplemente hacer Command = "{Binding Path = ConnectCommand}"? El botón debe heredar el DataContext del UserControl y, por lo tanto, no requiere toda la sintaxis RelativeSource/FindAncestor. – MetalMikester

Respuesta

25

(Editar) Ya que menciona esto es en la plantilla de ItemsControl, las cosas son diferentes:

1) Obtener la clase BindingProxy desde este blog (y leer el blog, ya que esta es información interesante): How to bind to data when the DataContext is not inherited.

Básicamente los elementos en ItemsControl (o ContextMenu) no son parte del árbol visual o lógico, y por lo tanto no pueden encontrar el DataContext de su UserControl. Mis disculpas por no escribir más sobre esto aquí, pero el autor ha hecho un buen trabajo explicando paso a paso, así que no hay forma de que yo pueda dar una explicación completa en solo unas pocas líneas.

2) hacer algo como esto: (puede que tenga que adaptar un poco para hacer que funcione en su control):

a. Esto le dará acceso al UserControl DataContext usando un StaticResource:

<UserControl.Resources> 
<BindingProxy 
    x:Key="DataContextProxy" 
    Data="{Binding}" /> 
</UserControl.Resources> 

b. Este utiliza el DataContextProxy definido en (a):

<Button.ContextMenu> 
<ContextMenu> 
    <MenuItem Header="Remove" CommandParameter="{Binding Name}" 
     Command="{Binding Path=Data.RemoveCommand, Source={StaticResource DataContextProxy}}"/> 
</ContextMenu> 

Esto ha funcionado para nosotros en cosas como árboles y DataGrids.

+0

El problema es que este botón es parte de ItemsControl ItemTemplate. Tengo una colección de modelos que se usa como enlace para ItemsControl '' Es por eso que la forma simple de enlazar los comandos no funcionó porque no están en esos modelos (Creo que sigue siendo casi mágico para mí) – Valyrion

+1

Oh, eso es diferente entonces, pero tuve que pasar por lo mismo con un XamDataTree (control de árbol Infragistics). Permíteme encontrar la información para ti (si nadie más publica la solución antes que yo) :) – MetalMikester

+0

@Baboon ¿te importa explicarlo? – SZT

11

ContextMenu está en un árbol lógico diferente, por eso RelativeSource no funciona. Pero el menú contextual hereda DataContext de su "contenedor", en este caso es Button. Es suficiente en el caso común, pero en su caso necesita dos "contextos de datos", del elemento ItemsControl y de ItemsControl. Creo que no tiene otra opción que combinar sus modelos de vista en uno, implementar clases personalizadas para usar como contexto de datos de elemento ItemsControl y contener "Nombre" y "Eliminar comando" o el modelo de vista de su artículo puede definir RemoveCommand "proxy", ese comando llamaría padres internamente

EDIT: poco he cambiado el código del babuino, tiene que trabajar de esta manera:

<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}" 
    Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" 
    Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}"> 
      <Button.ContextMenu> 
       <ContextMenu> 
        <MenuItem Header="Remove" 
        CommandParameter="{Binding Name}" 
        Command="{Binding Path=PlacementTarget.Tag.DataContext.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/> 
       </ContextMenu> 
      </Button.ContextMenu> 
+0

Eso funciona. Estaba usando "Datos" en lugar de "DataContext" cuando probé el código original. Sin embargo, me quedaré con el enfoque BindingProxy - la sintaxis es un poco menos pesada, pero sobre todo a veces utilizamos la etiqueta en algunos objetos para llevar información adicional y no quiero llegar al punto en el que estamos usando este enfoque y se encuentra con un escenario donde la etiqueta es necesaria para dos propósitos diferentes. ¡Es bueno tener opciones! – MetalMikester

3

eso es un tema complicado, seguro marginalmente encontrará una solución rápida, pero aquí hay una solución no-mágica:

<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}" 
     Tag={Binding} 
     Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">  
    <Button.ContextMenu> 
     <ContextMenu> 
      <MenuItem Header="Remove" 
         CommandParameter="{Binding Path=PlacementTarget.Tag.Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" 
         Command="{Binding Path=PlacementTarget.Tag.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/> 
     </ContextMenu> 
    </Button.ContextMenu> 
... 

Se reduce a usar el Tag del PlacementTarget (el Button aquí).

+0

Interesante. Tendré que probar eso en nuestras cosas para ver si puede reemplazar el enfoque BindingProxy. – MetalMikester

+0

En realidad creo que originalmente deduje eso de un ejemplo de MSDN en alguna parte ... no puedo recordar cuál. –

+0

Hice esto una prueba rápida en nuestro XamDataTree ItemTemplate y no voy - no puede encontrar el comando. Tal vez funcione en el caso de Slyder, es difícil decirlo sin el código que lo rodea. – MetalMikester

4

koshdim es perfecto, funciona como un encanto! Gracias Koshdim

he modificado su código de encajar en mi menú contextual

<DataGrid 
     AutoGenerateColumns="False" 
     HeadersVisibility="Column" 
     Name="dgLosses" 
     SelectedItem="{Binding SelectedItem, Mode= TwoWay}" 
     AllowDrop="True" 
     ItemsSource="{Binding Losses}" 
     Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"> 


     <DataGrid.ContextMenu > 
      <ContextMenu > 
       <MenuItem Header="Move to Top  " Command="{Binding PlacementTarget.Tag.MoveToTopCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem> 
       <MenuItem Header="Move to Period 1" Command="{Binding PlacementTarget.Tag.MoveToPeriod1Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem> 
       <MenuItem Header="Move to Period 2" Command="{Binding PlacementTarget.Tag.MoveToPeriod2Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem> 
       <MenuItem Header="Move to Period 3" Command="{Binding PlacementTarget.Tag.MoveToPeriod3Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>      
      </ContextMenu> 
     </DataGrid.ContextMenu> 
Cuestiones relacionadas