2012-04-03 14 views
17

Sto tentando di associare un comando a un menu in WPF. Sto usando lo stesso metodo che ha funzionato con tutti i miei altri binding di comandi, ma non riesco a capire perché non funzioni qui.MVVM binding command to contextmenu item

che sto attualmente vincolante miei comandi in questo modo:

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

Questo è dove le cose vanno male (questo è all'interno di un UserControl)

<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> 
    ... 

Il primo comando funziona come dovrebbe vincolante, ma il secondo si rifiuta di fare qualsiasi cosa. Ho provato a cambiare il livello di antenato e nominare il mio Controllo per accedervi tramite ElementName invece di RelativeSource, ma ancora nessuna modifica. Continua a dire "Impossibile trovare la fonte per l'associazione con riferimento ..."

Cosa mi manca?

+1

avrei dovuto controllare, ma il MenuItem può essere in un albero diverso, in modo che non riesce a trovare l'UserControl in quanto tecnicamente è non un antenato (Snoop potrebbe confermare se ricordo bene o no). Per gli altri binding di comandi (come il comando per il controllo Button), perché non puoi semplicemente Command = "{Binding Path = ConnectCommand}"? Il pulsante deve ereditare DataContext da UserControl e pertanto non richiede l'intera sintassi RelativeSource/FindAncestor. – MetalMikester

risposta

25

(Edit) Dal momento che lei ha citato questo è in modello di un ItemsControl, le cose sono diverse:

1) ottenere la classe BindingProxy da questo blog (e leggere il blog, in quanto questo è informazioni interessanti): How to bind to data when the DataContext is not inherited.

Fondamentalmente gli elementi in ItemsControl (o ContextMenu) non fanno parte dell'albero visivo o logico e pertanto non riescono a trovare il DataContext del tuo UserControl. Le mie scuse per non aver scritto più su questo qui, ma l'autore ha fatto un buon lavoro spiegandolo passo dopo passo, quindi non c'è modo di fornire una spiegazione completa in poche righe.

2) fare qualcosa di simile: (potrebbe essere necessario adattare un po 'per farlo funzionare nel vostro controllo):

a. Questo vi darà l'accesso alla UserControl DataContext utilizzando uno StaticResource:

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

b. Questo utilizza la DataContextProxy definito in (a):

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

Questo ha lavorato per noi in cose come gli alberi e DataGrid.

+0

Il problema è che questo pulsante è parte di ItemControl ItemTemplate. Ho una collezione di modelli che viene usata come binding per ItemsControl '' Ecco perché il modo semplice di legare i comandi non ha funzionato perché non sono in quei modelli (Penso che per me sia ancora perlopiù magica) – Valyrion

+1

Oh, è diverso allora, ma ho dovuto passare attraverso la stessa cosa con un XamDataTree (controllo dell'albero Infragistics). Fammi trovare le informazioni per te (se nessun altro pubblica la soluzione prima di me) :) – MetalMikester

+0

@Baboon ti interessa spiegare? – SZT

11

ContextMenu è in un albero logico diverso, ecco perché RelativeSource non funziona. Ma il menu contestuale eredita DataContext dal suo "contenitore", in questo caso è Button. È abbastanza nel caso comune, ma nel tuo caso hai bisogno di due "contesti dati", di item ItemsControl e di ItemsControl stesso. Penso che tu non abbia altra scelta che combinare i tuoi modelli di vista in uno, implementare una classe personalizzata da utilizzare come contesto di dati item ItemsControl e contenere sia "Name" che "Remove command" o il modello di visualizzazione dell'articolo può definire RemoveCommand "proxy", che il comando avrebbe chiamato genitore internamente

EDIT: ho un po 'cambiato il codice del Babbuino, si deve lavorare in questo modo:

<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

Che funziona. Stavo usando "Dati" invece di "DataContext" quando ho provato il codice originale. Continuerò comunque con l'approccio BindingProxy - la sintassi è un po 'meno pesante, ma soprattutto a volte utilizziamo il Tag su alcuni oggetti per portare alcune informazioni aggiuntive e non voglio arrivare a un punto in cui stiamo usando questo approccio e imbattersi in uno scenario in cui il tag è necessario per due scopi diversi. Bello avere opzioni però! – MetalMikester

3

questa è una questione spinosa, certo marginalmente si trova una soluzione rapida, ma qui è un no-magic-solution:

<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> 
... 

Si riduce all'utilizzo dello Tag dello PlacementTarget (lo Button qui).

+0

Interessante. Dovrò provarlo sulle nostre stesse cose per vedere se può sostituire l'approccio BindingProxy. – MetalMikester

+0

In realtà credo di aver inizialmente dedotto che da un esempio MSDN da qualche parte ... non riesco a ricordare quale però. –

+0

Ho dato una rapida occhiata al nostro XamDataTree ItemTemplate e no go - non riesce a trovare il comando. Forse funzionerà nel caso di Slyder, difficile da dire senza il codice circostante. – MetalMikester

4

koshdim è perfetto, funziona come un fascino !! Grazie Koshdim

ho modificato il suo codice per adattarsi nel mio menu contestuale

<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> 
Problemi correlati