2012-04-03 29 views
17

Tôi đang cố gắng ràng buộc lệnh với menuitem trong WPF. Tôi đang sử dụng cùng một phương pháp đã được làm việc cho tất cả các ràng buộc lệnh khác của tôi, nhưng tôi không thể tìm ra lý do tại sao nó không hoạt động ở đây.Lệnh ràng buộc MVVM đối với mục contextmenu

tôi đang ràng buộc lệnh của tôi như thế này:

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

Đây là nơi mà nó đi sai (đây là bên trong một 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> 
    ... 

Lệnh đầu tiên ràng buộc các công trình như nó nên, nhưng người thứ hai từ chối làm bất cứ điều gì. Tôi đã thử thay đổi cấp độ tổ tiên và đặt tên cho Điều khiển của tôi để truy cập nó thông qua ElementName thay vì RelativeSource, nhưng vẫn không thay đổi. Nó tiếp tục nói "Không thể tìm thấy nguồn để ràng buộc với tham chiếu ..."

Tôi đang thiếu gì?

+1

tôi sẽ phải kiểm tra, nhưng MenuItem có thể trong một cây khác nhau, vì vậy nó không thể tìm thấy UserControl từ kỹ thuật đó là không phải là tổ tiên (Snoop có thể xác nhận liệu tôi có nhớ điều này đúng hay không). Đối với các ràng buộc lệnh khác (chẳng hạn như lệnh cho điều khiển Nút), tại sao bạn không thể thực hiện Command = "{Binding Path = ConnectCommand}"? Nút nên kế thừa DataContext từ UserControl và do đó không yêu cầu toàn bộ cú pháp RelativeSource/FindAncestor. – MetalMikester

Trả lời

25

(Chỉnh sửa) Kể từ khi bạn đề cập đến đây là trong mẫu của một ItemsControl, mọi thứ đều khác nhau:

1) Lấy lớp BindingProxy từ blog này (và đọc các blog, vì đây là thông tin thú vị): How to bind to data when the DataContext is not inherited.

Về cơ bản các yếu tố trong ItemsControl (hoặc ContextMenu) không phải là một phần của cây hình ảnh hoặc lôgic, và do đó không thể tìm thấy DataContext của UserControl của bạn. Tôi xin lỗi vì không viết nhiều hơn về điều này ở đây, nhưng tác giả đã làm tốt công việc giải thích từng bước một, vì vậy không có cách nào tôi có thể đưa ra lời giải thích đầy đủ chỉ trong một vài dòng.

2) Làm điều gì đó như thế này: (bạn có thể phải thích ứng với nó một chút để làm cho nó hoạt trong kiểm soát của bạn):

a. Điều này sẽ cho phép bạn truy cập vào DataContext UserControl bằng cách sử dụng StaticResource:

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

b. Này sử dụng DataContextProxy quy định tại (a):

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

này đã làm việc cho chúng ta trong những thứ như cây cối và datagrids.

+0

Vấn đề là nút này là một phần của ItemControl ItemTemplate. Tôi có một bộ sưu tập các mô hình được sử dụng như là ràng buộc cho ItemsControl '' Đó là lý do tại sao cách đơn giản ràng buộc các lệnh không hoạt động vì chúng không nằm trong các mô hình đó (Tôi nghĩ rằng, nó vẫn chủ yếu là ma thuật với tôi) – Valyrion

+1

Ồ, đó là khác nhau sau đó, nhưng tôi đã phải đi qua cùng một điều với một XamDataTree (Infragistics cây kiểm soát). Hãy để tôi tìm thông tin cho bạn (nếu không ai khác viết giải pháp trước khi tôi làm) :) – MetalMikester

+0

@Baboon quan tâm giải thích? – SZT

11

Ngữ cảnhMenu nằm trong cây logic khác nhau, đó là lý do tại sao RelativeSource không hoạt động. Nhưng menu ngữ cảnh kế thừa DataContext từ "container" của nó, trong trường hợp này nó là Button. Nó là đủ trong trường hợp phổ biến nhưng trong trường hợp của bạn, bạn cần hai "bối cảnh dữ liệu", của ItemsControl mục và của ItemsControl chính nó. Tôi nghĩ rằng bạn không có lựa chọn nào khác ngoài việc kết hợp các mô hình xem của bạn thành một, triển khai lớp tùy chỉnh để sử dụng như bối cảnh dữ liệu mục ItemsControl và chứa cả "Tên" và "Xóa lệnh" hoặc mô hình xem của mặt hàng của bạn có thể xác định RemoveCommand "proxy", rằng lệnh sẽ gọi mẹ trong nội bộ

EDIT: tôi hơi thay đổi mã con khỉ đầu chó, nó phải làm việc theo cách này:

<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

Điều đó có hiệu quả. Tôi đã sử dụng "Dữ liệu" thay vì "DataContext" khi tôi thử mã gốc. Tuy nhiên, tôi sẽ gắn bó với cách tiếp cận BindingProxy - cú pháp hơi ít nặng nề, nhưng trên tất cả chúng ta đôi khi sử dụng Thẻ trên một số đối tượng để mang một số thông tin bổ sung và tôi không muốn đến một điểm mà chúng ta đang sử dụng cách tiếp cận này và chạy vào một tình huống trong đó Thẻ là cần thiết cho hai mục đích khác nhau. Rất tốt để có các tùy chọn mặc dù! – MetalMikester

3

đó là một vấn đề khó khăn, chắc chắn nhỉnh bạn sẽ tìm thấy một workaround nhanh chóng, nhưng đây là một 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> 
... 

Nó sẽ được sử dụng Tag của số PlacementTarget (số Button tại đây).

+0

Thú vị. Tôi sẽ phải thử nó trên công cụ của riêng mình để xem nó có thể thay thế cách tiếp cận BindingProxy hay không. – MetalMikester

+0

Thực ra tôi tin rằng ban đầu tôi đã suy luận rằng từ một ví dụ MSDN ở đâu đó ... không thể nhớ được điều gì. –

+0

Tôi đã thử một cách nhanh chóng trong mục XamDataTree ItemTemplate của chúng tôi và không đi - nó không thể tìm thấy lệnh. Có lẽ nó sẽ hoạt động trong trường hợp của Slyder - khó nói mà không có mã xung quanh. – MetalMikester

4

koshdim là điểm trên, nó hoạt động như một sự quyến rũ !! Cảm ơn Koshdim

tôi đổi mã của mình để phù hợp trong menu ngữ cảnh của tôi

<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> 
Các vấn đề liên quan