我设计了一个可重用的用户控件。它包含UserControl.InputBindings。它非常简单,因为它只包含一个标签和一个按钮(以及新的属性等)。
当我在我的窗口中使用控件时,它工作得很好。但是键绑定只有在集中注意力的时候才起作用。当一个控件绑定到alt+f8时,此快捷方式仅在焦点时有效。当另一个有自己的绑定的对象被聚焦时,那个对象可以工作,但alt+f8不再起作用。当所有控件都没有焦点时,将无法工作。
我怎样才能实现我的用户控件定义窗口范围的键绑定?
特别是以下MVVM设计模式(Caliburn。微型使用),但任何帮助是赞赏。
用户控件的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>
示例用法:
<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>
如前所述,每个按钮都可以在鼠标单击时工作(执行被触发),当聚焦时,我可以使用空间来激活按钮,聚焦按钮的输入绑定可以工作,但不聚焦按钮的输入绑定永远不会工作。
5条答案
按热度按时间unhi4e5o1#
由于InputBindings的工作方式,它不会对没有焦点的控件执行--在视觉树中从焦点元素到视觉树的根(窗口)搜索输入绑定的处理程序。当控件未聚焦时,它将不会成为搜索路径的一部分。
正如@韦恩提到的,最好的方法是简单地将输入绑定移动到父窗口。但是有时这是不可能的(例如,当窗口的xaml文件中没有定义UserControl时)。
我的建议是使用一个附加的行为将这些输入绑定从UserControl移动到窗口。使用附加的行为这样做还有一个好处,即可以在任何
FrameworkElement
上工作,而不仅仅是您的UserControl。所以基本上你会有这样的东西:使用方法:
ep6jt1vc2#
是的,UserControl KeyBindings仅在控件具有焦点时才起作用。
如果你想让KeyBinding在窗口上工作,那么你必须在窗口本身上定义它。在Windows XAML上,您可以使用以下命令执行此操作:
然而,你说你想让UserControl定义KeyBinding。我不知道在XAML中有什么方法可以做到这一点,所以你必须在UserControl的代码隐藏中设置它。这意味着找到UserControl的父Window并创建KeyBinding
在这种情况下,ViewModel.FunctionKey需要是Key类型,否则您将需要从字符串转换为Key类型。
必须在代码隐藏而不是XAML中执行此操作并不会破坏MVVM模式。所做的一切就是将绑定逻辑从XAML转移到C#。ViewModel仍然独立于View,因此可以在不示例化View的情况下进行单元测试。将这样的 *UI特定 * 逻辑放在视图的代码隐藏中是绝对好的。
pftdvrlh3#
我们扩展了Adi Lesters附加的行为代码,在UnLoaded上使用取消订阅机制来清理传输的绑定。如果控件退出可视树,InputBindings将从Window中移除,以避免它们处于活动状态。(我们没有探索在附加属性上使用WPF触发器。
由于WPF在我们的解决方案中重用控件,因此行为不会分离:已加载/未加载被调用多次。这不会导致泄漏,因为该行为不包含对FrameWorkElement的引用。
不知何故,在我们的解决方案中,有些控件没有抛出UnLoaded事件,尽管它们再也不会被使用,甚至在一段时间后被垃圾收集。我们通过使用HashCode/WeakReferences跟踪并获取InputBindings的副本来解决这个问题。
完整的类是:
ldxq2e6h4#
虽然有点晚,可能不是100% MVVM符合,但可以使用以下onloaded-event将所有Inputbindings传播到窗口。
由于这只影响视图层,我会很好地与这个解决方案在MVVM方面。找到此位here
inn6fuwd5#
keyPressPlaceHoler是目标uielement的容器名称
记住在usercontrol中设置Focusable=“True”