定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚

标题有点那啥,但确实能表达我掌握此法后的心情。

写自定义控件时往往会有一个需求,就是给属性指定一个默认值(就是可以在VS中右键该属性→重置),如果该属性的类型是内置值类型还好,直接使用DefaultValue特性就好,例如:

[DefaultValue(false)]
public bool CanSelect
{
get;
set;
}

对于能够根据字符串常量转换得到的类型也还好,可以这样:

[DefaultValue(typeof(Font), "宋体, 9pt")]
public Font TitleFont
{
get;
set;
}

但这种情况下,DefaultValue的第2个参数必须是字符串常量,不能是变量、字段、属性、方法返回值啥的。题外,一个类型能否从字符串转换得到,依赖的是该类型的TypeConverter特性指定的转换类中的实现。有关TypeConverter的更多信息请参看:

http://msdn.microsoft.com/zh-cn/library/system.componentmodel.typeconverter(v=vs.80).aspx

回到正题,那么问题来了,如果我想让TitleFont的默认值是SystemFonts.DefaultFont咋办?几经磨难,总算让我学到一招,下面通过一个自定义控件示例说明:

/// <summary>
/// 增强型GroupBox
/// </summary>
/// <remarks>
/// Author:AhDung
/// Update:201411181832,可独立设置标题颜色和字体
/// </remarks>
public class GroupBoxEx : GroupBox
{
static Font defaultTitleFont; //定义一个静态字段
/// <summary>
/// 默认标题字体
/// </summary>
public static Font DefaultTitleFont //封装该静态字段,其实不封装直接使用字段也行,但字段命名必须是DefaultXXX
{
get { return defaultTitleFont ?? (defaultTitleFont = SystemFonts.DefaultFont); }
} Color titleColor;
/// <summary>
/// 获取或设置标题颜色
/// </summary>
[Description("获取或设置标题颜色")]
[DefaultValue(typeof(Color), "0, 70, 213")]
public Color TitleColor
{
get { return titleColor; }
set
{
if (titleColor != value)
{
titleColor = value;
this.Invalidate();
}
}
} Font titleFont;
/// <summary>
/// 获取或设置标题字体
/// </summary>
[Description("获取或设置标题字体")]
public Font TitleFont
{
get { return titleFont; }
set
{
titleFont = value ?? DefaultTitleFont; //防止属性被设为null
this.Invalidate();
}
} /// <summary>
/// 重置标题字体
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void ResetTitleFont() //实现一个重置属性默认值的方法,命名须为ResetXXX
{
this.TitleFont = null; //属性setter中有null处理
} /// <summary>
/// 是否显式设置标题字体
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual bool ShouldSerializeTitleFont() //实现一个指示是否把属性值写入窗体Designer文件的方法,命名须是ShouldSerializeXXX
{
return !titleFont.Equals(DefaultTitleFont);
} /// <summary>
/// 重绘
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
if ((Application.RenderWithVisualStyles && (Width >= 10)) && (Height >= 10))
{
TextFormatFlags flags = TextFormatFlags.PreserveGraphicsTranslateTransform | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak;
if (!this.ShowKeyboardCues)
{
flags |= TextFormatFlags.HidePrefix;
}
if (this.RightToLeft == RightToLeft.Yes)
{
flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
} GroupBoxRenderer.DrawGroupBox(
e.Graphics,
this.ClientRectangle,
this.Text,
this.TitleFont,
this.Enabled ? this.TitleColor : SystemColors.ControlDark,
flags,
this.Enabled ? System.Windows.Forms.VisualStyles.GroupBoxState.Normal : System.Windows.Forms.VisualStyles.GroupBoxState.Disabled);
}
else
{
base.OnPaint(e);
}
} /// <summary>
/// 初始化该控件
/// </summary>
public GroupBoxEx()
{
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint
| ControlStyles.OptimizedDoubleBuffer, true); titleColor = Color.FromArgb(0, 70, 213);
ResetTitleFont(); //直接调用重置方法以初始化属性值
}
}

