1

当焦点位于 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')。

然而

  1. 将光标放在 WinForms 文本框中
  2. 按 Ctrl-T:出现上下文菜单
  3. 按向下箭头:没有任何反应

在我们的应用程序中,键盘导航是必不可少的,所以这很烦人。

谁能帮我解释一下?最好想出一个解决方案,让第二种情况像第一种一样工作。

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

更新

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

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

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

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

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

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

4

1 回答 1

0

我已经尝试了您的示例应用程序,并注意到当在 WPF 文本框中使用光标打开 contextMenu 时,将logical focus移动到 contextMenu(光标在文本框中不再可见,因此键在上下文菜单中工作正常)。

但是,当我尝试使用 maskedTextBox 时,逻辑焦点停留在 textBox 上,因此没有适用于 ContextMenu 的键。

因此,作为一种解决方法,我调整了您的代码,看看这是否有帮助-

<DockPanel Name="DockPanel" Focusable="True" FocusVisualStyle="{x:Null}">
   .....     
</DockPanel>

FocusableDockPanel 设置为 true 并设置FocusVisualStyle为 null,以便在获得焦点时移除 DockPanel 周围的虚线。

现在在打开 contextMenu 之前,将逻辑焦点手动放在它的位置目标上,即 DockPanel,如下所示 -

void OpenContextMenu(object param)
{
   Keyboard.Focus(DockPanel);
   DockPanel.ContextMenu.PlacementTarget = DockPanel;
   DockPanel.ContextMenu.Placement = PlacementMode.Center;
   DockPanel.ContextMenu.IsOpen = true;
}

更新

由于它的窗口形式文本框的限制,它不会失去对上下文菜单打开的键盘焦点。

所以我们必须在打开上下文菜单之前跟踪被屏蔽的文本框是否有焦点,然后在关闭上下文菜单后手动设置焦点。

代码示例 -

<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;
}
于 2012-08-15T09:08:19.637 回答