详解WPF中VisualTree增加Visual的处理方法

开发 后端
本文将介绍的是WPF中VisualTree增加Visual的处理方法,希望对大家用好WPF有所帮助。

这里我们将介绍WPF中VisualTree增加Visual的处理方法,首先我们将从一个典型的问题开始讲述。希望这些讨论能对大家有所帮助。

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

#T#

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

从FrameworkElement继承

增加一个Button到它的VisualTree上

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

Code  
 class MyElement : FrameworkElement  
{  
private Button _button = new Button() { Content = "I'm a Button!"};  
 
public MyElement()  
{  
this.AssembleVisualChildren();  
}  
 
private void AssembleVisualChildren()  
{  
this.AddVisualChild(this._button);  
}  
protected override int VisualChildrenCount  
{  
get 
{  
return 1;  
}  
}  
protected override Visual GetVisualChild(int index)  
{  
return this._button ;  
}  
 } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

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

Code     
<Window      
x:Class="AddVisualChildTest.Window1"    
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
xmlns:loc="clr-namespace:AddVisualChildTest"    
WindowStartupLocation="CenterScreen"    
Title="Window1" Height="300" Width="300">    
<Grid>    
<loc:MyElement Margin="10"/>    
</Grid>    
</Window>  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

运行后的结果如下:

运行结果

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

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

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

Code  
protected override Size MeasureOverride(Size availableSize)  
{  
if (this.VisualChildrenCount > 0)  
{  
UIElement child = this.GetVisualChild(0) as UIElement;  
Debug.Assert(child != null); // !Assert  
child.Measure(availableSize);  
return child.DesiredSize;  
}  
 
return availableSize;  
}  
 
protected override Size ArrangeOverride(Size finalSize)  
{  
Rect arrangeRect = new Rect()  
{  
Width = finalSize.Width,  
Height = finalSize.Height  
};  
 
if (this.VisualChildrenCount > 0)  
{  
UIElement child = this.GetVisualChild(0) as UIElement;  
Debug.Assert(child != null); // !Assert  
child.Arrange(arrangeRect);  
}  
 
return finalSize;  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

再次运行程序:

再次运行程序结果

目标实现。

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

在MyElement的构造器中调用AddVisualChild方法;

重写VisualChildCount属性;

重写GetVisualChild方法;

重写MeasureOverride方法;

重写ArrangeOverride方法;

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

1、Panel

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

Code  
 class MyElement : Panel  
{  
private Button _button = new Button() { Content = "I'm a Button!" };  
 
public MyElement()  
{  
this.Children.Add(_button);  
}  
 
protected override Size MeasureOverride(Size availableSize)  
{  
if (this.VisualChildrenCount > 0)  
{  
UIElement child = this.GetVisualChild(0) as UIElement;  
Debug.Assert(child != null); // !Assert  
child.Measure(availableSize);  
return child.DesiredSize;  
}  
 
return availableSize;  
}  
protected override Size ArrangeOverride(Size finalSize)  
{  
Rect arrangeRect = new Rect()  
{  
Width = finalSize.Width,  
Height = finalSize.Height  
};  
 
if (this.VisualChildrenCount > 0)  
{  
UIElement child = this.GetVisualChild(0) as UIElement;  
Debug.Assert(child != null); // !Assert  
child.Arrange(arrangeRect);  
}  
 
return finalSize;  
}  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

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

AddVisualChild

VisualChildCount

GetVisualChild

2、VisualCollection

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

原文标题:WPF:如何在VisualTree上增加Visual?

链接:http://www.cnblogs.com/AaronLu/archive/2009/11/09/1599348.html

责任编辑:彭凡 来源: 博客园
相关推荐

2011-05-24 15:48:24

linux软路由路由

2009-12-28 13:28:03

WPF视频

2009-12-28 16:00:36

WPF样式继承

2009-12-24 17:57:53

WPF页面跳转

2010-03-01 09:16:22

Visual Stud

2010-10-13 08:57:06

Visual Stud

2009-12-24 16:11:07

WPF图像处理

2009-07-01 16:52:47

增加浏览器Visual Stud

2009-07-01 18:17:32

JSP处理Cookie

2010-01-27 10:32:40

Visual Stud

2009-11-24 09:00:02

Visual Stud

2009-12-28 16:10:38

WPF生成文件

2009-12-28 17:17:52

WPF导航

2012-04-29 11:13:14

APP

2009-09-10 09:15:38

监视程序死锁

2010-01-14 14:12:14

Visual Stud

2010-02-23 09:02:00

Visual Stud

2010-12-16 10:00:20

QtVisual Stud

2009-10-14 09:08:23

Visual Stud

2009-11-04 09:16:00

Visual Stud
点赞
收藏

51CTO技术栈公众号