2011-09-27 31 views
3

我試圖執行位於我的ViewModel上的命令,使用帶有KeyBinding的TreeViewItem和MenuContext。在樹視圖項上鍵入綁定,使用CommandParameter

當前,使用上下文菜單,將在正確的ViewModel實例上調用該命令。 但是,當我選擇TreeViewItem並按下「C」鍵時,將在「root」ViewModel上調用該命令。

我試着擴展KeyBinding類(Keybinding a RelayCommand),但沒有運氣。

也許我要走錯了路:我只想顯示正確的MessageBox,如果我使用上下文菜單或鍵。

名爲WpfTest的WPF項目的代碼示例。

MainWindow.xaml

<Window x:Class="WpfTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:vm="clr-namespace:WpfTest" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <TreeView ItemsSource="{Binding}"> 
      <TreeView.ItemTemplate> 
       <HierarchicalDataTemplate ItemsSource="{Binding Child}" DataType="{x:Type vm:ViewModel}"> 
        <TextBlock Text="{Binding Name}" /> 
       </HierarchicalDataTemplate> 
      </TreeView.ItemTemplate> 
      <TreeView.ItemContainerStyle> 
       <Style TargetType="{x:Type TreeViewItem}"> 
        <Setter Property="ContextMenu"> 
         <Setter.Value> 
          <ContextMenu> 
           <MenuItem Header="{Binding Name}" Command="{Binding SomeCommand}" CommandParameter="{Binding}"/> 
          </ContextMenu> 
         </Setter.Value> 
        </Setter> 
        <Setter Property="vm:MyAttached.InputBindings"> 
         <Setter.Value> 
          <InputBindingCollection> 
           <KeyBinding Key="C" Command="{Binding SomeCommand}" CommandParameter="{Binding}"/> 
          </InputBindingCollection> 
         </Setter.Value> 
        </Setter> 
       </Style> 
      </TreeView.ItemContainerStyle> 
     </TreeView> 
    </Grid> 
</Window> 

MainWindow.xaml.cs:

namespace WpfTest 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.Collections.ObjectModel; 
    using System.Windows; 
    using System.Windows.Input; 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      this.DataContext = new List<ViewModel> 
      { 
       new ViewModel 
       { 
        Name = "Parent", 
        Child = new ObservableCollection<ViewModel> 
        { 
         new ViewModel { Name = "Child 1" }, 
         new ViewModel { Name = "Child 2" }, 
         new ViewModel { Name = "Child 3" } 
        } 
       } 
      }; 
     } 
    } 

    public class ViewModel 
    { 
     public string Name { get; set; } 
     public ObservableCollection<ViewModel> Child { get; set; } 
     public ICommand SomeCommand { get; set; } 

     public ViewModel() 
     { 
      this.SomeCommand = new RelayCommand<ViewModel>(OnCommandExecuted); 
     } 

     private void OnCommandExecuted(ViewModel parameter) 
     { 
      MessageBox.Show("CommandExecuted on " + Name + " with parameter " + parameter.Name); 
     } 
    } 

    public class MyAttached 
    { 
     public static readonly DependencyProperty InputBindingsProperty = 
      DependencyProperty.RegisterAttached("InputBindings", typeof(InputBindingCollection), typeof(MyAttached), 
      new FrameworkPropertyMetadata(new InputBindingCollection(), 
      (sender, e) => 
      { 
       var element = sender as UIElement; 
       if (element == null) return; 
       element.InputBindings.Clear(); 
       element.InputBindings.AddRange((InputBindingCollection)e.NewValue); 
      })); 

     public static InputBindingCollection GetInputBindings(UIElement element) 
     { 
      return (InputBindingCollection)element.GetValue(InputBindingsProperty); 
     } 

     public static void SetInputBindings(UIElement element, InputBindingCollection inputBindings) 
     { 
      element.SetValue(InputBindingsProperty, inputBindings); 
     } 
    } 

    public class RelayCommand<T> : ICommand 
    { 
     readonly Action<T> _execute = null; 
     public RelayCommand(Action<T> execute) { _execute = execute; } 
     public bool CanExecute(object parameter) { return true; } 
     public void Execute(object parameter) { _execute((T)parameter); } 
     public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } 
    } 
} 

回答

7

這裏的問題:Style只爲所有ListViewItems創建一個InputBindingCollection,你必須要出於這個原因,非常小心Setter.Values

這裏是修復:

<TreeView ItemsSource="{Binding}"> 
     <TreeView.Resources> 
      <!-- x:Shared="False" forces the new creation of that object whenever referenced --> 
      <InputBindingCollection x:Shared="False" x:Key="InputBindings"> 
       <KeyBinding Key="C" Command="{Binding SomeCommand}" CommandParameter="{Binding}" /> 
      </InputBindingCollection> 
     </TreeView.Resources> 
     <!-- ... --> 
     <TreeView.ItemContainerStyle> 
      <Style TargetType="{x:Type TreeViewItem}"> 
       <!-- ... --> 
       <Setter Property="vm:MyAttached.InputBindings" Value="{StaticResource InputBindings}"/> 
      </Style> 
     </TreeView.ItemContainerStyle> 
    </TreeView> 
+0

我也發現了大約InputBindingCollection的單個實例,但是沒有找到如何解決它。 3個字:你搖滾的人! – mathieu

+0

爲什麼,謝謝;高興地幫助:) –

+0

嘿mathieu,將它標記爲接受的答案! – Natxo