2012-04-03 77 views
17

我試圖將命令綁定到WPF中的菜單項。我使用的是與我所有其他命令綁定相同的方法,但我無法弄清楚爲什麼它不起作用。MVVM綁定命令上下文菜單項

我目前正在結合我的命令是這樣的:

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

這是它出錯(這是一個用戶控件內)

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

第一個命令綁定像它應該工作,但第二個拒絕做任何事情。 我試過改變祖先級別,並命名我的控制通過ElementName而不是RelativeSource來訪問它,但仍然沒有改變。它一直說「無法找到綁定參考源...」

我錯過了什麼?

+1

我不得不去檢查,但菜單項可以在不同的樹,所以它無法找到用戶控件,因爲在技術上它是不是一個祖先(史努比可以證實我是否記得這個權利)。對於其他命令綁定(例如Button控件的命令),爲什麼不能只執行Command =「{Binding Path = ConnectCommand}」? Button應該繼承UserControl的DataContext,因此不需要使用整個RelativeSource/FindAncestor語法。 – MetalMikester 2012-04-03 13:31:35

回答

25

(編輯)既然你提到這是一個ItemsControl的模板,情況就不同了:

1)獲取從這個博客的BindingProxy類(和閱讀博客,因爲這是有趣的信息):How to bind to data when the DataContext is not inherited

基本上,ItemsControl(或ContextMenu)中的元素不是可視或邏輯樹的一部分,因此無法找到UserControl的DataContext。我很抱歉沒有在這裏寫更多的內容,但作者已經做得很好,一步一步地解釋它,所以我不可能只用幾行就給出完整的解釋。

2)做這樣的事情:(您可能不得不適應它一下,使之在你的控制工作):

一個。這將使您可以使用StaticResource訪問UserControl DataContext:

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

b。這將使用(a)中定義的DataContextProxy:

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

這等東西樹木和數據網格爲我們工作。

+0

問題是這個按鈕是ItemsControl ItemTemplate的一部分。我有一個用作ItemsControl的綁定的模型集合'這就是爲什麼綁定命令的簡單方法不起作用,因爲它們不在這些模型中我認爲,它對我來說還是很神奇的) – Valyrion 2012-04-03 13:49:48

+1

哦,那就不一樣了,但是我必須用XamDataTree(Infragistics樹形控件)來做同樣的事情。讓我爲你找到信息(如果沒有其他人在我做之前發佈解決方案):) – MetalMikester 2012-04-03 13:54:55

+0

@Baboon關心解釋? – SZT 2014-06-06 16:52:22

11

ContextMenu位於不同的邏輯樹中,這就是爲什麼RelativeSource不起作用。但是上下文菜單從其「容器」繼承DataContext,在這種情況下它是Button。在普通情況下,這足夠了,但在你的情況下,你需要兩個「數據上下文」,ItemsControl項和ItemsControl本身。 我想你沒有其他選擇,只能將你的視圖模型合併爲一個,實現用作ItemsControl項目數據上下文的自定義類幷包含「名稱」和「刪除命令」,或者您的項目的視圖模型可以定義RemoveCommand「proxy」,這將要求家長命令內部

編輯: 我稍微改變狒狒的代碼,它必須以這種方式工作:

<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

這有效。當我嘗試原始代碼時,我使用「Data」而不是「DataContext」。我會堅持使用BindingProxy方法 - 語法稍微不重要,但最重要的是,我們有時會在某些對象上使用標記來提供一些附加信息,而我不想到達使用該點的位置這種方法並且遇到兩種不同目的需要標籤的場景。很高興有選擇,但! – MetalMikester 2012-04-03 17:51:48

3

這是一個棘手的問題,確保勉強你會找到一個快速的解決方法,但這裏是一個無魔術解決方案:

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

歸結爲使用PlacementTargetTag(這裏的Button)。

+0

有趣。我將不得不嘗試在我們自己的東西上看看它是否可以取代BindingProxy方法。 – MetalMikester 2012-04-03 14:13:56

+0

其實我相信我最初從某個地方的MSDN例子中推斷出......不記得哪一個。 – 2012-04-03 14:15:30

+0

我在我們的XamDataTree ItemTemplate中給了這個快速嘗試,但沒有去 - 它找不到命令。也許它會在斯萊德的情況下工作 - 很難說沒有周圍的代碼。 – MetalMikester 2012-04-03 14:21:41

4

koshdim是現貨,它的作品就像一個魅力!由於Koshdim

我修改了他的代碼,以適應在我的上下文菜單

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