定义控件如何给特殊类型的属性添加默认值了,附自定义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. x11vnc

    http://cisight.com/how-to-setup-vnc-server-remote-desktop-in-ubuntu-11-10-oneiric/ Install VNC serve ...

  2. WaitForSingleObject与WaitForMultipleObjects用法详解(好用,而且进入一个非常高效沉睡状态,只占用极少的CPU时间片)

    在多线程下面,有时候会希望等待某一线程完成了再继续做其他事情,要实现这个目的,可以使用Windows API函数WaitForSingleObject,或者WaitForMultipleObjects ...

  3. Struts2笔记——利用token防止表单重复提交

    在一些项目中经常会让用户提交表单,当用户点击按钮提交后,如果再次浏览器刷新,这就会造成表单重复提交,若是提交的内容上传至服务器并请求数据库保存,重复提交的表单可能会导致错误,然后跳转到错误界面,这是一 ...

  4. java string,需要进行首字母大写改写

    java string,需要进行首字母大写改写,网上大家的思路基本一致,就是将首字母截取,转化成大写然后再串上后面的,类似如下代码 //首字母大写     public static String c ...

  5. dojo 资源库

    文档 :http://wenku.baidu.com/link?url=Nnek_tAjIC-Q3t3e9zHQmsh4LhU3f0ncC1QH8WD_U9-I8-fJ7mMisscFpgfuS8nU ...

  6. Java WEB安全问题及解决方案

    1.弱口令漏洞   解决方案:最好使用至少6位的数字.字母及特殊字符组合作为密码.数据库不要存储明文密码,应存储MD5加密后的密文,由于目前普通的MD5加密已经可以被破解,最好可以多重MD5加密.   ...

  7. R之pryr

    1. Pryr安装 由于项目pryr,还没有发布到CRAN,仅支持从github安装.要使用devtools包来通过github来安装,在https://github.com/hadley/pryr中 ...

  8. make clean vs make clobber

    make is pretty smart, and picks up what has changed from the last build, so if you run repo sync and ...

  9. SPOJ 1487 Query on a tree III(划分树)

    题目链接:http://www.spoj.com/problems/PT07J/ 题意:给出一个有根树,1为根节点,每个节点有权值.若干询问,询问以u为根的子树中权值第K小的节点编号. 思路:DFS一 ...

  10. PHP文件上传代码和逻辑详解

    文件上传的逐步完善------ [简单的上传:]   <form action="upload.php"  method="post"  enctype= ...