InputBindings只在聚焦时才起作用

Zoo*_*Way 11 wpf user-controls mvvm inputbinding .net-4.5

我设计了一个可重复使用的用户控件.它包含UserControl.InputBindings.这很简单,因为它只包含一个标签和一个按钮(和新的属性等)

当我在窗口中使用控件时效果很好.但是密钥绑定仅在集中时才起作用.当一个控件具有对alt + f8的绑定时,此快捷方式仅在聚焦时才有效.当具有自己的绑定的另一个被聚焦时,那个可以工作但不再是alt + f8.当没有控件具有焦点时,没有任何作用.

如何实现我的usercontrol定义窗口范围的键绑定?

特别是遵循MVVM设计模式(Caliburn.Micro使用),但任何帮助表示赞赏.


用户控件的XAML:

<UserControl x:Class="MyApp.UI.Controls.FunctionButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyApp.UI.Controls"
             xmlns:cm="http://www.caliburnproject.org"
             x:Name="Root"
             Focusable="True"
             mc:Ignorable="d" 
             d:DesignHeight="60" d:DesignWidth="120">
    <UserControl.Resources>
        ...
    </UserControl.Resources>
    <UserControl.InputBindings>
        <KeyBinding Key="{Binding ElementName=Root, Path=FunctionKey}" Modifiers="{Binding ElementName=Root, Path=KeyModifiers}" Command="{Binding ElementName=Root, Path=ExecuteCommand}" />
    </UserControl.InputBindings>
    <DockPanel LastChildFill="True">
        <TextBlock DockPanel.Dock="Top" Text="{Binding ElementName=Root, Path=HotkeyText}" />
        <Button DockPanel.Dock="Bottom" Content="{Binding ElementName=Root, Path=Caption}" cm:Message.Attach="[Event Click] = [Action ExecuteButtonCommand($executionContext)]" cm:Action.TargetWithoutContext="{Binding ElementName=Root}" />
    </DockPanel>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

用法示例:

    <Grid>
    <c:FunctionButton Width="75" Height="75" Margin="10,10,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" FunctionKey="F1" ShiftModifier="True" cm:Message.Attach="[Event Execute] = [Action Button1Execute]" />
    <c:FunctionButton Width="75" Height="75" Margin="10,90,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" FunctionKey="F2" ShiftModifier="True" cm:Message.Attach="[Event Execute] = [Action Button2Execute]" />
</Grid>
Run Code Online (Sandbox Code Playgroud)

正如所说的每个按钮在鼠标点击时工作(执行被触发),当聚焦时,我可以使用空间来激活按钮,聚焦按钮的输入绑定可以工作但从不聚焦.

Adi*_*ter 33

由于它们的工作方式,不会对未聚焦的控件执行InputBindings - 在可视树中从聚焦元素到可视树的根(窗口)搜索输入绑定的处理程序.当控件没有聚焦时,他将不会成为该搜索路径的一部分.

正如@Wayne所提到的,最好的方法是将输入绑定移动到父窗口.但有时这是不可能的(例如,当窗口的xaml文件中未定义UserControl时).

我的建议是使用附加行为将这些输入绑定从UserControl移动到窗口.使用附加行为这样做也可以使用任何FrameworkElement而不仅仅是UserControl.所以基本上你会有这样的事情:

public class InputBindingBehavior
{
    public static bool GetPropagateInputBindingsToWindow(FrameworkElement obj)
    {
        return (bool)obj.GetValue(PropagateInputBindingsToWindowProperty);
    }

    public static void SetPropagateInputBindingsToWindow(FrameworkElement obj, bool value)
    {
        obj.SetValue(PropagateInputBindingsToWindowProperty, value);
    }

    public static readonly DependencyProperty PropagateInputBindingsToWindowProperty =
        DependencyProperty.RegisterAttached("PropagateInputBindingsToWindow", typeof(bool), typeof(InputBindingBehavior),
        new PropertyMetadata(false, OnPropagateInputBindingsToWindowChanged));

    private static void OnPropagateInputBindingsToWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((FrameworkElement)d).Loaded += frameworkElement_Loaded;
    }

    private static void frameworkElement_Loaded(object sender, RoutedEventArgs e)
    {
        var frameworkElement = (FrameworkElement)sender;
        frameworkElement.Loaded -= frameworkElement_Loaded;

        var window = Window.GetWindow(frameworkElement);
        if (window == null)
        {
            return;
        }

        // Move input bindings from the FrameworkElement to the window.
        for (int i = frameworkElement.InputBindings.Count - 1; i >= 0; i--)
        {
            var inputBinding = (InputBinding)frameworkElement.InputBindings[i];
            window.InputBindings.Add(inputBinding);
            frameworkElement.InputBindings.Remove(inputBinding);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

<c:FunctionButton Content="Click Me" local:InputBindingBehavior.PropagateInputBindingsToWindow="True">
    <c:FunctionButton.InputBindings>
        <KeyBinding Key="F1" Modifiers="Shift" Command="{Binding FirstCommand}" />
        <KeyBinding Key="F2" Modifiers="Shift" Command="{Binding SecondCommand}" />
    </c:FunctionButton.InputBindings>
</c:FunctionButton>
Run Code Online (Sandbox Code Playgroud)

  • 完美的解决方案.谢谢@Adi (2认同)