2012-06-09 94 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