2010-01-09 121 views
8

在WPF應用程序中有一個Grid與一些對象(它們是從一個自定義控件派生的)。我想使用上下文菜單上的每個人執行一些動作:如何在WPF上下文中引用右鍵單擊的對象菜單項單擊事件處理程序?

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

但在事件處理程序我不能知道哪些對象是右鍵單擊:

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; 
    } 

在那個註釋行調試器說:未將對象引用設置爲對象的實例。

請幫忙,我的代碼有什麼問題?

編輯(添加):

我試圖做同樣的,使用命令方法:

我宣佈一個DataCommands類具有RoutedUICommand Requery,然後使用Window.CommandBindings

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

MenuItem的XAML現在看起來像:

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

和事件處理程序是這樣的:

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); 
    } 

但調試器顯示相同的運行時錯誤:對象引用不設置到對象的實例。

我的兩種方法都缺少什麼?

我應該如何引用在WPF上下文菜單項右鍵單擊事件處理程序?

+0

我試着用命令的方式爲更多的WPF十歲上下,但得到了同樣的錯誤。我編輯了我的問題並添加了我的命令方法嘗試的步驟。我對如何獲得點擊的對象引用的理解在這兩種情況下都缺少一些東西 – rem 2010-01-09 11:50:17

回答

21

注意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> 

並在處理程序使用它來弄清楚它是電網

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

更新:
如果你想在菜單項處理程序來獲取到網格的孩子,而不是網格本身,使用這種方法

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

只需將TextBlocks替換爲您的自定義對象類型即可。然後在事件處理程序中,將Grid g = cm.PlacementTarget as Grid替換爲TextBlock t = cm.PlacementTarget as TextBlock(或任何您的自定義對象類型)。

+0

謝謝!問題是,最後(我的意思是你的代碼示例),我們得到「g」 - 對Grid的引用(我的上下文菜單XAML聲明被放置),但我需要引用單擊的對象,它位於Grid內部網格我有數百個類似的對象,每個對象都可以右鍵單擊以獲得上下文菜單)。 – rem 2010-01-14 18:08:28

+0

,而不是將上下文菜單放在網格上,將它放在網格的子節點上。 – kenwarner 2010-01-14 18:35:59

+0

是的,它的工作原理。謝謝! – rem 2010-01-15 10:36:46

2

menu = sender as MenuItem如果發件人不是MenuItem或其派生類,它將爲null。隨後嘗試解除引用菜單將炸燬。

您的發件人很可能是Menu或ContextMenu或ToolStripMenuItem或某種其他形式的菜單項,而不是具體是MenuItem對象。使用調試器斷點在此處停止代碼並檢查發件人對象以查看它到底是什麼類。

+0

我在這一行上使用了一個調試器斷點,它說「sender」類型如下:「sender {System.Windows.Controls.MenuItem Header:Change status Items.Count:0} \t object {System.Windows.Controls.MenuItem}「 – rem 2010-01-09 09:04:25

+0

您可能從幾個項目中獲取該事件,其中一些是MenuItems(就像您在調試器中捕獲的項目一樣)以及一些其中不是(像導致你的崩潰的那個)。如果在處理代碼周圍使用if(menu!= null),則可以停止它嘗試處理來自非MenuItem對象的任何事件,這可能有所幫助。或者它實際上在下一行崩潰,並且它是不是MyCustControl對象的menu.DataContext。只需單步執行調試器,查看每個值,直到找出哪一個爲空。 – 2010-01-09 14:48:24

+0

menu = sender作爲MenuItem 不起作用,因爲默認情況下,MenuItem是System.Windows.Forms中的類, 但是它的工作方式與 sender類似System.Windows.Controls.MenuItem; – horiatu 2015-01-08 03:09:47

1

難道你不應該檢查RoutedEventArgs.Source而不是sender

2

RoutedEventArgs對於

  • RoutedEventArgs.source是基準對引發事件
  • RoutedEventArgs.originalSource對象報告源如由純命中測試確定的,之前的任何可能的源由父母班級調整。

所以.Sender應該是答案。但這取決於菜單項的添加和綁定方式

請參閱此answer collection並選擇適合您的方法!

+0

感謝您的「答案收集」鏈接。 Certianly許多方法來剝皮貓。你認爲微軟現在已經讓這個更清潔了! – user73993 2010-01-15 08:40:55

1

你有兩個不同的問題。這兩個問題導致了同樣的異常,但在其它方面無關:

第一個問題

在你的第一種方法你的代碼是正確的,運行良好,除了這裏的問題:

SCurrent.Status = MyCustControl.Status.Sixth; 

的名稱「狀態」既用作靜態成員又用作實例成員。我認爲你將代碼錯誤地粘貼到你的問題中。

它也可能需要添加以下MenuItem menu = sender as MenuItem;後,根據您的具體情況:

if(menu==null) return; 

問題二

在你的第二個方法,你使用「發件人」,而不是「E 。資源」。下面的代碼工作期望:

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);  
} 

最後請注意

注:沒有任何理由可言綁定CommandParameter此,如果您使用的指揮方式。它顯着較慢,需要更多的代碼。 e.Source將始終是源對象,因此不需要使用CommandParameter,所以請改用它。

+0

Ray,在使用您最後一段代碼的情況下,我得到一個調試器錯誤:無法投入'System.Windows.Controls.Grid'類型的對象來鍵入'MyCustControl'。看起來,e.sourse不是指向被點擊的對象,而是指向網格(我的上下文菜單XAML聲明的位置)。 – rem 2010-01-14 17:32:06

+0

有趣。我記得我實際上是將你的代碼剪切並粘貼到一個項目中並嘗試使用它。我認爲我可能將ContextMenu粘貼到模板中而不考慮它,而不是將其附加到網格上,因爲如果ContextMenu附加到網格上,它顯然不可能按預期工作。 – 2010-01-15 18:20:01

5

通過在XAML綁定的數據上下文,像這樣:

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

然後你可以這樣做:

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

    MyDoStuffFunction(menuItem.DataContext); 
} 

數據上下文將被綁定到被點擊打開的對象上下文菜單。

我是從這個鏈接一個CodeProject上的文章:

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

0

這個工作對我來說: -

XAML: -

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

對於加入菜單項: -

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

這裏來監聽器: -

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

謝謝...

相關問題