2012-06-09 96 views
2

我有一個用戶控件,如下圖所示,用於顯示主細節種類。帶有基本視圖模型的典型MVVM體系結構與CloseCommand一起完成。KeyBinding的困境

我想範圍一個KeyBinding,將在TabItem上執行關閉命令,只是不能讓它工作。

有趣的是,我可以得到它,如果我穿上PersonDetailView(的是,TabControl的可能顯示,如下圖所示兩種可能的用戶控件之一)的結合工作,但它應該在的TabControl或邊境地區的包含它。

有什麼建議嗎?

乾杯,
Berryl

用戶控件

<Grid> 

    <ListBox Style="{StaticResource ListBoxStyle}" /> 

    <GridSplitter 
     HorizontalAlignment="Right" VerticalAlignment="Stretch" Grid.Column="1" 
     ResizeBehavior="PreviousAndNext" Width="5" Background="#FFBCBCBC" KeyboardNavigation.IsTabStop="False" 
        /> 

    <Border Grid.Column="2" Background="{StaticResource headerBrush}"> 

     // ** THIS is the scope I want, but it doesn't work 
     <Border.InputBindings> 
      <KeyBinding Key="F4" Modifiers="Control" Command="{Binding CloseCommand}"/> 
     </Border.InputBindings> 

     <TabControl Style="{StaticResource TabControlStyle}" > 

      <TabControl.Resources>     
       <DataTemplate DataType="{x:Type personVm:PersonDetailVm}"> 
        <local:PersonDetailView /> 
       </DataTemplate> 
       <DataTemplate DataType="{x:Type orgVm:OrganizationDetailVm}"> 
        <local:OrganizationDetailView /> 
       </DataTemplate> 
      </TabControl.Resources> 

     </TabControl> 
    </Border> 

</Grid> 

TabItem的風格

<Style x:Key="OrangeTabItemStyle" TargetType="{x:Type TabItem}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TabItem}"> 
       <Border AllowDrop="true" ToolTip="{Binding DisplayName}"> 
        <Border Name="Border" Background="Transparent" BorderBrush="Transparent" BorderThickness="1,1,1,0" CornerRadius="2,2,0,0"> 
         <DockPanel x:Name="TitlePanel" TextElement.Foreground="{StaticResource FileTabTextBrush}"> 
          <ctrl:GlyphButton 

           // ** This works as expected 
           Command="{Binding CloseCommand}" CommandParameter="{Binding}" 
           > 
          </ctrl:GlyphButton> 

         </DockPanel> 
        </Border> 

        // ** Can't get it to work from here either ** 
        <Border.InputBindings> 
         <KeyBinding Command="{Binding CloseCommand}" Key="F4" Modifiers="Control" /> 
        </Border.InputBindings> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

UPDATE

我茫然地設置的RoutedCommand在我的風格

<Style x:Key="OrangeTabItemStyle" TargetType="{x:Type TabItem}"> 
    <Setter Property="beh:RoutedCommandWire.RoutedCommand" Value="F4"/> **** ?? **** 
    <Setter Property="beh:RoutedCommandWire.ICommand" Value="{Binding CloseCommand}"/> 
</Style> 

這是我認爲答案代碼看起來像在C#

public class RoutedCommandWire 
{ 

    public static readonly DependencyProperty RoutedCommandProperty = 
     DependencyProperty.RegisterAttached("RoutedCommand", typeof(RoutedCommand), typeof(RoutedCommandWire), new PropertyMetadata(OnCommandChanged)); 

    public static RoutedCommand GetRoutedCommand(DependencyObject d) { return (RoutedCommand) d.GetValue(RoutedCommandProperty); } 
    public static void SetRoutedCommand(DependencyObject d, RoutedCommand value) { d.SetValue(RoutedCommandProperty, value); } 

    public static readonly DependencyProperty ICommandProperty = 
     DependencyProperty.RegisterAttached("Iommand", typeof(ICommand), typeof(RoutedCommandWire)); 

    public static ICommand GetICommand(DependencyObject d) { return (ICommand) d.GetValue(ICommandProperty); } 
    public static void SetICommand(DependencyObject d, ICommand value) { d.SetValue(ICommandProperty, value); } 

