欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【WPF学习】第五十八章 理解逻辑树和可视化树

程序员文章站 2022-05-29 13:18:06
在前面章节中,花费大量时间分析了窗口的内容模型——换句话说,研究了如何在其他元素中嵌套元素,进而构建完整的窗口。 例如,考虑下图中显示的一个非常简单的窗口,该窗口包含两个按钮。为创建该按钮,在窗口中嵌套了一个StackPanel控件。在StackPanel控件中,放置了两个Button控件,并且在每 ......

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

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

【WPF学习】第五十八章 理解逻辑树和可视化树

  下面是该窗口的标记:

<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特性(如属性值继承、事件路由以及样式)都是通过逻辑树进行工作的。

【WPF学习】第五十八章 理解逻辑树和可视化树

 

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

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

【WPF学习】第五十八章 理解逻辑树和可视化树

 

 

  所有这些细节本身都是元素——换句话说,控件(如按钮)中的每个单独的细节都是由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()方法,可以遍历任意窗口的可视化树,并且为了进行分析可以将它们显示出来。这是一种非常好的学习工具,只需要使用一些递归的代码就可以实现。

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

【WPF学习】第五十八章 理解逻辑树和可视化树

   在该例中,名为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 = 0; 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();