原文:WPF的逻辑树与视觉树(2)Visual容器

 

一.摘要

虽然我们平时几乎不会从该类派生,但要想了解视觉树就必须要了解Visual,Visual是一个基本抽象类,继承自DependencyObject.其是所有控件的基类.并提供了视觉树操作的基本方法.

二.提纲

  1. 视觉树是一棵树
  2. 遍历视觉树
  3. 内置Visual集合容器ContainerVisual
  4. 小结

视觉树是一棵树

这好像是一句废话,但也没有错.我们来看下Visual提供的一些基本的成员。

首先我们创立一个测试的对象

  1. public class DefaultVisual : Visual
  2. {
  3. public string Key { get; set; }
  4.  
  5. protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
  6. {
  7. Console.WriteLine(this.Key + " ChildrenChanged");
  8. if (visualAdded != null) Console.WriteLine((visualAdded as DefaultVisual).Key+" Added");
  9. if (visualRemoved != null) Console.WriteLine((visualRemoved as DefaultVisual).Key+"Removed");
  10. base.OnVisualChildrenChanged(visualAdded, visualRemoved);
  11. }
  12.  
  13. protected override void OnVisualParentChanged(DependencyObject oldParent)
  14. {
  15. Console.WriteLine(this.Key + " ParentChanged");
  16. if (oldParent != null) Console.WriteLine((oldParent as DefaultVisual).Key);
  17. base.OnVisualParentChanged(oldParent);
  18. }
  19. }

测试代码

  1. public static void Test()
  2. {
  3. var test1 = new DefaultVisual();
  4. test1.Key = "test1";
  5. var test2 = new DefaultVisual();
  6. test2.Key = "test2";
  7. test1.AddVisualChild(test2);
  8. var test3 = new DefaultVisual();
  9. test3.Key = "test3";
  10. test2.AddVisualChild(test3);
  11. var test4 = new DefaultVisual();
  12. test4.Key = "test4";
  13. test1.AddVisualChild(test4);
  14. test1.RemoveVisualChild(test4);
  15. }

结果

2.遍历视觉树

在调用AddVisualChild的时候,将会为两个Visual之间建立父子关系,子级知道父级,但父级却不知道有几个子级.所以很难遍历全部节点.需要把子节点给保存下来.Visual提供了两个成员用于视觉树的遍历,只要实现这两个成员就可以使用VisualTreeHelper进行遍历了.

下面我们就来实现这两个成员

3.Visual容器

Visual本身具备一些功能,同时也可以充当容器。

在实际情况下,容器分为两种,单容器和集合容器.比如Border就是一个单容器,其内部只可以放一个元素.Panel是一个集合容器.可以放多个元素.

单容器实现

  1. public class SigletonVisual : DefaultVisual
  2. {
  3. public Visual _child;
  4. public Visual Child
  5. {
  6. get
  7. {
  8. return _child;
  9. }
  10. set
  11. {
  12. this.RemoveVisualChild(_child);
  13. this.AddVisualChild(value);
  14. _child = value;
  15. }
  16. }
  17.  
  18. protected override Visual GetVisualChild(int index)
  19. {
  20. return _child;
  21. }
  22.  
  23. protected override int VisualChildrenCount
  24. {
  25. get
  26. {
  27. if (this._child != null)
  28. {
  29. return 1;
  30. }
  31. return 0;
  32. }
  33. }
  34. }

集合容器实现

  1. public class PanelVisual : DefaultVisual
  2. {
  3. public List<Visual> Visuals { get; set; }
  4.  
  5. public PanelVisual()
  6. {
  7. Visuals = new List<Visual>(5);
  8. }
  9.  
  10. public void Add(Visual visual)
  11. {
  12. Visuals.Add(visual);
  13. this.AddVisualChild(visual);
  14. }
  15.  
  16. protected override Visual GetVisualChild(int index)
  17. {
  18. return Visuals[index];
  19. }
  20.  
  21. protected override int VisualChildrenCount
  22. {
  23. get
  24. {
  25. return Visuals.Count;
  26. }
  27. }
  28. }

遍历测试

  1. void Test()
  2. {
  3. var test1 = new PanelVisual();
  4. test1.Key = "test1";
  5. var test2 = new PanelVisual();
  6. test2.Key = "test2";
  7. test1.Add(test2);
  8. var test3 = new PanelVisual();
  9. test3.Key = "test3";
  10. test2.Add(test3);
  11. var test4 = new PanelVisual();
  12. test4.Key = "test4";
  13. test1.Add(test4);
  14. PrintVisualTree(0, test1);
  15. }
  16.  
  17. public void PrintVisualTree(int depth, PanelVisual obj)
  18. {
  19. Console.WriteLine(new string(' ', depth) + obj.Key);
  20. for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
  21. {
  22. PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i) as PanelVisual);
  23. }
  24. }

测试结果

3.内置Visual集合容器ContainerVisual

其实我们不用这么复杂,WPF内置类ContainerVisual已经默认实现了Visual集合容器,

ContainerVisual内部采用VisualCollection集合来维护视觉树,所以当我们添加Visual的时候,不需要调用AddVisualChild方法,而是应该调用VisualCollection的Add和Remove等方法

