2010-01-09 23 views
8

Trong ứng dụng WPF có một Grid với một số đối tượng (chúng được bắt nguồn từ một điều khiển tùy chỉnh). Tôi muốn thực hiện một số hành động trên mỗi người trong số họ sử dụng menu ngữ cảnh:Làm cách nào để tham chiếu đối tượng được nhấp chuột phải trong trình đơn Xử lý sự kiện nhấn vào Trình đơn ngữ cảnh WPF?

<Grid.ContextMenu> 
    <ContextMenu> 
     <MenuItem Name="EditStatusCm" Header="Change status" Click="EditStatusCm_Click"/> 
    </ContextMenu>     
    </Grid.ContextMenu> 

Nhưng trong xử lý sự kiện tôi không thể nhận biết được của các đối tượng đã phải nhấp:

private void EditStatusCm_Click(object sender, RoutedEventArgs e) 
    { 
     MyCustControl SCurrent = new MyCustControl(); 
     MenuItem menu = sender as MenuItem; 
     SCurrent = menu.DataContext as MyCustControl; // here I get a run-time error 
     SCurrent.Status = MyCustControl.Status.Sixth; 
    } 

On rằng dòng nhận xét Trình gỡ lỗi cho biết: Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng.

Xin vui lòng giúp đỡ, có gì sai trong mã của tôi?

Edited (thêm):

Tôi cố gắng để làm như vậy, sử dụng lệnh cách tiếp cận:

tôi tuyên bố một Class DataCommands với RoutedUICommand Requery và sau đó sử dụng Window.CommandBindings

<Window.CommandBindings> 
    <CommandBinding Command="MyNamespace:DataCommands.Requery" Executed="RequeryCommand_Executed"></CommandBinding> 
</Window.CommandBindings> 

XAML của MenuItem bây giờ trông giống như:

<Grid.ContextMenu> 
<ContextMenu> 
    <MenuItem Name="EditStatusCm" Header="Change status" Command="MyNamespace:DataCommands.Requery"/> 
</ContextMenu>     
</Grid.ContextMenu> 

Và xử lý sự kiện trông giống như:

private void RequeryCommand_Executed(object sender, ExecutedRoutedEventArgs e) 
    { 
     IInputElement parent = (IInputElement)LogicalTreeHelper.GetParent((DependencyObject)sender); 
     MyCustControl SCurrent = new MyCustControl(); 
     SCurrent = (MuCustControl)parent; 
     string str = SCurrent.Name.ToString();// here I get the same error 
     MessageBox.Show(str); 
    } 

Nhưng debugger cho thấy cùng một lỗi thời gian chạy: tham khảo Object không được đặt để một thể hiện của một đối tượng.

Điều gì còn thiếu trong cả hai cách tiếp cận của tôi?

Tôi nên tham khảo đối tượng được nhấp chuột phải trong trình đơn xử lý sự kiện theo ngữ cảnh WPF như thế nào?

+0

tôi đã cố gắng sử dụng cách tiếp cận lệnh như nhiều WPF-ish, nhưng có những lỗi tương tự. Tôi đã chỉnh sửa câu hỏi của mình và thêm các bước trong nỗ lực tiếp cận Command của tôi. Sự hiểu biết của tôi về làm thế nào để có được tham chiếu đối tượng nhấp là thiếu một cái gì đó trong cả hai trường hợp – rem

Trả lời

21

lưu ý các CommandParameter

<Grid Background="Red" Height="100" Width="100"> 
    <Grid.ContextMenu> 
     <ContextMenu> 
      <MenuItem 
       Header="Change status" 
       Click="EditStatusCm_Click" 
       CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" /> 
     </ContextMenu> 
    </Grid.ContextMenu> 
</Grid> 

và sử dụng nó trong xử lý để tìm ra Lưới nó là

private void EditStatusCm_Click(object sender, RoutedEventArgs e) 
    { 
     MenuItem mi = sender as MenuItem; 
     if (mi != null) 
     { 
      ContextMenu cm = mi.CommandParameter as ContextMenu; 
      if (cm != null) 
      { 
       Grid g = cm.PlacementTarget as Grid; 
       if (g != null) 
       { 
        Console.WriteLine(g.Background); // Will print red 
       } 
      } 
     } 
    } 

Cập nhật:
Nếu bạn muốn xử lý menuitem để đến Con của lưới thay vì chính Lưới, sử dụng phương pháp này

