从WindowsFormsHost打开时,上下文菜单不响应按键

时间:2012-08-15 07:29:30

标签: wpf keyboard contextmenu windowsformshost

当焦点位于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,将出现一个简单的上下文菜单。

现在运行程序

  1. 将光标放在WPF文本框中
  2. 按Ctrl-T:出现上下文菜单
  3. 按向下箭头:选择上下文菜单中的第一项('aaa')。
  4. ,而

    1. 将光标放在WinForms文本框中
    2. 按Ctrl-T:出现上下文菜单
    3. 按向下箭头:没有任何反复发生
    4. 在我们的应用程序中,键盘导航至关重要,因此非常烦人。

      有人可以帮我解释一下吗?最好提出一个解决方案,使第二个案例成为第一个案例。

      (我注意到,普通菜单 - 至少使用助记符打开 - 工作正常)。

      更新

      我想,我应该详细说明一下真实案例,我正在研究:我有一个TabControl,其中每个TabItem的内容都在其他地方开发(但通常很复杂)。我的Ctrl-T上下文菜单将显示选项卡项标题列表,以便在选项卡之间快速导航。 (它将组合框模仿到Visual Studio中选项卡的右侧。)

      现在,当当前标签的内容是WPF控件并且焦点位于控件内时,一切正常。如果打开上下文菜单并选择相同的选项卡,焦点将返回到打开上下文菜单之前的位置。这是有效的,因为上下文菜单是一个焦点范围,因此逻辑焦点保持在控件内。

      但是,如果内容是WindowsFormHost,并且焦点位于其中,则上下文菜单无法正常工作,如上所述。下面的第一个答案通过在打开上下文菜单之前将焦点移动到任何WPF控件来解决此问题。不幸的是,这也改变了逻辑焦点,使得在上下文菜单关闭后无法重置它。

      我尝试在更改焦点和打开上下文菜单之前捕获并保存焦点,但没有运气。

      我已经解决的解决方案是仅在必要时改变焦点。 (签入Keyboard.FocusedElement == null似乎有效)。这意味着在您关注WinForms组件,打开上下文菜单并选择不更改选项卡的情况下,我的解决方案仅次优。这是罕见的,所以它比我的第一个问题有了很大的改进。

      但我仍然很想知道是否存在更好的解决方案。

1 个答案:

答案 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;
}