WPF,Silverlight与XAML读书笔记第六 WPF新概念之一逻辑树与可视树

说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。

         WPF中,XAML来呈现用户界面,其层次化的特性构建了用户界面需要的对象树,这棵树为逻辑树。通过XAML与过程式代码(C#)都可以实现逻辑树。逻辑树概念很重要,其与WPF的属性,事件,资源等很多方面相关联。如属性值会沿着树自动传递给子元素(后文要介绍的依赖属性),而触发的事件可以自底向上或自顶向下遍历树(后文要介绍的路由事件)。

可视树类似于逻辑树,其基本上是逻辑树的扩展,在可视树中,节点都被打散,分散到核心可视组件,这样其就提供了一些详细的可视化实现。

如逻辑树中的节点ListBox在可视化树中对应到一个Border对象,两个ScrollBar及其它元素。

只有由System.Window.Media.VisualSystem.Window.Media.Visual3D派生的元素才会出现可视树中。其它元素不出现在可视树中的原因很简单它们并没有呈现自己的能力。

查看可视树的方法,将XAML复制到XamlPad中,将最外层容器由Window改为Page(并删除SizeToContent属性),这样点击相应按钮就可以看XAML对应的可视树(及其中每个元素的属性),下面给出一个示例:

XAML代码:

<StackPanel>
      <Label FontWeight="Bold" FontSize="20"   Foreground="White" Background="OrangeRed">
          Show Visual Tree
      </Label>
      <Label>by hystar</Label>
      <ListBox>
          <ListBoxItem>Chapter 1</ListBoxItem>
          <ListBoxItem>Chapter 2</ListBoxItem>
    </ListBox>
      <StackPanel Orientation="Horizontal"   HorizontalAlignment="Center">
          <Button MinWidth="75"   Margin="10">Help</Button>
          <Button MinWidth="75"   Margin="10">OK</Button>
      </StackPanel>
      <StatusBar>This is a StatusBar an bottom.</StatusBar>
</StackPanel>

将其拷贝到XamlPad中下方的<Grid>标签之间,上方显示出XAML时时解析的结果。

如下图:点击这个红色圆圈标记的按钮(Show/Hide visual tree),即可显示树状展示的可视化树与每个元素的属性的树状列表。

clip_image002.jpg

       WPF中有一个核心原则就是可视部分与逻辑部分分开。这两部分大致分别对应可视树与逻辑树。可视树会受到用户切换Window主题的影响,但逻辑树不会,其是静态的,动态添加删除元素也不会影响逻辑树。

使用System.Window.LogicalTreeHelperSystem.Window.Media.VisualTreeHelper这两个类可以方便的遍历一段代码的逻辑树与可视树。下面是两个代码,分别实现遍历前文那段XAML代码的逻辑树与可视树:

遍历逻辑树:

C#

public Window1()
{
    InitializeComponent();
    PrintLogicalTree(0, this);
}
 
private 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);
}
 
protected override void OnContentRendered(EventArgs   e)
{
    base.OnContentRendered(e);
    PrintVisualTree(0, this);
}
 
private 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));
}

    以上代码,构造函数中调用的函数就实现遍历逻辑树,这也证明了前文所述逻辑树的静态性。但可视树需要一次布局后才会生成,所以遍历可视树需要在布局完成后的Content Rendered事件处理函数OnContentRendered中遍历。

    另外,元素自己的实例方法也可以操作两种树。

    对于可视树,Visual类的3个protected成员VisualParent,VisualChildrenCount和GetVisualChild可以用来访问一个可视节点的父节点与子节点。

    对于逻辑树,所有通用控件(如Button和Label)的基类,FrameworkElement的Parent公共属性用来访问父节点。对于子节点由此元素的Children集合属性提供,有些以Content提供(说明这个元素只能有一个子元素/节点,如Button与Label – 具体原因前文有说明)。

 

参考:

WPF揭秘》

 

原文地址:https://www.cnblogs.com/lsxqw2004/p/4554323.html