[笔记/简译]WPF的新特性——逻辑树与视觉树(下)

我们可以简单地通过System.Windows.LogicalTreeHelperSystem.Windows.VisualTreeHelper实现对逻辑树和视觉树的遍历。如下例:

 

using System;

using System.Diagnostics;

using System.Windows;

using System.Windows.Media;

 

public partial class AboutDialog : System.Windows.Window

{

 

    public AboutDialog()

    {

        InitializeComponent();

        PrintLogicalTree(0, this);

    }

 

    protected override void OnContentRendered(EventArgs e)

    {

        base.OnContentRendered(e);

        PrintVisualTree(0, this);

    }

 

    void PrintLogicalTree(int depth, object obj)

    {

        //  按深度打印缩进空白

        Debug.WriteLine(new string(' ', depth) + obj);

 

        //  有时候叶子节点不是DependencyObject(string)

        if (!(obj is DependencyObject)) return;

 

        //  递归打印逻辑树的节点

        foreach (object child in LogicalTreeHelper.GetChildren(

            obj as DependencyObject))

        {

            PrintLogicalTree(depth + 1, child);

        }

    }

 

    void PrintVisualTree(int depth, DependencyObject obj)

    {

        //  按深度打印缩进空白

        Debug.WriteLine(new string(' ', depth) + obj);

 

        //  递归打印视觉树的节点

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)

        {

            PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i));

        }

    }

}

 

    AboutDialog的构造方法中的InitCompoment方法之后,可以调用PrintLogicalTree遍历逻辑树上的节点。然而对于视觉树,直到AboutDialog完成一次布局之前,它都将是空的,因此要在OnContentRendered方法中调用PrintVisualTree实现遍历。

 

    有时候在这两棵树上的导航操作可由元素自己的实例方法来完成。比如,Visual类包含三个受保护的成员方法VisualParentVisualChordCountGetVisualChild,它们用来检视它的视觉父元素和子元素。比如,ButtonLabel等控件的通用基类FrameworkElement定义了一个公共的Parent属性用来表示逻辑父元素。再比如,特定的FramewordElement子类们以不同的方式暴露了它们的逻辑子元素。如一些类暴露了Children集合,其它类(如ButtonLabel)则暴露了一个Content属性,用以表示这个元素只能包含一个逻辑子元素。

 

    2表示的树通常也被称作元素树,因为它同时包含了逻辑树元素和特定的视觉树元素。由此,术语视觉树用来描述任何仅包含了视觉元素的子树。例如,许多人会说Window默认视觉树由一个Border、一个AdornerDecorator、两个AdornerLayers和一个ContentPresenter组成。图2中,顶级StackPannel通常不被算做ContentPresenter的视觉子元素,尽管它可以由VisualTreeHelper输出。

 

    WPF中的这两棵树是一个非常神奇有趣的特色。我们可以看到,现在的控件不再像以前那样需要在一个或多个函数中重绘了。从前,如果我们想要在VC中制作一个自定义的菜单,需要响应WM_DRAWITEMWM_MEASUREITEM两条消息,然后根据参数在响应函数中生画,比如先画背景,然后是左侧的图标,然后是右侧的菜单文本,总之是一个烦人的工作。这也是很多人说的,用VC做界面不方便的地方。后来到了Windows Form,控件已经有了很好的编程模型,但是仍旧没有摆脱重绘的模式。想我曾经自绘了一个仿IE7的标签页控件,那叫一个累。不过如今好了,WPF中的控件是以树形的层次结构叠加而成的,这样使得修改控件的外观变得很方便。比如之前在《XAML揭秘》中的那个按钮示例,它的中心不是文本,而是一个黑色的方块,这正是因为,组成按钮的TextBlock被替换为Rectangle。使用如下XAML代码配合上面的程序代码即可看出。

 

<Window x:Class="AboutDialog"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="关于" SizeToContent="WidthAndHeight"

        Background="OrangeRed" Height="200">

  <StackPanel>

    <Button Content="OK" />

    <Button>

      <Button.Content>

        <Rectangle Height="40" Width="40" Fill="Black" />

      </Button.Content>

    </Button>

  </StackPanel>

</Window>

 

第一个Button输出的视觉树为:

System.Windows.Controls.Button: OK

Microsoft.Windows.Themes.ButtonChrome

           System.Windows.Controls.ContentPresenter

              System.Windows.Controls.TextBlock

 

第二个Button输出的视觉树为:

    System.Windows.Controls.Button

       Microsoft.Windows.Themes.ButtonChrome

           System.Windows.Controls.ContentPresenter

              System.Windows.Shapes.Rectangle

     很有趣吧!)

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值