说明一下,写这个控件的本意是让GroupBox在NT6下,标题变得显眼一点。 NT5下默认就是显眼的蓝色,但NT6是黑色,不那么显眼,影响程序体验。固然可以直接设置GroupBox的ForeColor和Font属性达到目 的,但这样的话,它里面的子控件会继承,还得把子控件的这俩属性改回来~蛋疼。所以为了能独立设置GroupBox的标题的颜色和字体,增加了 TitleColor和TitleFont这俩自定义属性,也正是想把TitleFont的默认值设为SystemFonts.DefaultFont时 遇到了本文的问题,几经搜索,看了些有用的帖子,后来又从Control类的源码中得到正果(上述例子参考的就是Control类中的标准做法),那么既 然解决了,就想着把招法和控件一起与大家分享一下。控件实现没什么好说的,下面主要就为非常规类型的属性指定默认值的招法说一下。

就用上述控件中类型为Font、名为TitleFont的属性来说事:

- 要有一个同类型的字段或属性,命名必须为Default+属性名,即DefaultTitleFont,并且为static。为该字段/属性赋值想要的默认值,本例为SystemFonts.DefaultFont,可见这里就不像DefaultValue只能赋值内置值类型或字符串常量那么蛋疼了,可以随意赋值~不然还说个球

- 要实现一个Reset+属性名的 无参无返回方法,即ResetTitleFont()。该方法的作用是重新把属性赋值为默认值。本例因为在属性的setter中有处理,即赋值为null 时就替换为默认值,所以直接赋值null无碍,如果setter没有这种处理,就需要赋值为上面的DefaultTitleFont~切记。至于修饰符无 所谓,Control中是public virtual,考虑到这个方法没必要让外部调用,所以本例是protected virtual。至于加上[EditorBrowsable(EditorBrowsableState.Never)]特性是为了让用户在使用控件时, 避免在VS智能提示中出现该方法,这也是Control中的做法。原因很显然,这种方法是给设计器用的,不是给人用的,显它做甚~碍眼

- 再实现一个ShouldSerialize+属性名的方法,无参,但要返回bool。 即ShouldSerializeTitleFont(),个方法从字眼上是跟序列化有关的,我没测试序列化,不知道是否有关,但可以肯定与是否把默认值 写入窗体的Designer文件有关,就是VS为窗体自动生成的那个含有InitializeComponent()方法的文件,不止如此,没有这方法你 根本玩不转属性重置,缺它不可。方法的逻辑是,如果为属性的赋的值就是默认值,那么就告诉VS不要在InitializeComponent中显式为该属 性赋值了。需要注意的是,返回true代表要显式赋值,所以在写该方法的return时请注意逻辑。修饰符什么的与Reset方法一样,没要求

- 最后是在构造函数中 为属性赋初始值,由于Reset方法就是干这个的,所以本例直接调用这方法。这不是Control的做法,Control的构造函数中没见到调用 Reset方法,但有很多处理,包括调用一些internal方法,懒得追踪了,也没试过不赋初始值会不会有问题,保险起见,还是赋了一下。这里再扯点题 外,就是通过DefaultValue指定的默认值其实只是在VS中右键→重置时,让VS不再往InitializeComponent显式赋值,同时在 PropertyGrid中让值不再粗体显式,并不代表属性的初始值已经设置为DefaultValue指定的值, 什么意思,比如本例,虽然为TitleColor指定了DefaultValue,但如果不在构造函数中初始化titleColor = Color.FromArgb(0, 70, 213)的话,TitleColor值就会是default(Color),即Color.Empty,所以在用DefaultValue后别忘了还得赋 初始值,要记住DefaultValue是不负责赋值的。但是对于用Reset这种方法会不会一样,没试验过,我猜也是不会自动赋初始值的,毕竟初始化是 构造函数的工作,VS再强大再智能,也不太可能自作主张见到Reset就自动往构造函数中插一条~不合适也不科学。所以保险起见,构造函数中我还是对 TitleFont赋了

最后,晒一下成果:

美白前:

美白后:

