8

如何在代码中定义容器或焦点子的逻辑焦点,而不给它键盘焦点?

我只想控制当控件将通过 Tab 键获得焦点或单击未击中子项的容器部分时,哪个子项将获得焦点,但如果还没有给它(或窃取)实际焦点,则不给它(或窃取)实际焦点有它。

而且我还希望通过键盘手势选择特定的孩子或用鼠标单击它仍然是可能的。

我理解键盘焦点和逻辑焦点在 WPF 中的区别,并且对于一个元素来说,拥有键盘焦点也意味着拥有逻辑焦点,但拥有逻辑焦点并不意味着元素拥有键盘焦点。

我也了解附加属性 FocusManager.FocusedElement 定义了哪个元素在视觉树中具有逻辑焦点,从定义此属性的元素开始。

我意识到这个属性不仅在 FocusManager.IsFocusScope 设置为 true 时使用,而且还用于诸如GroupBox之类的容器。

我已经做了很多尝试,大量搜索和阅读 WPF 焦点主题,但到目前为止没有成功,我不明白我错过了什么:

  • 调用FocusManager.SetFocusedElement也给键盘焦点,并且我之前暂时将我的子元素的属性 Focusable 更改为 false,它只在之前没有孩子有焦点的第一次工作,但在孩子获得焦点后不工作
  • 在元素或容器处处理事件GotKeyboardFocusPreviewGotKeyboardFocus以覆盖初始聚焦元素也不起作用,因为我无法判断焦点是通过鼠标还是键盘获得的,以及焦点是直接设置到子元素还是通过容器。
  • 一个例子来说明我想要实现的目标:我有一个简单的 RadioButtons 组,我想在代码中动态控制当用户“tab”将焦点移到这个 GroupBox 时哪个选项将获得焦点(通常是选项有isChecked=true)。

    <GroupBox Header="Options" Name="myGroupBox"
              KeyboardNavigation.TabNavigation="Once" 
              KeyboardNavigation. DirectionalNavigation="Cycle" >
        <StackPanel>
            <RadioButton GroupName="g1" Name="opt1" Content="Option _1"/>
            <RadioButton GroupName="g1" Name="opt2" Content="Option _2"/>
            <RadioButton GroupName="g1" Name="opt3" Content="Option _3"/>
        </StackPanel>
    </GroupBox>
    

    最后的评论,我知道如何使用ListBox实现选项的动态列表,将列表的selectedItem绑定到数据上下文的属性,然后通过ListBoxItem上的样式和模板上的样式和模板将项目模板中RadioButton的IsChecked属性绑定到IsSelected其父ListBoxItem的属性,它可以工作,但对于我的具体情况,我需要将我的RadioButtons直接绑定到我的数据上下文的属性,我不能同时将它们绑定到列表的 IsSelected 属性。

    4

    2 回答 2

    1

    我知道这是一个老问题,但希望这个答案能帮助其他有类似问题的人。

    如果我正确理解了问题,您可以通过在 GroupBox 上设置 FocusManager.IsFocusScope="True" 来实现所需的行为,并为 RadioButton.Checked 事件连接一个事件处理程序,该事件将逻辑焦点设置为事件的发送者:

    xml:

            <GroupBox Header="Options" Name="myGroupBox"
                  FocusManager.IsFocusScope="True"
                  RadioButton.Checked="MyGroupBox_OnChecked"
                  KeyboardNavigation.TabNavigation="Once" 
                  KeyboardNavigation.DirectionalNavigation="Cycle">
            <StackPanel>
                <RadioButton GroupName="g1" Name="opt1" Content="Option _1"/>
                <RadioButton GroupName="g1" Name="opt2" Content="Option _2"/>
                <RadioButton GroupName="g1" Name="opt3" Content="Option _3"/>
            </StackPanel>
        </GroupBox>
    

    后面的代码:

        private void MyGroupBox_OnChecked(object sender, RoutedEventArgs e)
        {
            var radioB = sender as RadioButton;
            if (radioB != null)
                FocusManager.SetFocusedElement(myGroupBox, radioB);
        }
    
    于 2013-06-07T21:14:02.477 回答
    0

    正如 Document 所说,FocusManager.SetFocusedElement 将在指定的焦点范围内为指定的元素提供逻辑焦点,并将尝试为元素提供键盘焦点。

    我遇到了同样的问题,我觉得这种赋予元素键盘焦点的尝试是不可预测的。在某些情况下,获得逻辑焦点的元素也会获得键盘焦点。但在其他一些情况下,它不会。我还没有弄清楚背后的确切机制。

    如果官方文件是这样说的,我认为可能没有“官方”的方式来实现你想要的。现在我只是通过调用 Keyboard.Focus 来完成此操作,您希望在设置逻辑焦点后保持键盘焦点。

    我正在解释的说明如下:

    private void SomeMember(){
      FocusManager.SetFocusedElement(focusScope, logicalFocus);
      Keyboard.Focus(controlMaintainingKeyboardFocus);
    }
    
    于 2021-01-31T13:55:19.180 回答