在前面章节中,花费大量时间分析了窗口的内容模型——换句话说,研究了如何在其他元素中嵌套元素,进而构建完整的窗口。

  例如,考虑下图中显示的一个非常简单的窗口,该窗口包含两个按钮。为创建该按钮,在窗口中嵌套了一个StackPanel控件。在StackPanel控件中,放置了两个Button控件,并且在每个按钮中可以添加所选择的内容。

  下面是该窗口的标记:

<Window x:Class="SimpleWindow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="301.316" Width="306.579">
<StackPanel Margin="5">
<Button Padding="5" Margin="5" Click="cmd_Click">First Button</Button>
<Button Padding="5" Margin="5" Click="cmd_Click">Second Button</Button>
</StackPanel>
</Window>

  添加的元素分类称为逻辑树,下图中显示了逻辑树。WPF编程人员需要耗费大部分时间构建逻辑树,然后使用事件处理代码支持他们。实际上,到目前为止介绍的所有WPF特性(如属性值继承、事件路由以及样式)都是通过逻辑树进行工作的。

  然而,如果希望自定义元素,逻辑树起不到多大帮助作用。显然,可使用另一个元素替换整个元素(例如,可使用自定义的FacyButton类替换当前的Button类),但这需要更多工作,并且可能扰乱应用程序的用户界面或代码。因此,WPF通过可视化树进入更深层次。

  可视化树是逻辑树的扩展版本。它将元素分为更小的部分,换句话说,它并不查看被精心封装到一起的黑色方框,如按钮,而是查看按钮的可视化元素——使按钮具有阴影背景特性的边框(由ButtonChrome类表示)、内部的容器(ContentPresenter对象)以及存储按钮文本的块(由大家熟悉的TextBlock表示)。下图显示上面示例的可视化树。

  所有这些细节本身都是元素——换句话说,控件(如按钮)中的每个单独的细节都是由FrameworkElement类的派生类表示的。

  到目前为止介绍的内容似乎并没有什么值得注意的,只是介绍了所有WPF元素可被分解成更小的部分。但这对于WPF开发人员有什么用处呢?通过可视化树可以完成以下两项非常有用的工作:

  •   可视化样式改变可视化树中的元素。可使用Style.TargetType熟悉选择希望修改的特定元素。甚至当控件属性发生变化时,可使用触发器自动完成更改。不过,某些特定的细节很难甚至无法修改。
  •   可为控件创建新模板。对于这种情况,控件模板将被用于按期望的方式构建可视化树。

  非常有趣的是,WPF提供了用于浏览逻辑树和可视化树的两个类:System.Windows.LogicalTreeHelper和System.Windows.Media.VisualTreeHelper。

  LogicalTreeHelper类允许通过动态加载XAML文档在WPF应用程序中关联事件处理程序。LogicalTreeHelper类提供了较少的方法,下表列出了这些方法。尽管这些方法偶尔很有用,但大多数情况下回改用特定的FrameworkElement类中的方法。

表 LogicalTreeHelper类的方法

 名   称     说      明
FindLogicalNode() 根据名称查找特定元素,从指定的元素开始并向下查找逻辑树
BringIntoView() 如果元素在可滚动的容器中,并且当前不可见,就将元素滚动到试图中。FrameworkElement.BringIntoView()方法执行相同的工作
GetPrarent() 获取指定元素的父元素
GetChildren() 获取指定元素的子元素。不同元素支持不同的内容模型。例如,面板支持多个子元素,而内容控件只支持一个子元素。然而,GetChildren()方法抽象了这一区别,并且可使用任何类型的元素进行工作

  除了抓们用来执行低级绘图操作的一些方法外,VisualTreeHelper类提供的方法与LogicalTreeHelper类提供的方法类似,也提供了GetChildrenCount()、GetChild()以及GetParent()方法。

  VisualTreeHelper类还提供了一种研究应用程序中可视化树的有趣方法。使用GetChild()方法,可以遍历任意窗口的可视化树,并且为了进行分析可以将它们显示出来。这是一种非常好的学习工具,只需要使用一些递归的代码就可以实现。

  下图显示了一种可能的实现。该例在一个单独的窗口中显示了一颗完整的可视化树,该可视化树从提供的任意对象开始。

  在该例中,名为MainWindow的窗口包含一个Border元素,这个Border元素包含一个AdornerDecorator元素(AdornerDecorator类在装饰层中添加对绘制内容的支持,装饰层是特殊的不可见区域,该区域覆盖在元素内容之上。WPF使用装饰层绘制一些细节,如焦点提示以及拖放指示器)。AdornerDecorator元素内是一个ContentPresenter元素,该元素承载了窗口内容。窗口内容包含的StackPanel面板具有两个Button控件,每个Button控件包含一个ButtonChrome元素(该元素绘制按钮的标准化可视外观)和一个ContentPresenter元素(该元素包含了按钮的内容)。最后,在每个按钮的ContentPresenter元素中是TextBlock元素,TextBlock元素封装了在窗口中可见的文本。

  下面是VisualTreeDisplay窗口的完整代码:

 public partial class VisualTreeDisplay : Window
{
public VisualTreeDisplay()
{
InitializeComponent();
}
public void ShowVisualTree(DependencyObject element)
{
// Clear the tree.
treeElements.Items.Clear(); // Start processing elements, begin at the root.
ProcessElement(element, null);
} private void ProcessElement(DependencyObject element, TreeViewItem previousItem)
{
// Create a TreeViewItem for the current element.
TreeViewItem item = new TreeViewItem();
item.Header = element.GetType().Name;
item.IsExpanded = true; // Check whether this item should be added to the root of the tree
//(if it's the first item), or nested under another item.
if (previousItem == null)
{
treeElements.Items.Add(item);
}
else
{
previousItem.Items.Add(item);
} // Check if this element contains other elements.
for (int i = ; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
// Process each contained element recursively.
ProcessElement(VisualTreeHelper.GetChild(element, i), item);
}
}
}

  一旦为项目添加这棵树,就可以使用其他任何窗口的代码显示其可视化树:

