WPF学习(4)逻辑树和可视树
前面几节说了一些WPF的基础,包括XAML和布局等。在接下来的几节,我们来说说WPF的核心概念,包括逻辑树和可视树、依赖对象和依赖属性、路由事件、命令这几个部分。本节介绍下逻辑树(Logical Tree)和可视树(Visual Tree)。
逻辑树和可视树
在WPF中,用户界面是由XAML来呈现的。粗略地讲,从宏观上看,叶子为布局组件和控件所组成的树既是逻辑树,从微观上看,将逻辑树的叶子再放大可看到其内部是由可视化组件(继承自Visual类)组成的,叶子为这些可视化组件组成的树既是可视树。
逻辑树
举个例子来说明:
<Window x:Class="WpfTreeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TabControl>
<TabItem Header="第一页">
<TextBlock Text="This is first page" />
</TabItem>
<TabItem Header="第二页">
<TextBox Text="This is second page" />
</TabItem>
</TabControl>
<Button x:Name="btnOK" Grid.Row="1" Width="80" Height="80" Click="btnOK_Click">
<Button.Content>
<Image Source="/Images/photo.png" x:Name="imgPhoto"/>
</Button.Content>
</Button>
<ListView x:Name="lvStudents" Grid.Row="2">
<ListView.View>
<GridView>
<GridViewColumn Header="Index" DisplayMemberBinding="{Binding Index}" />
<GridViewColumn Header="Username" DisplayMemberBinding="{Binding Username}" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
来看一下它的逻辑树:

