WPF性能优化の树、逻辑树、视觉树以及帮助类LogicalTreeHelper 和 VisualTreeHelper
一、WPF中的树
WPF 中,最完整的树结构是对象树。
如果在 XAML 中定义一个应用程序页,然后加载 XAML,将根据标记中元素之间的嵌套关系来创建树结构。
如果使用代码定义应用程序或应用程序的一部分,则将根据为属性(属性实现给定对象的内容模型)分配属性值的方式来创建树结构。
因为对象树更像是概念,而不像是实际 API,所以还可以将此概念视为对象图。 实际上,在运行时,对象之间的某些关系不能由树形式表示。 尽管如此,树形式的相关性还是很强,尤其是对于 XAML 定义的 UI。
二、逻辑树的用途以及构成
基本上,逻辑树是框架级别的近似运行时对象图(排除了视觉对象),但其足以用于对你自己的运行时应用程序组合执行多种查询操作。
逻辑树用途
借助逻辑树,内容模型可以方便地循环访问其可能的子对象,从而实现扩展。 此外,逻辑树还为某些通知提供框架,例如在加载逻辑树中的所有对象时。 基本上,逻辑树是框架级别的近似运行时对象图(排除了视觉对象),但其足以用于对你自己的运行时应用程序组合执行多种查询操作。
此外,通过在初始请求对象上的集合的逻辑树中向上查找 Resources ,然后继续运行逻辑树并检查每个 FrameworkElement (或 FrameworkContentElement) 对于 Resources
包含 ResourceDictionary (可能包含该密钥)的另一个值,可以解析静态和动态资源引用。 当同时存在逻辑树和可视化树时,将使用逻辑树进行资源查找。 有关资源字典和查找的详细信息,请参见 XAML 资源。
逻辑树的构成
逻辑树在 WPF 框架级别定义,这意味着,与逻辑树操作最相关的 WPF 基元素是 FrameworkElement 或 FrameworkContentElement 。 然而,如您所看到的实际使用的 LogicalTreeHelper 是 API,逻辑树有时会包含不是或的节点 FrameworkElement FrameworkContentElement 。 例如,逻辑树报告的 Text 值 TextBlock ,它是一个字符串
逻辑树的方法
BringIntoView(DependencyObject) |
尝试使所请求的 UI 元素可见,并在目标上引发 RequestBringIntoView 事件以报告结果。 |
FindLogicalNode(DependencyObject, String) |
尝试查找并返回具有指定名称的对象。 搜索从指定对象开始,并持续到逻辑树的子节点中。 |
GetChildren(DependencyObject) |
通过处理逻辑树返回指定的对象的即时子对象集合。 |
GetChildren(FrameworkContentElement) |
通过处理逻辑树,返回指定 FrameworkContentElement 的直接子对象的集合。 |
GetChildren(FrameworkElement) |
通过处理逻辑树,返回指定 FrameworkElement 的直接子对象的集合。 |
GetParent(DependencyObject) |
通过处理逻辑树,返回指定对象的父对象。 |
四、可视化树及其用途
可视化树
WPF 中除了逻辑树的概念,还存在可视化树的概念。 可视化树描述了可视化对象的结构,由 Visual 基类表示。 为控件编写模板时,将定义或重新定义适用于该控件的可视化树。 对于出于性能和优化考虑需要对绘图进行较低级别控制的开发人员来说,他们也会对可视化树感兴趣。 在传统 WPF 应用程序编程中,可视化树的一个应用是:路由事件的事件路由大多遍历可视化树而非逻辑树。 路由事件行为的这种微妙之处可能不会很明显,除非你是控件作者。 通过可视化树对事件进行路由可使控件在可视化级别实现组合以处理事件或创建事件资源库。
树、内容元素和内容宿主
派生自) (类的内容元素 ContentElement 不是可视化树的一部分; 它们不是从继承的 Visual ,也不具有可视表示形式。 若要在 UI 中显示,必须在同时作为 ContentElement Visual 和逻辑树参与者的内容主机中承载。 通常,此类对象为 FrameworkElement 。 从概念上讲,内容宿主有些类似于内容的“浏览器”,它选择在该宿主控制的屏幕区域中显示内容的方式。 承载内容时,可以使内容成为通常与可视化树关联的某些树进程的参与者。 通常情况下, FrameworkElement 主机类包括实现代码,该代码 ContentElement 通过 content 逻辑树的子节点将所有宿主添加到事件路由,即使寄宿内容不是真正的可视化树的一部分。 这是必需的,以便 ContentElement 可以将路由的路由事件作为路由到自身之外的任何元素的源。
五、树的遍历
树遍历
LogicalTreeHelper类 GetChildren GetParent FindLogicalNode 为逻辑树遍历提供、和方法。 在大多数情况下,不需要遍历现有控件的逻辑树,因为这些控件几乎总是将其逻辑子元素公开为一个专用集合属性,这种属性支持集合访问,如 Add
、索引器等等。 树遍历主要是一种方案,控件作者可以选择不从预期的控件模式派生 ItemsControl ,如或 Panel 已定义集合属性的位置,以及要提供自己的集合属性支持的用户。
可视化树还支持 visual tree 遍历的帮助器类 VisualTreeHelper 。 可视化树不会通过特定于控件的属性方便地公开,因此, VisualTreeHelper 如果您的编程方案需要,则建议使用类遍历可视化树。 有关详细信息,请参阅 WPF 图形呈现概述。
备注
有时有必要检查所应用模板的可视化树。 执行此操作时应谨慎。 即使您正在遍历定义模板的控件的可视化树,控件的使用者也可以通过设置实例的属性来更改模板, Template 甚至最终用户也可以通过更改系统主题来影响应用的模板。
“树”形式路由事件的路由
如前所述,对于任何给定的路由事件,其路由都沿着一条预定的树路径进行,这棵树是可视化树和逻辑树表示形式的混合体。 事件路由可在树中向上或向下进行,具体取决于该事件是隧道路由事件还是浮升路由事件。 事件路由概念没有直接支持的帮助器类(此类可用于独立于引发实际路由的事件,遍历事件)。 有一个表示路由的类 EventRoute ,但该类的方法通常仅供内部使用。
资源字典和树
对页中定义的所有 Resources
进行资源字典查找时,基本上遍历逻辑树。 逻辑树之外的对象可以引用键控资源,但资源查找顺序将从该对象与逻辑树的连接点开始。 在 WPF 中,只有逻辑树节点可以具有 Resources
包含的属性 ResourceDictionary ,因此,在从查找用于加密资源的可视化树方面没有任何好处 ResourceDictionary 。
但是,资源查找也可以超出直接逻辑树。 对于应用程序标记,资源查找可向前继续进行到应用程序级资源字典,然后再到作为静态属性或键进行引用的主题支持和系统值。 如果资源引用是动态的,则主题本身也可以引用主题逻辑树之外的系统值。 有关资源字典和查找逻辑的详细信息,请参阅 XAML 资源。