当焦点位于WindowsFormsHost并打开上下文菜单时,上下文菜单不会响应向上/向下箭头键按下。
以下(几乎自包含)代码说明了这个问题:
<Window x:Class="FocusAndWinformsSimple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="MainWindow" Height="350" Width="525">
<DockPanel Name="DockPanel">
<DockPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="aaa"/>
<MenuItem Header="bbb"/>
</ContextMenu>
</DockPanel.ContextMenu>
<StackPanel>
<TextBox/>
<WindowsFormsHost>
<wf:MaskedTextBox/>
</WindowsFormsHost>
</StackPanel>
</DockPanel>
</Window>
和代码背后:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InputBindings.Add(
new KeyBinding(
new RelayCommand(OpenContextMenu),
new KeyGesture(Key.T, ModifierKeys.Control)));
}
void OpenContextMenu()
{
DockPanel.ContextMenu.PlacementTarget = DockPanel;
DockPanel.ContextMenu.Placement = PlacementMode.Center;
DockPanel.ContextMenu.IsOpen = true;
}
}
这将显示两个文本框,WPF和WinForms。如果按Ctrl-T,将出现一个简单的上下文菜单。
现在运行程序
,而
在我们的应用程序中,键盘导航至关重要,因此非常烦人。
有人可以帮我解释一下吗?最好提出一个解决方案,使第二个案例成为第一个案例。
(我注意到,普通菜单 - 至少使用助记符打开 - 工作正常)。
更新
我想,我应该详细说明一下真实案例,我正在研究:我有一个TabControl,其中每个TabItem的内容都在其他地方开发(但通常很复杂)。我的Ctrl-T上下文菜单将显示选项卡项标题列表,以便在选项卡之间快速导航。 (它将组合框模仿到Visual Studio中选项卡的右侧。)
现在,当当前标签的内容是WPF控件并且焦点位于控件内时,一切正常。如果打开上下文菜单并选择相同的选项卡,焦点将返回到打开上下文菜单之前的位置。这是有效的,因为上下文菜单是一个焦点范围,因此逻辑焦点保持在控件内。
但是,如果内容是WindowsFormHost,并且焦点位于其中,则上下文菜单无法正常工作,如上所述。下面的第一个答案通过在打开上下文菜单之前将焦点移动到任何WPF控件来解决此问题。不幸的是,这也改变了逻辑焦点,使得在上下文菜单关闭后无法重置它。
我尝试在更改焦点和打开上下文菜单之前捕获并保存焦点,但没有运气。
我已经解决的解决方案是仅在必要时改变焦点。 (签入Keyboard.FocusedElement == null
似乎有效)。这意味着在您关注WinForms组件,打开上下文菜单并选择不更改选项卡的情况下,我的解决方案仅次优。这是罕见的,所以它比我的第一个问题有了很大的改进。
但我仍然很想知道是否存在更好的解决方案。
答案 0 :(得分:0)
我已经尝试过您的示例应用程序,并注意到当使用光标在WPF textBox中打开contextMenu时,logical focus
移动到contextMenu(光标在文本框中不再可见,因此键可用于上下文菜单)。
但是,当我尝试使用maskedTextBox时,逻辑焦点停留在textBox上,因此没有为ContextMenu工作的键。
因此,作为一种解决方法,我调整了你的代码,看看这是否有帮助 -
<DockPanel Name="DockPanel" Focusable="True" FocusVisualStyle="{x:Null}">
.....
</DockPanel>
将DockPanel的Focusable
设置为true,将FocusVisualStyle
设置为null,以便在获得焦点时移除DockPanel周围的虚线。
现在在打开contextMenu之前,将逻辑焦点手动放在其placementTarget上,即像这样的DockPanel -
void OpenContextMenu(object param)
{
Keyboard.Focus(DockPanel);
DockPanel.ContextMenu.PlacementTarget = DockPanel;
DockPanel.ContextMenu.Placement = PlacementMode.Center;
DockPanel.ContextMenu.IsOpen = true;
}
<强>更新强>
由于它的窗口限制形成了textBox,它不会松开键盘焦点上下文菜单打开。
因此,在打开之前,我们必须跟踪被遮罩的textBox是否有焦点 上下文菜单然后在上下文菜单后手动设置焦点 闭合。
代码示例 -
<DockPanel Name="DockPanel" Focusable="True" FocusVisualStyle="{x:Null}">
<DockPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="aaa"/>
<MenuItem Header="bbb"/>
</ContextMenu>
</DockPanel.ContextMenu>
<StackPanel>
<TextBox x:Name="txt"/>
<WindowsFormsHost>
<wf:MaskedTextBox x:Name="msk"/>
</WindowsFormsHost>
</StackPanel>
</DockPanel>
public MainWindow()
{
InitializeComponent();
InputBindings.Add(
new KeyBinding(
new RelayCommand<object>(OpenContextMenu),
new KeyGesture(Key.T, ModifierKeys.Control)));
msk.GotFocus += new EventHandler(msk_GotFocus);
msk.LostFocus += new EventHandler(msk_LostFocus);
}
bool isFocusOnMaskedTextBox;
void msk_LostFocus(object sender, EventArgs e)
{
isFocusOnMaskedTextBox = false;
}
void msk_GotFocus(object sender, EventArgs e)
{
isFocusOnMaskedTextBox = true;
}
void OpenContextMenu(object param)
{
bool moveFocusOnClose = isFocusOnMaskedTextBox;
RoutedEventHandler eventHandler = null;
eventHandler = (s, e) =>
{
if (moveFocusOnClose)
msk.Focus();
DockPanel.ContextMenu.Closed -= eventHandler;
};
if (moveFocusOnClose)
{
Keyboard.Focus(DockPanel);
}
DockPanel.ContextMenu.IsOpen = true;
DockPanel.ContextMenu.Closed += eventHandler;
}