WPF在VisualTree上增加Visual

 更新时间:2022年6月27日 15:32  点击:247 作者:天方

作为一个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>

运行后的结果如下:

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

实际上,在上面这个例子中,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;
        }

再次运行程序:

目标实现。

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

  • 在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方法。如此一来,我们的工作量又减少了。

到此这篇关于WPF在VisualTree上增加Visual的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

原文出处:https://www.cnblogs.com/AaronLu/archive/2009/11/09/1599348.h

[!--infotagslink--]

相关文章

  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 怎么用PS为人像脸部增加打散颗粒特效

    用PS将人像脸部打造成打散颗粒的效果,成品出来后很美,过程略复杂,需要耐心和细心。现在跟着步骤来做吧! 1、首先在PS里面打开这张图。 2、选择快速选择工具的增加...2016-12-21
  • c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)

    这篇文章主要介绍了c# WPF中通过双击编辑DataGrid中Cell的示例(附源码),帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...2021-03-03
  • 通过两种方式增加从库――不停止mysql服务

    一般在线增加从库有两种方式,一种是通过mysqldump备份主库,恢复到从库,mysqldump是逻辑备份,数据量大时,备份速度会很慢,锁表的时间也会很长。另一种是通过xtrabackup工具备份主库,恢复到从库,xtrabackup是物理备份,备份速度快...2015-11-08
  • Visual Studio 2015下载和安装图文教程

    这篇文章主要为大家详细介绍了Visual Studio 2015下载和安装图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • 关于Visual Studio无法打开源文件"stdio.h"问题

    这篇文章主要介绍了关于Visual Studio无法打开源文件"stdio.h"问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-03
  • WPF实现类似360安全卫士界面的程序源码分享

    最近在网上看到了新版的360安全卫士,感觉界面还不错,于是用WPF制作了一个,时间有限,一些具体的控件没有制作,用图片代替了。感兴趣的朋友一起跟着小编学习WPF实现类似360安全卫士界面的程序源码分享...2020-06-25
  • vs2019安装和使用详细图文教程

    这篇文章主要介绍了vs2019安装和使用详细图文教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • WPF仿三星手机充电界面实现代码

    这篇文章主要为大家详细介绍了WPF仿三星手机充电界面实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • C# WPF 通过委托实现多窗口间的传值的方法

    这篇文章主要介绍了C# WPF 通过委托实现多窗口间的传值的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 解决Visual Studio 2012 Update 4 RC启动调试失败的方案

    这篇文章主要为大家详细介绍了Visual Studio 2012 Update 4 RC启动调试失败的解决方案,感兴趣的小伙伴们可以参考一下...2021-09-22
  • Visual C#类的定义及实现方法实例解析

    这篇文章主要介绍了Visual C#类的定义及实现方法实例解析,对于新手来说有不错的借鉴学习价值,需要的朋友可以参考下...2020-06-25
  • 在 Visual Studio 中查看反汇编代码

    这篇文章主要介绍了在 Visual Studio 中查看反汇编代码的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-03
  • C#中WPF使用多线程调用窗体组件的方法

    这篇文章主要介绍了C#中WPF使用多线程调用窗体组件的方法,涉及C#中多线程的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • WPF TextBox实现按字节长度限制输入功能

    这篇文章主要为大家详细介绍了WPF TextBox实现按字节长度限制输入功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • WPF InkCanvas绘制矩形和椭圆

    这篇文章主要为大家详细介绍了WPF InkCanvas绘制矩形和椭圆,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • Microsoft Visual C++ 6.0开发环境搭建教程

    这篇文章主要为大家详细介绍了Microsoft Visual C++ 6.0开发环境搭建教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • WPF基础教程之形状画刷与变换详解

    这篇文章主要给大家介绍了关于WPF基础教程之形状画刷与变换的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • Visual Studio 2017 (VS 2017)离线安装包制作方法

    这篇文章主要为大家详细介绍了Visual Studio 2017离线安装包的制作方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • WPF如何自定义TabControl控件样式示例详解

    这篇文章主要给大家介绍了关于WPF如何自定义TabControl控件样式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。...2020-06-25