• WPF Unleashed Chapter 3:Important New Concepts in WPF Logical and Visual Trees 翻译


      本书的第一部分已经介绍完了,在开始接触wpf真正有意思的部分之前,我将介绍一些.NET程序员不太熟悉的概念。这些概念是WPF所特有的,是学习WPF的拦路虎,只有掌握之后,您才能更好地掌握本书后面介绍的知识。 

         其中的一些概念是WPF特有的(例如逻辑树和视觉树);还有一些是在原有个概念的一些扩充(例如属性(property)和事件)。学习完这些概念后,我们提供了一个应用这些概念的例子:一个"About dialog"
       
         逻辑树和视觉树 

         XAML天然的层次性使得它非常适合用于展现用户界面。WPF的用户界面是由一个"对象树"来构成的.这个树就是我们所说的逻辑树。 

        List 3.1中的代码定义了"About dialog",它使用Window元素作为逻辑树的根。Window控件内部包含一个子元素:StackPanel控件(第六章"Layout with Panels"将介绍),StackPanel内部还包含一个内部有两个Button控件的StackPanel。

       LISTING 3.1 A Simple About Dialog in XAML


    <Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
    Title=”About WPF Unleashed” SizeToContent=”WidthAndHeight”
    Background=”OrangeRed”> 
        <StackPanel> 
            <Label FontWeight=”Bold” FontSize=”20” Foreground=”White”> 
                WPF Unleashed (Version 3.0) 
            </Label> 
            <Label>© 2006 SAMS Publishing</Label> 
            <Label>Installed Chapters:</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>You have successfully registered this product.</StatusBar> 
        </StackPanel>
    </Window>

      图3.1是List 3.1中代码运行后的结果(你可以将里面的代码复制到XamlPad),图3.2是这个窗体所对应的逻辑树。
     
       FIGURE 3.1 The rendered dialog from Listing 3.1.

       需要注意的是:逻辑树并不只存在于使用XAML构建的对象中,使用程序代码构建的对象同样存在逻辑树。
      
    FIGURE 3.2 The logical tree for Listing 3.1.
       从图中可以看出,逻辑树的概念还是比较简单的。那我们为何还要这么关注逻辑树这个概念呢?这是因为逻辑树会在会多方面影响到WPF。如属性,事件,资源等。举个例子来说,属性的值会沿着逻辑树传递到子元素上;事件的触发也是沿着逻辑树传递的。这些例子我们会在后面讨论 

        和逻辑树类似的是视觉树。逻辑树中包含很多节点,这些节点对我们来说是个黑盒(black box)。视觉树是逻辑树的一种扩展,可以将逻辑树中的节点分解成核心的视觉组件,这样视觉树可以将这些黑盒的内部实现暴露给我们。举例来说,虽然一个的ListBox在逻辑上是一个单独的控件,但是从视觉树的角度上看,ListBox是由很多wpf的原始元素组成:一个Border,两个ScrollBars...  

       需要说明的是:并不是逻辑树中的每个节点都是视觉树,符合条件的只有那些继承子 System.WindowsMedia.Visual或者System.Windows.Media.Visual3D的元素。其他的元素并不包括在内,因为那些元素(包括简单的字符串内容)没有继承自身的呈现行为。 

          图3.3是List 3.1中代码在Windows Vista的Aero主题下运行时的视觉树。图中的一些组件是在运行时看不到的,例如ListBox控件的那两个ScrollBar,Label控件的Border.从图中我们可以看出Button,Label,ListBoxItem这些控件都是由相同的一些元素组成。当然,Button控件使用的是ButtonChorme元素而不是Border元素。(这些控件之所以看上去不同是因为属性的默认值不同。例如Button的Margin属性的默认值为10,10,10,10,而Label控件的Margio值都为0)
       
    FIGURE 3.3 The visual tree for Listing 3.1, with logical tree nodes emphasized.

          视觉树是相当复杂的,它是WPF底层架构中非常本质的部分,可以让我们对WPF的构成有深入的了解. 幸运的是,我们没必要对其过多关注,除非你要彻底地改变一个控件的样式(第10章“Styles, Templates, Skins, and Themes”会提到)或者做一些底层的绘图(第11章2D Graphics会提到)

    WARNING

    避免编写和特定视觉树紧密结合的代码,虽然视觉树不会因为添加删除元素的操作而改变,但当控件应用不同的主题时,同一个元素的视觉树是可能不同的


        我们使用System.Windows.LogicalTreeHelperSystem.Windows.Media.VisualTreeHelper类可以很容易分割逻辑树和视觉树。List 3.2中的代码是List 3.1的XAML代码的后台代码。调试程序时会以深度优先的方式展现窗体的逻辑树和视觉树。
       
       LISTING 3.2 Walking and Printing the Logical and Visual Trees


    using System;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Media;
    public partial class AboutDialog : 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) 
            { 
                // Print the object with preceding spaces that represent its depth 
                Debug.WriteLine(new string(‘ ‘, depth) + obj); 
                // Sometimes leaf nodes aren’t DependencyObjects (e.g. strings) 
                if (!(obj is DependencyObject)) return; 
                    // Recursive call for each logical child 
                foreach (object child in LogicalTreeHelper.GetChildren( obj as DependencyObject))
                    PrintLogicalTree(depth + 1, child); 
            } 

        void PrintVisualTree(int depth, DependencyObject obj) 
            { 
                // Print the object with preceding spaces that represent its depth 
                Debug.WriteLine(new string(‘ ‘, depth) + obj); 
                // Recursive call for each visual child 
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) 
                    PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i)); 
            }
    }


     
           将窗体本身传递给方法后(PrintVisualTree和PrintLogicalTree),得到的是和图3.2和图3.3一样的结果,只不过是文本形式的。我们可以在Window的构造函调用PrintLogicalTree方法,对其逻辑树进行遍历。但是视觉树则需要整个窗体呈现后才能够遍历。所以我们才将PrintVisualTree方法放OnContentRendered方法内调用,而不是像PrintLogicalTree那样放在构造函数中。

            遍历任意一棵树时都可以使用树中元素的实例方法。例如,Visual类有三个保护成员(VisualParent,VisualChildrenCount和GetVisualChild),用于得到Visual对象的父和子。FrameworkElement是许多控件的基类。例如Button,Label都是它的子类。FrameworkElement定义了一个公共的属性:Parent。用于呈现逻辑上的父对象。而FrameworkElement的各个子类所暴露的逻辑子对象也各有不同。例如有的类的子类可以是集合,而有的类则暴露一个内容属性(Content property),强迫该元素只能包含一个逻辑子对象

           
            下一节将介绍Dependency Property,它是WPF非常重要的特性...
  • 相关阅读:
    个人第三次作业——原型设计
    《构建之法》团队作业第一次
    vsCode如何将结果输入到调试控制台
    Beta-冲刺第三天
    Beta版本(有更改)
    Beta冲刺-第二天
    Beta冲刺—第一天
    个人作业-测试
    团队项目—系统设计
    团队项目-需求分析
  • 原文地址:https://www.cnblogs.com/stswordman/p/791654.html
Copyright © 2020-2023  润新知