<Grid Background="Red" Height="100" Width="100"> 
    <Grid.Resources> 
     <ContextMenu x:Key="TextBlockContextMenu"> 
      <MenuItem 
       Header="Change status" 
       Click="EditStatusCm_Click" 
       CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" /> 
     </ContextMenu> 

     <Style TargetType="{x:Type TextBlock}"> 
      <Setter Property="ContextMenu" Value="{StaticResource TextBlockContextMenu}" /> 
     </Style> 
    </Grid.Resources> 

    <Grid.RowDefinitions> 
     <RowDefinition /> 
     <RowDefinition /> 
    </Grid.RowDefinitions> 

    <TextBlock Text="Row0" Grid.Row="0" /> 
    <TextBlock Text="Row1" Grid.Row="1" /> 
</Grid> 

Chỉ cần thay thế TextBlocks bằng bất kỳ loại đối tượng tùy chỉnh nào của bạn. Sau đó, trong trình xử lý sự kiện, thay thế Grid g = cm.PlacementTarget as Grid bằng TextBlock t = cm.PlacementTarget as TextBlock (hoặc bất kỳ loại đối tượng tùy chỉnh nào của bạn).

+0

Cảm ơn! Vấn đề là cuối cùng (tôi có nghĩa là ví dụ mã của bạn), chúng tôi nhận được "g" -the tham chiếu đến Grid (nơi mà trình đơn Ngữ cảnh XAML của tôi được đặt), nhưng tôi cần tham chiếu đến đối tượng được nhấp vào bên trong Grid (bên trong Grid Tôi có hàng trăm đối tượng tương tự, mỗi đối tượng có thể được nhấp chuột phải để có được menu ngữ cảnh). – rem

+0

thay vì đặt contextmenu trên chính lưới, đặt nó trên con của Grid. – kenwarner

+0

Có, nó hoạt động. Cảm ơn bạn! – rem

2

menu = sender as MenuItem sẽ không có giá trị nếu người gửi không phải là một MenuItem hoặc một lớp dẫn xuất của nó. Sau đó cố gắng để dereference menu sẽ thổi lên.

Có khả năng người gửi của bạn là Menu hoặc ContextMenu hoặc một ToolStripMenuItem hoặc một số hình thức khác của mục menu, thay vì cụ thể là đối tượng MenuItem. Sử dụng điểm ngắt trình gỡ rối để dừng mã ở đây và kiểm tra đối tượng người gửi để xem chính xác lớp đó là gì.

+0

Tôi đã sử dụng một điểm ngắt trình gỡ lỗi trên dòng này và nó nói về "người gửi" Loại như sau: "người gửi {System.Windows.Controls.MenuItem Tiêu đề: Thay đổi trạng thái Items.Count: 0} \t object {System.Windows.Controls.MenuItem} " – rem

+0

Có thể bạn nhận được sự kiện đó từ một số mục, một số trong số đó là MenuItems (giống như bạn đã gặp trong trình gỡ rối) và một số trong số đó không (giống như cái gây ra sự cố của bạn). Nếu bạn sử dụng if (menu! = Null) xung quanh mã xử lý của mình, bạn có thể dừng quá trình xử lý bất kỳ sự kiện nào từ các đối tượng không phải MenuItem, điều này có thể hữu ích. Hoặc nó thực sự là crashing trên dòng tiếp theo, và nó là menu.DataContext không phải là một đối tượng MyCustControl. Chỉ cần một bước thông qua trình gỡ lỗi và xem xét từng giá trị cho đến khi bạn tìm ra giá trị nào là rỗng. –

+0

menu = người gửi như MenuItem không hoạt động vì theo mặc định MenuItem là một lớp trong System.Windows.Forms, tuy nhiên nó hoạt động như người gửi dưới dạng System.Windows.Controls.MenuItem; – horiatu

1

Bạn không nên kiểm tra RoutedEventArgs.Source thay vì sender?

2

Đối RoutedEventArgs

  • RoutedEventArgs.source là tham chiếu đến đối tượng đó huy động sự kiện
  • RoutedEventArgs.originalSource được nguồn báo cáo được xác định bằng cách kiểm tra hit tinh khiết, trước khi bất kỳ nguồn càng tốt điều chỉnh bởi một lớp cha.

Vì vậy, người gửi phải là câu trả lời. Nhưng điều này tùy thuộc vào cách các menuitems được thêm vào và ràng buộc

Xem điều này answer collection và chọn phương pháp sẽ làm việc cho bạn!

