【WPF.NET开发】WPF中的焦点

发布时间:2024年01月04日

本文内容

  1. 键盘焦点
  2. 逻辑焦点
  3. 键盘导航
  4. 以编程方式导航焦点
  5. 焦点事件

在 WPF 中,有两个与焦点有关的主要概念:键盘焦点和逻辑焦点。 键盘焦点指接收键盘输入的元素,而逻辑焦点指焦点范围中具有焦点的元素。 本概述详细介绍了这些概念。 对于创建具有多个可获取焦点的区域的复杂应用程序来说,理解这些概念之间的区别非常重要。

参与焦点管理的主要类是?Keyboard?类、FocusManager?类和基本元素类,例如?UIElement?和?ContentElement

Keyboard?类主要与键盘焦点相关,而?FocusManager?主要与逻辑焦点相关,但这种区别不是绝对的。 具有键盘焦点的元素也具有逻辑焦点,但具有逻辑焦点的元素不一定具有键盘焦点。 使用?Keyboard?类来设置具有键盘焦点的元素时,这一点就很明显,因为它还在元素上设置逻辑焦点。

1、键盘焦点

键盘焦点指当前正在接收键盘输入的元素。 在整个桌面上,只能有一个具有键盘焦点的元素。 在 WPF 中,具有键盘焦点的元素会将?IsKeyboardFocused?设置为?true。?Keyboard?类的静态属性?FocusedElement?获取当前具有键盘焦点的元素。

为使元素获得键盘焦点,必须将基元素的?Focusable?和?IsVisible?属性设置为?true。 某些类(例如?Panel?基类)默认将?Focusable?设置为?false;因此,如果要此类元素能够获得键盘焦点,必须将?Focusable?设置为?true

可通过用户与 UI 交互(例如,按 Tab 键导航到某个元素或者在某些元素上单击鼠标)来获取键盘焦点。 也可以使用?Keyboard?类的?Focus?方法以编程方式获取键盘焦点。?Focus?方法尝试为指定元素提供键盘焦点。 返回的元素是具有键盘焦点的元素,如果旧的或新的焦点对象阻止请求,则具有键盘焦点的元素可能不是请求的元素。

以下示例使用?Focus?方法将键盘焦点设置在?Button?上。

C#复制

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

基元素类的?IsKeyboardFocused?属性获取一个值,该值指示元素是否具有键盘焦点。 基元素类的?IsKeyboardFocusWithin?属性获取一个值,该值指示元素或其任何一个视觉子元素是否具有键盘焦点。

如果在应用程序启动时设置初始焦点,接收焦点的元素必须位于应用程序加载的初始窗口的可视化树中,并且该元素必须将?Focusable?和?IsVisible?设置为?true。?Loaded?事件处理程序是设置初始焦点的推荐位置。 还可以通过调用?Invoke?或?BeginInvoke?来使用?Dispatcher?回叫。

2、逻辑焦点

逻辑焦点是指焦点范围内的FocusManager.FocusedElement。 焦点范围是一个元素,用于跟踪其范围内的?FocusedElement。 键盘焦点离开焦点范围时,焦点元素会失去键盘焦点,但保留逻辑焦点。 键盘焦点返回到焦点范围时,焦点元素会再次获得键盘焦点。 这使得键盘焦点可在多个焦点范围之间切换,但确保了焦点返回到焦点范围时,焦点范围中的焦点元素重新获得键盘焦点。

一个应用程序中可以有多个具有逻辑焦点的元素,但在一个特定的焦点范围中只能有一个具有逻辑焦点的元素。

具有键盘焦点的元素还具有其所属焦点范围的逻辑焦点。

通过将?FocusManager?附加属性?IsFocusScope?设置为?true,可将元素转换为 Extensible Application Markup Language (XAML) 中的焦点范围。 在代码中,可通过调用?SetIsFocusScope?将元素转换为焦点范围。

以下示例通过设置?IsFocusScope?附加属性将?StackPanel?转换为焦点范围。

XAML复制

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>

C#复制

StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

GetFocusScope?返回指定元素的焦点范围。

WPF 中默认为焦点范围的类是?WindowMenuItemToolBar?和?ContextMenu

GetFocusedElement?获取指定焦点范围的焦点元素。?SetFocusedElement?设置指定焦点范围中的焦点元素。?SetFocusedElement?通常用于设置初始焦点元素。

以下示例在焦点范围上设置焦点元素并获取焦点范围的焦点元素。

C#复制

3、键盘导航