    private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
     var fe = d as FrameworkElement; 
     if(fe==null) return; 

     if (e.OldValue != null) { 
      Detach(fe, (RoutedCommand) e.OldValue); 
     } 
     if (e.NewValue != null) { 
      Attach(fe, (RoutedCommand) e.NewValue, Execute, CanExecute); 
     } 
    } 

    private static void CanExecute(object sender, CanExecuteRoutedEventArgs e) { 
     var depObj = sender as DependencyObject; 
     if (depObj == null) return; 

     var command = GetICommand(depObj); 
     if (command == null) return; 

     e.CanExecute = command.CanExecute(e.Parameter); 
     e.Handled = true; 
    } 

    private static void Execute(object sender, ExecutedRoutedEventArgs e) 
    { 
     var depObj = sender as DependencyObject; 
     if (depObj == null) return; 

     var command = GetICommand(depObj); 
     if (command == null) return; 

     command.Execute(e.Parameter); 
     e.Handled = true; 
    } 

    public static void Detach(FrameworkElement fe, RoutedCommand command) { 
     var bindingCollection = fe.CommandBindings; 
     if (bindingCollection.Count == 0) return; 

     var matches = bindingCollection.Cast<CommandBinding>().Where(binding => binding.Equals(command)); 
     foreach (var binding in matches) { 
      bindingCollection.Remove(binding); 
     } 
    } 

    public static void Attach(FrameworkElement fe, RoutedCommand command, 
     ExecutedRoutedEventHandler executedHandler, CanExecuteRoutedEventHandler canExecuteHandler, bool preview = false) 
    { 
     if (command == null || executedHandler == null) return; 

     var binding = new CommandBinding(command); 
     if (preview) 
     { 
      binding.PreviewExecuted += executedHandler; 
      if (canExecuteHandler != null) 
      { 
       binding.PreviewCanExecute += canExecuteHandler; 
      } 
     } 
     else 
     { 
      binding.Executed += executedHandler; 
      if (canExecuteHandler != null) 
      { 
       binding.CanExecute += canExecuteHandler; 
      } 
     } 
     fe.CommandBindings.Add(binding); 
    } 
} 
+0

哪個類具有'CloseCommand'屬性? – 2012-06-09 07:11:48

+0

我想你會在這裏找到一個解決方案http://stackoverflow.com/questions/10365582/how-to-implement-commands-to-use-ancestor-methods-in-wpf/10390564#10390564 – Phil

+0

@funkett。我所有的視圖模型都是這樣做的,儘管這裏感興趣的是PersonDetailVm和OrganizationDetailVm。乾杯 – Berryl

回答

2

鍵綁定只在其接受鍵盤輸入的控制工作。邊界不是。通常,InputBindings與CommandBindings的不同之處在於,您可以在父元素上定義CommandBinding,以便在子元素具有焦點時處理命令,但無法在父元素上定義InputBindings以使它們對子元素有效。

您可以做的是將默認的InputGesture添加到您的命令的InputGestures集合中。這似乎使得該命令可以使用來自接受鍵盤輸入的每個控件的鍵盤快捷鍵(這比要在任何地方指定InputBindings好得多,不是嗎?)。爲了利用這一點,您將不得不使用RoutedCommand來調用您的MVVM-ICommand。您可以使用附加的屬性將這兩個屬性組合在一起,我稱之爲「粘性命令」,它與附加行爲非常相似。

此代碼定義附加屬性:

