WPF在VisualTree上增加Visual

作者:Aaron 时间:2023-03-20 00:06:47 

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

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

  • 从FrameworkElement继承

  • 增加一个Button到它的VisualTree上

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

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 ;
       }
    }

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

<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>

运行后的结果如下:

WPF在VisualTree上增加Visual

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

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

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

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;
       }

再次运行程序:

WPF在VisualTree上增加Visual

目标实现。

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

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

  • 重写VisualChildCount属性;

  • 重写GetVisualChild方法;

  • 重写MeasureOverride方法;

  • 重写ArrangeOverride方法; 

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

1、Panel

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

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;
       }
   }

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

  • AddVisualChild

  • VisualChildCount

  • GetVisualChild

2、VisualCollection

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

来源:https://www.cnblogs.com/AaronLu/archive/2009/11/09/1599348.html

标签:WPF,VisualTree,增加,Visual
0
投稿

猜你喜欢

  • 详解SpringMVC如何进行数据回显

    2023-09-12 08:48:15
  • executor包执行器功能

    2023-07-26 21:07:36
  • 带你了解C++的数组与函数

    2023-12-10 20:44:18
  • Android自定义指示器时间轴效果实例代码详解

    2023-06-13 01:06:33
  • jar包运行时提示jar中没有主清单属性的解决

    2023-11-23 19:04:10
  • Spring容器的创建过程之如何注册BeanPostProcessor详解

    2022-02-17 08:39:09
  • java(jdk)环境变量配置(XP、win7、win8)图文教程详解

    2021-11-01 15:06:48
  • Mybatis判断空字符串的问题

    2022-11-18 02:07:00
  • C# 根据表格偶数、奇数加载不同颜色

    2022-01-19 11:46:05
  • C#实现过滤sql特殊字符的方法集合

    2022-01-30 23:58:04
  • C#自定义针对URL地址的处理类实例

    2022-09-12 16:54:02
  • Spring事务失效问题分析及解决方案

    2023-07-09 11:08:15
  • opencv利用视频的前n帧求平均图像

    2021-06-20 11:43:02
  • Android 自定义AlertDialog对话框样式

    2022-02-06 12:20:07
  • Java Floyd算法求有权图(非负权)的最短路径并打印

    2023-04-10 12:53:42
  • java项目中的多线程实践记录

    2023-10-16 16:12:03
  • 基于Android SQLite的升级详解

    2021-06-24 05:26:05
  • Java中的字节,字符输出流与字节和字符输入流的简单理解

    2022-11-30 01:56:13
  • Android Volley图片加载功能详解

    2023-11-19 08:01:41
  • Android App实现应用内部自动更新的最基本方法示例

    2022-11-02 02:49:46
  • asp之家 软件编程 m.aspxhome.com