VisualTreeDisplay treeDisplay = new VisualTreeDisplay();
treeDisplay.ShowVisualTree(this);
treeDisplay.Show();

【WPF学习】第五十八章 理解逻辑树和可视化树的更多相关文章

  1. 【WPF学习】第二十八章 程序集资源

    WPF应用程序中的程序集资源与其他.NET应用程序中的程序集资源在本质上是相同的.基本概念是为项目添加文件,从而Visual studio可将其嵌入到编译过的应用程序的EXE或DLL文件中.WPF程序 ...

  2. 【WPF学习】第十八章 多点触控输入

    多点触控(multi-touch)是通过触摸屏幕与应用程序进行交互的一种方式.多点触控输入和更传统的基于笔(pen-based)的输入的区别是多点触控识别手势(gesture)——用户可移动多根手指以 ...

  3. “全栈2019”Java第五十八章:多态中方法返回类型可以是子类类型

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. 【WPF学习】第二十六章 Application类——应用程序的生命周期

    在WPF中,应用程序会经历简单的生命周期.在应用程序启动后,将立即创建应用程序对象,在应用程序运行时触发各种应用程序事件,你可以选择监视其中的某些事件.最后,当释放应用程序对象时,应用程序将结束. 一 ...

  5. wpf 逻辑树与可视化树

    XAML天生就是用来呈现用户界面的,这是由于它具有层次化的特性.在WPF中,用户界面由一个对象树构建而成,这棵树叫作逻辑树.逻辑树的概念很直观,但是为什么要关注它呢?因为几乎WPF的每一方面(属性.事 ...

  6. WPF中的逻辑树和可视化树

    WPF中的逻辑树是指XAML元素级别的嵌套关系,逻辑树中的节点对应着XAML中的元素. 为了方便地自定义控件模板,WPF在逻辑树的基础上进一步细化,形成了一个“可视化树(Visual Tree)”,树 ...

  7. 【WPF学习】第四十八章 理解WPF动画

    在许多用户框架中(特别是WPF之前的框架,如Windows窗体和MFC),开发人员必须从头构建自己的动画系统.最常用的技术是结合使用计时器和一些自定义的绘图逻辑.WPF通过自带的基于属性的动画系统,改 ...

  8. 【WPF学习】第十五章 WPF事件

    前两章学习了WPF事件的工作原理,现在分析一下在代码中可以处理的各类事件.尽管每个元素都提供了许多事件,但最重要的事件通常包括以下5类: 生命周期事件:在元素被初始化.加载或卸载时发生这些事件. 鼠标 ...

  9. 【WPF学习】第二十五章 日期控件

    WPF包含两个日期控件:Calender和DatePicker.这两个控件都被设计为允许用户选择日期. Calendar控件显示日期,在与Windows操作系统中看到的日历(例如,当配置系统日期时看到 ...

随机推荐

  1. 管理Exchange Online用户介绍(二)

    一.Exchange Online配置邮件传递限制 1..进入“Exchange 管理中心”,依次点击 收件人->邮箱->选择需要管理的用户->编辑->邮箱功能->邮件传 ...

  2. 98)PHP,文件类型获取和创建文件夹

    看手册  finfo这个类:This class provides an object oriented interface into the fileinfo functions. 这个$mime_ ...

  3. 吴裕雄--天生自然python学习笔记:python OpenCV 基本绘图

    Open CV 提供了绘制直线.圆形.矩形等基本绘 图的功能 . Open CV 画直线的语法为: 在画布上添加文字的语法为 : 用 Open CV 绘制基本图形 以 OpenCV 基本绘图绘制各种图 ...

  4. 简化Java编程的法宝,让工作更高效

    如果你没有看过之前的文章,也不要紧,这并不影响你对接下来的内容的理解,不过为了照顾直接看到第二篇的同学,还是有必要介绍一下HuTool的引入方式. 在项目的pom.xml的dependencies中加 ...

  5. Java开发面试常见问题合集

    次面试事故 面试官:你看过哪些源码?我:都挺熟悉的面试官:对hashMap了解程度怎么样?面试官:那你能讲讲 HashMap的实现原理吗?面试官:HashMap什么时候会进行 rehash?面试官:结 ...

  6. win10安装mudbox失败,怎么强力卸载删除注册表并重新安装

    一些搞设计的朋友在win10系统下安装mudbox失败或提示已安装,也有时候想重新安装mudbox的时候会出现本电脑windows系统已安装mudbox,你要是不留意直接安装mudbox,只会安装mu ...

  7. 配置gitlab(备忘)

    已经配置好github的基础上,clone gitlab 地址git status 显示改变了的文件但是webstorm文件颜色不改变问题的解决:VCS->git-->remotes--& ...

  8. drf分页组件补充

    drf偏移分页组件 pahenations.py from rest_framework.pagination import LimitOffsetPagination class MyLimitOf ...

  9. Substring(Codeforces-D-拓扑排序)

    D. Substring time limit per test 3 seconds memory limit per test 256 megabytes You are given a graph ...

  10. 分布式ID生成策略 · fossi

    分布式环境下如何保证ID的不重复呢?一般我们可能会想到用UUID来实现嘛.但是UUID一般可以获取当前时间的毫秒数再加点随机数,但是在高并发下仍然可能重复.最重要的是,如果我要用这种UUID来生成分表 ...