【环境属性AmbientValueAttribute】使用ShouldSerialize 和 Reset 方法定义默认值----------VS视图设计器开发

发布时间 2023-07-29 14:04:38作者: 唧唧复唧唧木兰当户织

今天和大家讲解下关于winform控件的 环境属性AmbientValueAttribute 开发,net自带标准控件库里面的Control基类里面有些属性会根据父容器的属性变化而变化的,其中最常用环境属性包括 BackColor、ForeColor、Font、Cursor。在这里不得不吐槽微软把所有技术文档全放在一个网站,用他的搜索找资料就像大海捞针一样。环境属性 这个概念与功能的实现我足足找了两天才找到,还是在外网谷歌搜索电子书籍找到讲解,后来才找到官网对应文档网址。百度引擎全是不沾边的抄袭粘贴结果,真令人气愤。因为最近没啥心情可能写的比较乱,你慢慢理解吧因为这是winform控件开发很重要知识点。

我们用Label 控件来举个例子。“Text” 属性“普通属性”,“BackColor” 环境属性。当你修改窗体的 Text 属性值时窗体里面的 Label 控件的 Text 属性是不会发生变化的。但当你修改窗体 BackColor 属性时,你会发现 Label 控件的 BackColor 属性值会同时改变成和窗体 BackColor 属性值一样的值。

我们在使用net自带标准控件库里面的控件下,大家应该发现控件属性是有默认值的,我们在VS属性面板看到属性值如果是等于属性的默认值,属性面板显示的为本比较小,而且在Designer.cs文件或资源文件找不到对该属性赋值的代码。当我们在属性面板把属性值修改成其他值时会发现属性面的为本会变成粗体,而且能在Designer.cs文件或资源文件找到对该属性赋值的代码。  那VS是如何实现的呢,这里就要提到 “DefaultValueAttribute”默认值特性、“DesignerSerializationVisibility”生成代码类型特性、“ShouldSerialize”是否需要生成代码方法 、“Reset”重置属性值方法

1.当我们为属性添加DefaultValueAttribute 特性时,如果VS发现你在视图设计器属性面板修改属性值,而修改后的值与DefaultValueAttribute 特性设置的默认值不一样时,VS就会在Designer.cs文件或资源文件 添加对应属性值赋值代码。那这个特性什么时候适合使用呢,一般是在编写代码时就已经能确定默认值。因为它的参数只能时常量变量不行因为编译器在编译时就必须知道。

例子:

        private Color activateColor = Color.White;
        [DefaultValueAttribute(typeof(Color), "White")]
        public Color ActivateColor
        {
            get { return this.activateColor; }
            set{this.activateColor = value;}
        }

  

2.当我们确定某个属性只能在运行期间用来存放值,任何情况下不希望VS为它生成对应代码。这时候应该使用DesignerSerializationVisibility特性。

例子:

        private Color tmpColor = Color.White;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Color TmpColor
        {
            get { return this.tmpColor; }
            set { this.tmpColor = value; }
        }

  

 3。以上两个例子都是“普通属性”例子。他们利用默认值特性和代码生成类型特性告诉VS是否需要生成属性代码。下面是个“环境属性”例子。在下面例子中我们定义新控件TestControl,它新增 ActivateColor 的环境属性,这个属性的值会根据它的父容器的BackColor属性值改变而改变。现在我们把TestControl控件 添加到窗体上,我们把窗体的BackColor属性的值改成Red红色,这时候你可以可以发现TestControl控件的ActivateColor的属性值也变成Red红色,但是你查看Designer.cs文件的TestControl实例,他并没有生成一句 ...ActivateColor=Color.Red; 这样的代码。重点在于 ShouldSerializeActivateColor 这个方法。这个方法的存在就告诉了VS  ActivateColor是个环境属性,它发现独立存储值activateColor==Color.Empty意味值是从父容器对应属性获取的,所以没有生成对应属性代码。简单的说环境属性时通过属性get访问器、set访问器、和ShouldSerialize方法配合使用实现的。这种方式能够让我们灵活控制是否需要生成代码的逻辑开发。

3.1 用于判断是否为属性生成代码的方法命名有规范的 :ShouldSerialize+属性名称 就得出ShouldSerializeActivateColor方法,这个方法会被VS通过反射调用在ActivateColor属性身上。
3.2 用属性值重置方法命名有规范的 :Reset+属性名称 就得出ResetActivateColor方法,这个方法会被VS通过反射调用在ActivateColor属性身上。

例子:

        public class TestControl : Control
        {
            public readonly Color ActivateDefaultColor = Color.White;//预留默认值,因为颜色必须要有值。

            private Color activateColor = Color.White;//用于存放与默认值不一样的独立存储值

            /// <summary>
            /// 与BackColor属性关联的ActivateColor环境属性
            /// </summary>
            public Color ActivateColor
            {
                get
                {
                    // 1.设置过独立存储值
                    if (this.activateColor != Color.Empty)
                    {
                        return activateColor;
                    }

                    // 2.父容器控件的对应属性值
                    if (this.Parent != null)
                    {
                        return this.Parent.BackColor;
                    }

                    // 3.如果是ActiveX控件返回指定值(这个要根据自己实际情况,这部分代码省略,可以参考微软控件库Control.cs源码)

                    // 4.如果找到对应ISite,返回指定值(这个要根据自己实际情况,这部分代码省略,可以参考微软控件库Control.cs源码)

                    // 5.返回预留默认值
                    return ActivateDefaultColor;
                }
                set
                {
                    Color default_value = Color.Empty;//先找出属性当前默认值是什么
                    if (this.activateColor != Color.Empty)
                    {
                        default_value = activateColor;
                    }
                    else if (this.Parent != null)
                    {
                        default_value = this.Parent.BackColor;
                    }
                    //   else if (如果是ActiveX控件)
                    // {
                    //  default_value =返回指定值(这个要根据自己实际情况,这部分代码省略,可以参考微软控件库Control.cs源码)
                    //  }

                    // else if (如果找到对应ISite)
                    // {
                    //  default_value =返回指定值(这个要根据自己实际情况,这部分代码省略,可以参考微软控件库Control.cs源码)
                    //  }
                    else
                    {
                        default_value = ActivateDefaultColor;
                    }


                    if (value == default_value)
                        this.activateColor = Color.Empty;
                    else
                        this.activateColor = value;

                }
            }

            /// <summary>
            /// 如果activateColor不等于Color.Empty,这代表用户修改过属性值
            /// </summary>
            /// <returns></returns>
            private bool ShouldSerializeActivateColor()
            {
                return activateColor != Color.Empty;
            }

            /// <summary>
            /// VS属性面板属性右键菜单重置选项要把属性设置成该值
            /// </summary>
            private void ResetActivateColor()
            {
                ActivateColor = Color.Empty;
            }

        }