控件中的聚焦样式和 FocusVisualStyle

Windows Presentation Foundation (WPF)提供了两种并行机制,用于在控件接收键盘焦点时更改控件的视觉外观。 第一种机制是在应用于控件的样式或模板中使用属性设定器来设置属性,例如 IsKeyboardFocused。 第二种机制是将单独的样式作为 FocusVisualStyle 属性的值提供;“焦点视觉样式”会为叠加在控件之上的装饰元素创建一个独立的视觉树,而不是通过替换控件或其他 UI 元素来改变其视觉树。 本主题讨论在何种场景下每种机制是适用的。

焦点视觉样式的用途

焦点视觉样式功能提供了一个通用的“对象模型”,可以在任何UI元素上,通过键盘导航引入视觉反馈给用户。 这可以在不向控件应用新模板或知道特定模板组合的情况下实现。

但是,正是因为焦点视觉样式功能在不知道控件模板的情况下有效,因此使用焦点视觉样式的控件可以显示的视觉反馈必然受到限制。 该功能的实际作用是将不同的可视化树(装饰器)覆盖在控件通过模板呈现所创建的可视化树的顶部。 使用填充 FocusVisualStyle 属性的样式定义此单独的可视化树。

默认焦点视觉风格行为

仅当焦点操作由键盘启动时,焦点视觉样式才起作用。 任何鼠标操作或程序的焦点更改都会禁用焦点视觉样式模式。 有关焦点模式区别的详细信息,请参阅 焦点概述

控件的主题包括默认焦点视觉样式行为,该行为成为主题中所有控件的焦点视觉样式。 此主题样式由静态键的值 FocusVisualStyleKey标识。 当您在应用程序级别声明自己的焦点视觉样式时,您将替换主题提供的默认样式行为。 或者,如果定义整个主题,则应使用相同的键来定义整个主题的默认行为的样式。

在主题中,默认焦点视觉样式通常非常简单。 下面是粗略近似值:

<Style x:Key="{x:Static SystemParameters.FocusVisualStyleKey}">
  <Setter Property="Control.Template">
    <Setter.Value>
      <ControlTemplate>
        <Rectangle StrokeThickness="1"
          Stroke="Black"
          StrokeDashArray="1 2"
          SnapsToDevicePixels="true"/>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

何时使用焦点视觉样式

从概念上讲,应用于控件的焦点视觉样式应该在各个控件之间保持一致。 确保一致性的一种方法是,仅在编写整个主题时才更改焦点视觉样式。在这种情况下,主题中定义的每个控件都会获得相同的焦点视觉样式,或者获取在视觉上与其他控件相关的样式的一些变体。 或者,你可以使用同样的样式(或类似的样式)来为页面或 UI 中所有可通过键盘聚焦的元素进行样式设置。

FocusVisualStyle 应用于不属于主题的单个控件样式上,这不是焦点视觉样式的正确用途。 这是因为控件之间的视觉表现不一致可能会导致用户在键盘焦点方面感到困惑。 如果您打算在一个主题中故意使键盘焦点不一致,并希望使用控件特定的行为,那么更好的方法是在样式中为单个输入状态属性(如 IsFocusedIsKeyboardFocused)使用触发器。

焦点视觉样式专门用于键盘焦点。 因此,焦点视觉样式是一种无障碍功能。 如果希望对任何类型的焦点(无论是通过鼠标、键盘还是以编程方式)进行 UI 更改,则不应使用焦点视觉样式。相反,应该在样式或模板中使用取值器和触发器,这些是根据常规焦点属性的值(例如 IsFocusedIsKeyboardFocusWithin)来操作的。

如何创建焦点视觉样式

为焦点视觉样式创建的样式应始终具有 TargetTypeControl。 风格应主要由ControlTemplate构成。 未将目标类型指定为焦点视觉样式分配给 FocusVisualStyle的类型。

由于目标类型始终 Control,因此必须使用所有控件通用的属性(使用 Control 类的属性及其基类)进行样式设置。 应创建一个模板,该模板将正确用作 UI 元素的覆盖层,并且不会遮盖控件的功能区域。 通常,这意味着视觉反馈应出现在控件边缘之外,或作为临时的或不显眼的效果显示,不会阻碍在应用焦点视觉样式的控件上的点击检测。 在模板绑定中,可用于确定覆盖模板大小和定位的属性包括 ActualHeightActualWidthMarginPadding

使用焦点视觉样式的替代方法

对于不适合使用焦点视觉样式的情况,例如您只需设置单个控件的样式,或您希望更精准地掌控控件模板,还有许多其他的属性和技术可以通过响应焦点变化来创建视觉效果。

样式和模板化中对触发器、设置器和事件设置器进行了详细讨论。 路由事件处理在 路由事件概述中讨论。

IsKeyboardFocused

如果对键盘焦点特别感兴趣,IsKeyboardFocused 依赖属性可用于属性 Trigger。 在样式或模板中使用属性触发器是一种更为适合的方法,用于定义一个专门针对单个控件的键盘焦点行为,这种行为可能在视觉上与其他控件的键盘焦点行为不一致。

另一个类似的依赖属性是 IsKeyboardFocusWithin,如果想要直观地标注键盘焦点位于组合或控件的功能区域内,则可能适合使用此属性。 例如,可以放置一个 IsKeyboardFocusWithin 触发器,以便对多个控件进行分组的面板以不同的方式显示,即使键盘焦点可能更精确地位于该面板内的单个元素上。

还可以使用事件 GotKeyboardFocusLostKeyboardFocus(及其预览版等效项)。 可以将这些事件用作 EventSetter的基础,也可以为后台代码中的事件编写处理程序。

其他焦点属性

如果希望更改焦点的所有可能原因引发视觉效果,则应将 setter 或触发器基于 IsFocused 依赖属性,或者基于用于 EventSetterGotFocusLostFocus 事件。

另请参阅