代码改变世界

WPF:如何在VisualTree上增加Visual?

2009-11-09 23:50  Aaron Lu  阅读(2849)  评论(12编辑  收藏  举报

作为一个WPF控件开发者,我在工作中经常遇到如本文标题所示的问题。其实,这个问题并不是很难,只是在操作上有些繁琐。本文将尝试对这个问题进行解答,并且对相关的一些技术细节加以探讨。  

先从我遇到的一个典型的问题开始吧:写一个MyElement类,要求如下:

  • FrameworkElement继承
  • 增加一个Button到它的VisualTree  

Visual上有一个AddVisualChild方法,相信很多刚接触这个方法的同学们(好吧,至少我是这样)都会“顾名思义”地认为这个方法就可以解决本文的问题。再加上MSDN上也给出了一个例子来“火上浇油”一把。于是,一阵窃喜之后,我兴奋地敲出了以下代码: 

 

Code

然后将这个MyElement加入测试窗口,代码如下: 

 

Code

运行后的结果如下:

 

空空如也!嗯,被忽悠了。一阵失落、打击之后,我的好奇心被激发了:这是为什么呢?于是我狂找资料,终于被我发现了: 

实际上,在上面这个例子中,AddVisualChild这个方法只是在MyElement和Button之间建立起了一种VisualTree上的父子关系,但是并没有将Button挂接到MyElement的VisualTree上,所以最终我们没有在屏幕上看到这个Button。

为了将Button真正挂接到MyElement的VisualTree上,还需要额外做一件事情:在VisualTree上为这个Button分配空间并且指定位置,这个过程叫做Layout。此过程分两个部分:一个是Measure,另一个是Arrange。这两个过程在FrameworkElement上对应着两个方法:MeasureOverride和ArrangeOverride方法。具体做法如下: 

 

Code

再次运行程序:

 

目标实现。

      由此,我们可以总结出这个问题的解决方案如下:

  • MyElement的构造器中调用AddVisualChild方法;
  • 重写VisualChildCount属性;
  • 重写GetVisualChild方法;
  • 重写MeasureOverride方法;
  • 重写ArrangeOverride方法; 

另外,WPF在此问题的解决上也为开发者提供了一些必要的帮助。就我所知的,有如下几个内容:

1、Panel

     还是本文开始提到的问题,只不过要将其中的FrameworkElement换为Panel。除了上面所提到的方法,Panel为我们提供了更加方便的实现方式。代码如下: 

 

Code

之所以能这样做的原因是Panel已经替我们将如下几个工作封装在了UIElementCollection(Panel的Children属性)中:

  • AddVisualChild
  • VisualChildCount
  • GetVisualChild

 2、VisualCollection

      另外,在这个过程中,我们还可以使用一个叫做VisualCollection的类来作为所有 Visual Child的容器。这个容器构造的时候需要一个Visual类型的Parent,然后在添加、删除Visual Child的时候,它的相应方法(Add,Remove)就会帮助我们自动调用Parent的AddVisualChild和RemoveVisualChild方法。如此一来,我们的工作量又减少了。具体的实现代码很简单,这里就不贴了(总得动动脑子是不?)。

 

     希望对大家有所帮助。如果您有更好的方法或者对本文所提到的方法有所质疑,欢迎和我交流。

Creative Commons License

本博客内容除特别声明外,全部使用 创作共用(署名-非商业性使用) 2.5 中文版许可协议共享。欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 路响亮 (包含链接)。如果您有任何授权方面的疑问,请 给我留言