按下导航键之一时,KeyboardNavigation?类负责实现默认键盘焦点导航。 导航键包括:Tab、Shift+Tab、Ctrl+Tab、Ctrl+Shift+Tab、向上键、向下键、向左键和向右键。

可以通过设置附加的?KeyboardNavigation?属性?TabNavigationControlTabNavigation?和?DirectionalNavigation?来更改导航容器的导航行为。 这些属性的类型为?KeyboardNavigationMode,可能的值为?ContinueLocalContainedCycleOnce?和?None。 默认值为?Continue,表示该元素不是导航容器。

以下示例创建具有多个?MenuItem?对象的?Menu。?TabNavigation?附加属性在?Menu?上设置为?Cycle。 使用 Tab 键在?Menu?中改变焦点时,焦点会从每个元素上移过,到达最后一个元素后会返回第一个元素。

XAML复制

<Menu KeyboardNavigation.TabNavigation="Cycle">
  <MenuItem Header="Menu Item 1" />
  <MenuItem Header="Menu Item 2" />
  <MenuItem Header="Menu Item 3" />
  <MenuItem Header="Menu Item 4" />
</Menu>

C#复制

Menu navigationMenu = new Menu();
MenuItem item1 = new MenuItem();
MenuItem item2 = new MenuItem();
MenuItem item3 = new MenuItem();
MenuItem item4 = new MenuItem();

navigationMenu.Items.Add(item1);
navigationMenu.Items.Add(item2);
navigationMenu.Items.Add(item3);
navigationMenu.Items.Add(item4);

KeyboardNavigation.SetTabNavigation(navigationMenu,
    KeyboardNavigationMode.Cycle);

4以编程方式导航焦点

与焦点结合使用的其他 API 是?MoveFocus?和?PredictFocus

MoveFocus?将焦点转移到应用程序中的下一个元素。?TraversalRequest?用于指定方向。 传递给?MoveFocus?的?FocusNavigationDirection?指定焦点可移动的各个方向,例如?FirstLastUp?和?Down

以下示例使用?MoveFocus?更改焦点元素。

C#复制

// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = _focusMoveValue;

// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);

// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;

// Change keyboard focus.
if (elementWithFocus != null)
{
    elementWithFocus.MoveFocus(request);
}

PredictFocus?返回在焦点更改时接收焦点的对象。 目前,PredictFocus?仅支持?UpDownLeft?和?Right

焦点事件

与键盘焦点相关的事件是?PreviewGotKeyboardFocusGotKeyboardFocus?和?PreviewLostKeyboardFocusLostKeyboardFocus。 这些事件定义为?Keyboard?类上的附加事件,但更多地作为基元素类上的等效路由事件来访问。?

元素获得键盘焦点时会引发?GotKeyboardFocus。 元素丢失键盘焦点时会引发?LostKeyboardFocus。 如果已处理?PreviewGotKeyboardFocus?事件或?PreviewLostKeyboardFocusEvent?事件并将?Handled?设置为?true,则焦点不会改变。

以下示例将?GotKeyboardFocus?和?LostKeyboardFocus?事件处理程序附加到?TextBox

XAML复制

<Border BorderBrush="Black" BorderThickness="1"
        Width="200" Height="100" Margin="5">
  <StackPanel>
    <Label HorizontalAlignment="Center" Content="Type Text In This TextBox" />
    <TextBox Width="175"
             Height="50" 
             Margin="5"
             TextWrapping="Wrap"
             HorizontalAlignment="Center"
             VerticalScrollBarVisibility="Auto"
             GotKeyboardFocus="TextBoxGotKeyboardFocus"
             LostKeyboardFocus="TextBoxLostKeyboardFocus"
             KeyDown="SourceTextKeyDown"/>
  </StackPanel>
</Border>

TextBox?获得键盘焦点时,TextBox?的?Background?属性更改为?LightBlue

C#复制

private void TextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it obtains focus.
        source.Background = Brushes.LightBlue;

        // Clear the TextBox.
        source.Clear();
    }
}

TextBox?丢失键盘焦点时,TextBox?的?Background?属性变回白色。

C#复制

private void TextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it loses focus.
        source.Background = Brushes.White;

        // Set the  hit counter back to zero and updates the display.
        this.ResetCounter();
    }
}

与逻辑焦点相关的事件是?GotFocus?和?LostFocus。 这些事件在?FocusManager?上定义为附加事件,但?FocusManager?不公开 CLR 事件包装器。?UIElement?和?ContentElement?可以更方便地公开这些事件。

文章来源:https://blog.csdn.net/m0_51887793/article/details/135351426
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。