2012-04-03 19 views
17

Próbuję powiązać polecenie z menuitem w WPF. Używam tej samej metody, która działa dla wszystkich innych powiązań poleceń, ale nie mogę zrozumieć, dlaczego to nie działa tutaj.Polecenie wiązania MVVM do elementu menu kontekstowego

Jestem obecnie wiążące moich poleceń tak:

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

To gdzie to pójdzie źle (jest wewnątrz 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> 
    ... 

pierwszym łączącym prace jak powinien polecenie, ale drugi nie chce nic zrobić. Próbowałem zmienić poziom przodków i nazwać moją Kontrolę, aby uzyskać do niej dostęp przez ElementName zamiast RelativeSource, ale wciąż nie zmienia się. Powtarza się "Nie można znaleźć źródła do powiązania z odniesieniem ..."

Czego mi brakuje?

+1

Musiałbym sprawdzić, ale MenuItem może być w innym drzewem, tak że nie można znaleźć UserControl ponieważ to technicznie nie przodek (Snoop mógłby potwierdzić, czy pamiętam to prawo, czy nie). W przypadku innych powiązań poleceń (takich jak polecenie kontrolki Button), dlaczego nie można po prostu wykonać polecenia Command = "{Binding Path = ConnectCommand}"? Przycisk powinien dziedziczyć DataContext z UserControl i dlatego nie wymaga całej składni RelativeSource/FindAncestor. – MetalMikester

Odpowiedz

25

(Edit) Skoro wspomniałeś to w szablonie ItemsControl za rzeczy są różne:

1) Get klasę BindingProxy z tego bloga (i czytać bloga, ponieważ jest to ciekawa informacja): How to bind to data when the DataContext is not inherited.

Zasadniczo elementy w ItemControl (lub ContextMenu) nie są częścią wizualnego lub logicznego drzewa i dlatego nie mogą znaleźć DataContext w UserControl. Przepraszam, że nie pisałem więcej na ten temat, ale autor wykonał dobrą robotę, tłumacząc to krok po kroku, więc nie ma możliwości, bym dał pełne wyjaśnienie w kilku linijkach.

2) Czy coś takiego: (być może trzeba będzie dostosować go trochę, aby pracować w kontroli):

się. Umożliwi to dostęp do Kontekstu danych UserControl przy użyciu StaticResource:

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

b. Ten wykorzystuje DataContextProxy zdefiniowaną w punkcie (a):

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

ten pracował dla nas rzeczy, jak drzewa i datagrids.

+0

Problem polega na tym, że ten przycisk jest częścią ItemTemplate ItemsControl. Mam kolekcję modeli, które są używane jako bind dla ItemsControl '" Dlatego prosty sposób wiązania poleceń nie zadziałał, ponieważ nie ma ich w tych modelach (Myślę, że nadal jest to w dużej mierze magia) – Valyrion

+1

Och, to jest inne, ale musiałem przejść przez to samo z XamDataTree (kontrola drzewa Infragistics). Pozwól mi znaleźć informacje dla ciebie (jeśli nikt inny nie wysyła rozwiązania, zanim to zrobię) :) – MetalMikester

+0

@Baboon dbać o wyjaśnienia? – SZT

11

Menu kontekstowe jest w innym drzewie logicznym, dlatego funkcja RelativeSource nie działa. Ale menu kontekstowe dziedziczy DataContext z jego "kontenera", w tym przypadku jest to Button. Wystarczy w tym przypadku, ale w twoim przypadku potrzebujesz dwóch "kontekstów danych", przedmiotów ItemsControl i samych ItemsControl. Myślę, że nie ma innego wyjścia, jak połączyć modele widoku w jedną, zaimplementować niestandardową klasę, która będzie używana jako Kontekst danych elementu ItemsControl i zawierać zarówno "Nazwę", jak i "Usuń polecenie" lub model widoku elementu może zdefiniować RemoveCommand "proxy", że komenda nazwałbym rodzic wewnętrznie

EDIT: I lekko zmieniony kod pawiana, to musi działać w ten sposób:

<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

To działa. Użyłem "Dane" zamiast "DataContext", gdy próbowałem oryginalnego kodu. Będę trzymać się podejścia BindingProxy - składnia jest trochę mniej ciężka, ale przede wszystkim czasami używamy Tagu na niektórych obiektach do przenoszenia dodatkowych informacji i nie chcę dojść do punktu, w którym używamy takie podejście i uruchomić scenariusz, w którym tag jest potrzebny do dwóch różnych celów. Miło mieć opcje! – MetalMikester

3

że to delikatna kwestia, na pewno nieznacznie znajdziesz szybkiego obejścia, ale tutaj jest nie-magiczne rozwiązanie:

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

Sprowadza się do korzystania z Tag z PlacementTarget (tutaj: Button).

+0

Interesujące. Będę musiał wypróbować to na własnych materiałach, aby sprawdzić, czy może zastąpić podejście BindingProxy. – MetalMikester

+0

Właściwie wydaje mi się, że pierwotnie wywnioskowałem, że z przykładu MSDN gdzieś ... nie pamiętam, który. –

+0

Szybko wypróbowałem w naszym ItemTemplate XamDataTree i nie ma go - nie może znaleźć polecenia. Może to zadziała w przypadku Slydera - trudno powiedzieć bez otaczającego kodu. – MetalMikester

4

koshdim jest na miejscu, działa jak czar !! Dzięki Koshdim

zmodyfikowałem swój kod, aby zmieścić się w moim menu kontekstowego

<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> 
Powiązane problemy