为控件中的焦点设置样式以及 FocusVisualStyle

Windows Presentation Foundation (WPF) 提供两种用于在控件接收键盘焦点时更改其视觉外观的并行机制。 第一种机制是对应用于控件的样式或模板中的属性(如 IsKeyboardFocused)使用属性 setter。 第二种机制是将一个单独的样式作为 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(无论是通过鼠标、键盘还是编程方式),那么不应使用焦点视觉样式,而应在样式或模板中使用 setter 和触发器,它们根据常规焦点属性(如 IsFocusedIsKeyboardFocusWithin)的值发生作用。

如何创建焦点视觉样式

为焦点视觉样式创建的样式应始终具有 ControlTargetType。 样式应主要包含 ControlTemplate。 不必将目标类型指定为将焦点视觉样式分配给 FocusVisualStyle 的类型。

因为目标类型始终是 Control,所以必须通过使用所有控件共有的属性(使用 Control 类及其基类的属性)来设置样式。 应创建将正确地用作 UI 元素的覆盖层、且不会隐藏控件的功能区域的模板。 通常,这意味着视觉反馈应显示在控件边距之外,或者显示为临时的或不显眼的效果(这样就不会阻止应用焦点视觉样式的控件上的命中测试)。 可以在可用于确定覆盖模板的大小和位置的模板绑定中使用的属性包括 ActualHeightActualWidthMarginPadding

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

对于不适合使用焦点视觉样式的情况(因为仅设置单个控件的样式或希望对控件模板有更多的控制),存在许多其他可用的属性和技术,可用来创建响应焦点更改的视觉行为。

样式设置和模板化中对触发器、setter 以及事件 setter 进行了详细介绍。 路由事件概述中对路由事件处理进行了介绍。

IsKeyboardFocused

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

另一个类似的依赖属性是 IsKeyboardFocusWithin,如果希望从视觉上显示出键盘焦点在控件组成中的位置或控件功能区域中的位置,那么可能适合使用此属性。 例如,可以放置一个 IsKeyboardFocusWithin 触发器,以便某个包含若干控件的面板以不同的外观显示,即使将键盘焦点的位置精确到面板中的某个元素上,也可呈现出不同的外观。

还可使用事件 GotKeyboardFocusLostKeyboardFocus(以及其 Preview 等效项)。 可以将这些事件用作 EventSetter 的基础,也可以通过代码隐藏的方式为这些事件编写处理程序。

其他焦点属性

如果希望导致更改焦点的所有可能原因都产生一个视觉行为,那么应让 setter 或触发器基于 IsFocused 依赖属性,或者基于用于 EventSetterGotFocusLostFocus 事件。

另请参阅