Public Class Close 

    Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("Command", GetType(RoutedCommand), GetType(Close), New PropertyMetadata(AddressOf OnCommandChanged)) 
    Public Shared Function GetCommand(ByVal d As DependencyObject) As RoutedCommand 
     Return d.GetValue(CommandProperty) 
    End Function 
    Public Shared Sub SetCommand(ByVal d As DependencyObject, ByVal value As RoutedCommand) 
     d.SetValue(CommandProperty, value) 
    End Sub 

    Public Shared ReadOnly MVVMCommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("MVVMCommand", GetType(ICommand), GetType(Close)) 
    Public Shared Function GetMVVMCommand(ByVal d As DependencyObject) As ICommand 
     Return d.GetValue(MVVMCommandProperty) 
    End Function 
    Public Shared Sub SetMVVMCommand(ByVal d As DependencyObject, ByVal value As ICommand) 
     d.SetValue(MVVMCommandProperty, value) 
    End Sub 


    Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs) 
     If e.OldValue IsNot Nothing Then 
      Detach(d, DirectCast(e.OldValue, RoutedCommand)) 
     End If 
     If e.NewValue IsNot Nothing Then 
      Attach(d, DirectCast(e.NewValue, RoutedCommand), AddressOf DoCloseCommand, AddressOf CanDoCloseCommand) 
     End If 
    End Sub 

    Private Shared Sub CanDoCloseCommand(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs) 
     If sender IsNot Nothing Then 
      Dim com As ICommand = GetMVVMCommand(sender) 
      If com IsNot Nothing Then 
       e.CanExecute = com.CanExecute(e.Parameter) 
       e.Handled = True 
      End If 
     End If 
    End Sub 

    Private Shared Sub DoCloseCommand(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs) 
     If sender IsNot Nothing Then 
      Dim com As ICommand = GetMVVMCommand(sender) 
      If com IsNot Nothing Then 
       com.Execute(e.Parameter) 
       e.Handled = True 
      End If 
     End If 
    End Sub 

    Public Shared Sub Detach(ByVal base As FrameworkElement, ByVal command As RoutedCommand) 
     Dim commandBindings As CommandBindingCollection = base.CommandBindings 
     If commandBindings IsNot Nothing Then 
      Dim bindings = From c As CommandBinding In commandBindings 
          Where c.Command Is command 
          Select c 
      Dim bindingList As New List(Of CommandBinding)(bindings) 
      For Each c As CommandBinding In bindingList 
       commandBindings.Remove(c) 
      Next 
     End If 
    End Sub 

    Public Shared Sub Attach(ByVal base As FrameworkElement, ByVal command As RoutedCommand, ByVal executedHandler As ExecutedRoutedEventHandler, ByVal canExecuteHandler As CanExecuteRoutedEventHandler, Optional ByVal preview As Boolean = False) 
     If command IsNot Nothing And executedHandler IsNot Nothing Then 
      Dim b As CommandBinding = New CommandBinding(command) 
      If preview Then 
       AddHandler b.PreviewExecuted, executedHandler 
       If canExecuteHandler IsNot Nothing Then 
        AddHandler b.PreviewCanExecute, canExecuteHandler 
       End If 
      Else 
       AddHandler b.Executed, executedHandler 
       If canExecuteHandler IsNot Nothing Then 
        AddHandler b.CanExecute, canExecuteHandler 
       End If 
      End If 
      base.CommandBindings.Add(b) 
      'For Each i As InputGesture In command.InputGestures 
      ' GetInputBindings(base).Add(New InputBinding(command, i)) 
      'Next 
     End If 
    End Sub 

你會使用這兩者對你的TabItems,我想,因爲這是你要處理的關閉命令,你會設置關閉。命令傳遞給在其InputGestures中具有鍵盤快捷方式的RoutedCommand,以及Close.MVVMCommand =「{Binding CloseCommand}」。

UPDATE

您可以在視圖模型定義的RoutedCommand是這樣的:

Public Shared ReadOnly TestCommand As New RoutedUICommand("Test", "TestCommand", GetType(ViewModel)) 
Shared Sub New() 
    TestCommand.InputGestures.Add(New KeyGesture(Key.T, ModifierKeys.Control)) 
End Sub 

靜態構造函數命令的默認keygesture。如果你想在XAML中做到這一點,你也可以使用自定義附加屬性來做到這一點。無論如何,你需要在XAML中引用這樣的RoutedCommand:

Close.Command="{x:Static my:ViewModel.TestCommand}" 
+0

關於接受輸入的第一部分非常有意義,但不太清楚菜單。 TabItem的上下文菜單? – Berryl

+0

要查找元素是否接受鍵盤輸入的屬性是什麼?歡呼聲 – Berryl

+0

對於第一名:剛試了一遍,發現菜單與它無關。在我的測試項目中似乎如此,但事實上,它更好......請看我更正的答案,並對不起...... – hbarck