层级感很强,这也正是XAML强大表现力的体现。如何来操作这棵树呢?最简单的方法当然是设置控件的name属性,然后在cs文件中根据name属性值来获取控件。WPF内置了一个LogicalTreeHelper类,我们可以通过它来遍历树,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using Microsoft.Windows.Themes;
using System.Diagnostics; namespace WpfTreeDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Student> students = new List<Student>
{
new Student{Index=,Username="Jello",Age=},
new Student{Index=,Username="Taffy",Age=}
};
this.lvStudents.ItemsSource = students;
PrintLogicalTree(, this); }
private void btnOK_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("I am a button");
}
public void PrintLogicalTree(int depth, object obj)
{
Debug.WriteLine(new string(' ', depth) + obj);
if (!(obj is DependencyObject)) return;
foreach (var child in LogicalTreeHelper.GetChildren(obj as DependencyObject))
{
PrintLogicalTree(depth + , child);
}
}
}
public class Student
{
public int Index { get; set; }
public string Username { get; set; }
public int Age { get; set; }
}
}
Debug运行后可以在Debug输出窗口看到界面的逻辑树。
可视树
想要观察可视树,需要将控件“打碎”来看下,这里我们以TextBox为例,用Blend来“打碎”它,看看这个控件的内部结构:
<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
<GradientStop Color="#ABADB3" Offset="0.05"/>
<GradientStop Color="#E2E3EA" Offset="0.07"/>
<GradientStop Color="#E3E9EF" Offset="1"/>
</LinearGradientBrush>
<Style x:Key="TextBoxStyle1" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Microsoft_Windows_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
主要是ListBoxChrome及ScrollViewer构成的,这也很好理解,将TextBox的功能拆分来看,ListBoxChrome主要可以用来输入,ScrollViewer用于当内容过多时会有滚动条。这里通过name属性一般是不能直接获取控件的,需要借助VisualTreeHelper类和Visual中的方法来获取。也举个例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using Microsoft.Windows.Themes;
using System.Diagnostics; namespace WpfTreeDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Student> students = new List<Student>
{
new Student{Index=,Username="Jello",Age=},
new Student{Index=,Username="Taffy",Age=}
};
this.lvStudents.ItemsSource = students;
PrintLogicalTree(, this); }
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
PrintVisualTree(, this);
}
private void btnOK_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("I am a button");
}
public void PrintLogicalTree(int depth, object obj)
{
Debug.WriteLine(new string(' ', depth) + obj);
if (!(obj is DependencyObject)) return;
foreach (var child in LogicalTreeHelper.GetChildren(obj as DependencyObject))
{
PrintLogicalTree(depth + , child);
}
}
public void PrintVisualTree(int depth, DependencyObject obj)
{
Debug.WriteLine(new string(' ', depth) + obj);
for (int i = ; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
PrintVisualTree(depth + , VisualTreeHelper.GetChild(obj, i));
}
}
}
public class Student
{
public int Index { get; set; }
public string Username { get; set; }
public int Age { get; set; }
}
}
之所以在OnContentRendered中调用了一次是因为可视树直到Window完成至少一次布局后才会有节点,否则是空的,而实例化发生在布局完成之前(可以获取逻辑树),
OnContentRendered调用发生在布局完成之后(可以获取可视树)。
这里需要注意几点:
1.并不是所有的元素(无与生俱来的呈现行为)都会出现可视树中,只有继承自Visual类或者Visual3D类的元素才会包含其中。
2.逻辑树是静态的,而可视树是动态的(当用户切换Theme是会改变)。
3.一般情况下,我们不需要考虑可视树,除非要进行控件重塑。
WPF学习(4)逻辑树和可视树的更多相关文章
- WPF路由事件一:逻辑树和可视树
一.什么是逻辑树 逻辑树就是描述WPF界面元素的实际构成,它是由程序在XAML中所有的UI元素组成.最显著的特点就是由布局控件.或者其他常用的控件组成. <Window x:Class=&quo ...
- wpf 逻辑树与可视化树
XAML天生就是用来呈现用户界面的,这是由于它具有层次化的特性.在WPF中,用户界面由一个对象树构建而成,这棵树叫作逻辑树.逻辑树的概念很直观,但是为什么要关注它呢?因为几乎WPF的每一方面(属性.事 ...
- WPF学习(6)路由事件
做过.net开发的朋友对于事件应该都不陌生.追溯历史,事件(Event)首先应用在Com和VB上,它是对在MFC中使用的烦琐的消息机制的一个封装,然后.net又继承了这种事件驱动机制,这种事件也叫.n ...
- WPF学习(5)依赖属性
今天我们来学习WPF一个比较重要的概念:依赖属性.这里推荐大家看看周永恒大哥的文章,讲的确实很不错.我理解的没那么深入,只能发表一下自己的浅见.提到依赖属性,不得不说我们经常使用的传统的.net属性, ...
- 【WPF学习】第五十八章 理解逻辑树和可视化树
在前面章节中,花费大量时间分析了窗口的内容模型——换句话说,研究了如何在其他元素中嵌套元素,进而构建完整的窗口. 例如,考虑下图中显示的一个非常简单的窗口,该窗口包含两个按钮.为创建该按钮,在窗口中嵌 ...
- WPF中的逻辑树和可视化树
WPF中的逻辑树是指XAML元素级别的嵌套关系,逻辑树中的节点对应着XAML中的元素. 为了方便地自定义控件模板,WPF在逻辑树的基础上进一步细化,形成了一个“可视化树(Visual Tree)”,树 ...
- WPF 中的逻辑树(Logical Tree)与可视化元素树(Visual Tree)
一.前言 WPF 中有两种"树":逻辑树(Logical Tree)和可视化元素树(Visual Tree). Logical Tree 最显著的特点就是它完全由布局组件和控件 ...
- WP8.1程序开发,可视树VisualTreeHelper类的使用
对于可视树的使用,很久之前就接触了, 一方面当时知识太浅根本看不懂,就放下没看了: 另一方面,也没用到,就没往这方面努力研究学习: 现在好了,遇到问题了,正好涉及到VisualTreeHelper的使 ...
- AI逻辑实现-取舍行为树还是状态机
AI逻辑实现-选择行为树还是状态机? 关注AI的朋友可能会看过赖勇浩翻译的<有限状态机时代终结的10大理由> ,里面谈到了状态机的诸多弊端.同时在ppt(附上下载地址)中述说了行为树的诸多 ...
随机推荐
- BZOJ 1109 POI2007 堆积木Klo LIS
题目大意:给定一个序列,能够多次将某个位置的数删掉并将后面全部数向左串一位,要求操作后a[i]=i的数最多 首先我们如果最后a[i]=i的数的序列为S 那么S满足随着i递增,a[i]递增(相对位置不变 ...
- Android Ant打包笔记
本文文档的下载地址(Word版):http://download.csdn.net/detail/yangwei19680827/7250711 Android Ant 打包 网上找了ant打包的资料 ...
- Redis 的性能幻想与残酷现实(转)
2011 年,当初选择 Redis 作为主要的内存数据存储,主要吸引我的是它提供多样的基础数据结构可以很方便的实现业务需求.另一方面又比较担心它的性能是否足以支撑,毕竟当时 Redis 还属于比较新的 ...
- javaEE jdbc编程步骤
1.载入数据库驱动(jar文件) //须要下载一个数据库的jar包,并导入对应的JDBC项目中,创建路径! Class.forName("com.mysql.jdbc.Driver" ...
- 黄聪:Microsoft Enterprise Library 5.0 系列教程(四) Logging Application Block
原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(四) Logging Application Block 企业库日志应用程序模块工作原理图: 从上图我们可以 ...
- windows phone (25) Canvas元素B
原文:windows phone (25) Canvas元素B ZIndex 这也是一个附加属性,表示canvas的children集合内的子元素的显示顺序,在canvas中的元素默认情况下是后面的 ...
- 表白程序源代码,android
弄了一个表白程序,还是不错的,内容能够自己设置.并附上源代码:http://download.csdn.net/detail/a358763471/7803571 看下效果图吧.是动画的哦...
- 令人无限遐想的各种PCIe加速板卡
声明 本文不涉及不论什么特定API,也不针对不论什么特定的厂商,可是仍然值得透露一点的是,某些加速板卡厂商的成功点和失败点恰恰都是在于其通用性,在这个人们依旧依赖专业板卡的时代,依旧将板卡视为解决专业 ...
- OpenCV原则解读HAAR+Adaboost
因为人脸检测项目.用途OpenCV在旧分类中的训练效果.因此该检测方法中所使用的分类归纳.加上自己的一些理解.重印一些好文章记录. 文章http://www.61ic.com/Article/DaVi ...
- Javascript设计模式系列二
创建对象的基本模式,一.门户大开型,二.采用下划线来表示属性和方法的私用性,三.使用闭包来创建私用的成员. 一.门户大开型.只能提供公用成员.所有属性和方法都公开的.可访问的.这些共用属性都要使用th ...