+0

Cảm ơn bạn đã liên kết "bộ sưu tập câu trả lời". Certianly nhiều cách để da mèo. Bạn nghĩ rằng Microsoft đã có thể làm sạch này ngay bây giờ! – user73993

1

Bạn có hai vấn đề khác nhau. Cả hai vấn đề dẫn đến sự ngoại lệ tương tự, nhưng là bằng cách khác không liên quan:

Đầu tiên vấn đề

Trong phương pháp đầu tiên bạn mã của bạn là chính xác và chạy tốt trừ các vấn đề ở đây:

SCurrent.Status = MyCustControl.Status.Sixth; 

Các tên "Trạng thái" được sử dụng như một thành viên tĩnh và là một thành viên thể hiện. Tôi nghĩ rằng bạn cắt và dán mã không chính xác vào câu hỏi của bạn.

Nó cũng có thể cần phải thêm dòng sau sau MenuItem menu = sender as MenuItem;, tùy thuộc vào tình hình chính xác của bạn:

if(menu==null) return; 

vấn đề thứ hai

Trong phương pháp thứ hai của bạn, bạn sử dụng "người gửi" thay vì "e .Source ". Các mã sau đây hoạt động như mong muốn:

private void RequeryCommand_Executed(object sender, ExecutedRoutedEventArgs e)  
{  
    IInputElement parent = (IInputElement)LogicalTreeHelper.GetParent((DependencyObject)e.Source); 
     // Changed "sender" to "e.Source" in the line above 
    MyCustControl SCurrent = new MyCustControl();  
    SCurrent = (MuCustControl)parent;  
    string str = SCurrent.Name.ToString();// Error gone 
    MessageBox.Show(str);  
} 

cuối cùng Lưu ý

Lưu ý: Không có lý do nào cả để ràng buộc CommandParameter cho điều này nếu bạn sử dụng phương pháp chỉ huy. Nó chậm hơn đáng kể và có nhiều mã hơn. e.Source sẽ luôn là đối tượng nguồn nên không cần sử dụng CommandParameter, vì vậy hãy sử dụng đối tượng đó để thay thế.

+0

Ray, trong trường hợp sử dụng đoạn mã cuối cùng của bạn, tôi nhận được lỗi trình gỡ lỗi: Không thể truyền đối tượng thuộc loại 'System.Windows.Controls.Grid' để nhập 'MyCustControl'. Có vẻ như các điểm e.sourse không phải là đối tượng được nhấp vào mà là đến Grid (nơi mà trình đơn XAML của Context Menu của tôi được đặt). – rem

+0

Thú vị. Tôi nhớ tôi đã thực sự cắt và dán mã của bạn thành một dự án và thử nó. Tôi nghĩ rằng tôi có thể đã dán ContextMenu vào một khuôn mẫu mà không cần suy nghĩ về nó thay vì gắn nó vào Grid, vì nó rõ ràng sẽ không thể cho nó hoạt động như mong muốn nếu ContextMenu được gắn vào Grid. –

5

Bằng cách ràng buộc bối cảnh dữ liệu như vậy trong XAML:

ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource= {RelativeSource Self}}"> 

Sau đó bạn có thể làm điều này:

private void Context_MenuClick(object sender, RoutedEventArgs e) 
{ 
    var menuItem = e.Source as MenuItem; 

    MyDoStuffFunction(menuItem.DataContext); 
} 

Bối cảnh dữ liệu sẽ được ràng buộc với đối tượng mà được nhấp để mở Trình đơn ngữ cảnh.

tôi đã nhận nó từ một bài viết CodeProject tại liên kết này:

http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda

0

này làm việc cho tôi: -

XAML: -

<DataGrid.ContextMenu> 
<ContextMenu x:Name="AddColumnsContextMenu" MenuItem.Click="AddColumnsContextMenu_Click"> 
</ContextMenu> 

Để thêm các mục menu: -

foreach (String s in columnNames) 
{ 
var item = new MenuItem { IsCheckable = true, IsChecked = true ,Header=s}; 
AddColumnsContextMenu.Items.Add(item); 
} 

Và ở đây có người nghe: -

private void AddColumnsContextMenu_Click(object sender, RoutedEventArgs e) 
{ 
    MenuItem mi = e.Source as MenuItem; 
    string title = mi.Header.ToString(); 
    MessageBox.Show("Selected"+title); 
} 

Thanks ...

Các vấn đề liên quan