自定义控件如何给特殊类型的属性添加默认值 z的更多相关文章

  1. 自定义控件如何给特殊类型的属性添加默认值 z(转)

    自定义控件如何给特殊类型的属性添加默认值 z 定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚 标题有点那啥,但确实能表达我掌握此法后的心情. 写自定义控件时往往会有一个需求,就 ...

  2. 【C#】妈妈再也不用担心自定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚

    ------------------更新:201411190903------------------ 经过思考和实践,发现套路中的第1条是不必要的,就是完全可以不用定义一个名为Default+属性名 ...

  3. 如何为一个类型为Color的属性设置默认值

    最近在研究GDI+的时候,用winform来写自定义控件遇到需要为控件的属性设置默认值,但这个属性的类型是System.Drawing.Color.本文只是总结一下各种设置的方法. Example [ ...

  4. Newtonsoft.Json高级用法 1.忽略某些属性 2.默认值的处理 3.空值的处理 4.支持非公共成员 5.日期处理 6.自定义序列化的字段名称

    手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数 ...

  5. ADF为EO的ITEM添加默认值

    Literal:设置为缺省的静态值.Expression:使用 Groovy 表达式设置缺省值.下面是一个表达式,用于将数据库序列(EMPLOYEES_SEQ)作为主键的缺省值:(new oracle ...

  6. swift学习笔记(六)析关闭过程和使用分配给属性的默认值

    一.通过关闭和功能的默认实现财产值 当存储属性默认值需要定制,能为客户提供通过关闭或全局函数的自定义默认值. 注意:全局函数的结构,和枚举使用keywordstatic大喊    用classkeyw ...

  7. JS中给函数参数添加默认值

    最近在Codewars上面看到一道很好的题目,要求用JS写一个函数defaultArguments,用来给指定的函数的某些参数添加默认值.举例来说就是: // foo函数有一个参数,名为x var f ...

  8. [小问题笔记(八)] 常用SQL(读字段名,改字段名,打印影响行数,添加默认值,查找存储过程等)

    读取所有字段,自然排序 declare @fields varchar(max) Select @fields=ISNULL(@fields,'')++name+',' from syscolumns ...

  9. JS中给函数参数添加默认值(多看课程)

    JS中给函数参数添加默认值(多看课程) 一.总结 一句话总结:咋函数里面是可以很方便的获取调用函数的参数的,做个判断就好,应该有简便方法,看课程. 二.JS中给函数参数添加默认值 最近在Codewar ...

随机推荐

  1. onCreateOptionsMenu 和 onPrepareOptionsMenu 的区别

    onCreateOptionsMenu 第一次创建时调用 onPrepareOptionsMenu 运行时每次点击菜单按钮时都会调用这个函数,可以动态改变菜单项 public boolean onPr ...

  2. 293. Flip Game

    题目: You are playing the following Flip Game with your friend: Given a string that contains only thes ...

  3. 277. Find the Celebrity

    题目: Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exi ...

  4. Centos 用户组管理

    #组帐号管理 linux 组管理 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--= 1.组的分类 私用组:只能包含一个用 ...

  5. Spring 异常 —— cvc-elt.1: Cannot find the declaration of element 'beans'

    有个使用 Spring 的项目,运行时报错: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 5 ...

  6. Create a unit test project

    https://msdn.microsoft.com/en-us/library/hh598957.aspx Unit tests often mirror the structure of the ...

  7. leetcode:Excel Sheet Column Number

    Given a column title as appear in an Excel sheet, return its corresponding column number. For exampl ...

  8. JSON 之 SuperObject(4): 增、删、改

    unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, For ...

  9. android异步任务详解 AsynTask

    android提供了一套专门用于异步处理的类.即:AynsTask类.使用这个类可以为耗时程序开辟一个新线程进行处理,处理完时返回. 其实,AsynTask类就是对Thread类的一个封装,并且加入了 ...

  10. PopupWindwo和AlertDialog的区别

      AlertDialog 是非阻塞式对话框:AlertDialog弹出时,后台还可以做事情:而PopupWindow是阻塞式对话框:PopupWindow弹出时, 程序会等 待,在PopupWind ...