如下测试

  1. public class TestVisual : ContainerVisual
  2. {
  3. public static void Test2()
  4. {
  5. var test1 = new TestVisual();
  6. test1.Key = "test1";
  7. var test2 = new TestVisual();
  8. test2.Key = "test2";
  9. test1.Children.Add(test2);
  10. var test3 = new TestVisual();
  11. test3.Key = "test3";
  12. test2.Children.Add(test3);
  13. var test4 = new TestVisual();
  14. test4.Key = "test4";
  15. test1.Children.Add(test4);
  16. PrintVisualTree(0, test1);
  17. }
  18.  
  19. public static void PrintVisualTree(int depth, TestVisual obj)
  20. {
  21. Console.WriteLine(new string(' ', depth) + obj.Key);
  22. for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
  23. {
  24. PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i) as TestVisual);
  25. }
  26. }
  27.  
  28. public string Key { get; set; }
  29. }

测试结果是一样的,但我们就可以省却手动实现VisualChildrenCount和GetVisualChild这两个成员了.

如果不从ContainerVisual 继承又想简单的维护Visual的话,可以使用VisualCollection来维护.

4.小结

这篇讲到了Visual的基本功能,Visual本身具备父子级关系的功能,但默认没有容器,需要我们自己实现.内置的ContainerVisual 使用VisualCollection实现了一个Visual容器功能.有了容器才能遍历整个视觉树,对Visual进行一些交互.

可能到这里却还没有看到具体UI的呈现.那就下篇了.

WPF的逻辑树与视觉树(2)Visual容器的更多相关文章

  1. 【WPF】逻辑树和视觉树

    WPF中提供了遍历逻辑树和视觉树的辅助类:System.Windows.LogicalTreeHelper和 System.Windows.Media.VisualTreeHelper. 注意遍历的位 ...

  2. WPF的逻辑树与视觉树(1)基本概念

    原文:WPF的逻辑树与视觉树(1)基本概念     一.摘要 逻辑树与视觉树属于WPF的基本概念,学过WPF或者Silverlight的朋友一定会对其有所耳闻,这篇文章将来探讨逻辑树与视觉树的特质以及 ...

  3. WPF的逻辑树和视觉树

    原文:WPF的逻辑树和视觉树 这部分的内容来自于即将出版的新书<WPF Unleashed>的第三章样章.关于什么是逻辑树,我们先看下面的一个伪XAML代码的例子: <Window ...

  4. 理解WPF中的视觉树和逻辑树

    轉載地址:http://blog.csdn.net/changtianshuiyue/article/details/26981797 理解WPF中的视觉树和逻辑树  Understanding th ...

  5. WPF的逻辑树与视觉树(3)Visual呈现

    原文:WPF的逻辑树与视觉树(3)Visual呈现 这篇就点到为止,挑重点讲 绘图方式有两种 1.继承UIElement,重写OnRender方法 public partial class Windo ...

  6. wpf 逻辑树与可视化树

    XAML天生就是用来呈现用户界面的,这是由于它具有层次化的特性.在WPF中,用户界面由一个对象树构建而成,这棵树叫作逻辑树.逻辑树的概念很直观,但是为什么要关注它呢?因为几乎WPF的每一方面(属性.事 ...

  7. WPF学习(4)逻辑树和可视树

    前面几节说了一些WPF的基础,包括XAML和布局等.在接下来的几节,我们来说说WPF的核心概念,包括逻辑树和可视树.依赖对象和依赖属性.路由事件.命令这几个部分.本节介绍下逻辑树(Logical Tr ...

  8. WPF中的逻辑树和可视化树

    WPF中的逻辑树是指XAML元素级别的嵌套关系,逻辑树中的节点对应着XAML中的元素. 为了方便地自定义控件模板,WPF在逻辑树的基础上进一步细化,形成了一个“可视化树(Visual Tree)”,树 ...

  9. WPF路由事件一:逻辑树和可视树

    一.什么是逻辑树 逻辑树就是描述WPF界面元素的实际构成,它是由程序在XAML中所有的UI元素组成.最显著的特点就是由布局控件.或者其他常用的控件组成. <Window x:Class=&quo ...

随机推荐

  1. element ui源码解析 -- input篇

    el-input是element ui中使用最频繁的组件之一了,分析其构成从四个方面入手:DOM结构,属性,样式,事件入手 DOM结构: <div> <input /> < ...

  2. vue中的select框的值动态绑定

    <--这两种写法效果一样--> 1: <select v-model="wxStatus"> <option label="已添加" ...

  3. UILabel基本用法

    UILabel *_label = [[UILabel alloc]initWithFrame:CGRectMake(, self.view.frame.size.height*)]; _label. ...

  4. vue学习笔记二:v-if和v-show的区别

    v-if vs v-show v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做—— ...

  5. 【u248】交通序列号

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 在一条笔直的道路上共有N个路口,每个路口处都有关于该条道路的通行的信号灯. 显然,信号灯共有绿(G). ...

  6. 解决java中ZipFile解压缩时候的中文路径和乱码问题

    JAVA中对jar文件或zip文件解压的时候,能够使用JDK内置的API:JarFile和ZipFile,在windows下解压这2种格式文件的时候,常常报下面错误: Exception in thr ...

  7. 【51.27%】【codeforces 604A】Uncowed Forces

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  8. debian安装git管理本地代码

    debian安装git管理本地代码 安装git # aptitude install git-core # aptitude install git-doc git-svn git-email git ...

  9. js进阶正则表达式修饰符(i、g、m)(var reg2=/html/gi)

    js进阶正则表达式修饰符(i.g.m)(var reg2=/html/gi) 一.总结 1.正则表达式使用:通过那些支持正则表达式的字符串函数来使用(search.match.replace.spli ...

  10. java File类的基本使用

    package com.soar.file; import java.io.File; import java.io.IOException; public class Demo2_FileMetho ...