【C#】妈妈再也不用担心自定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚
------------------更新:201411190903------------------
经过思考和实践,发现套路中的第1条是不必要的,就是完全可以不用定义一个名为Default+属性名的字段或属性,只要实现Reset和ShouldSerialize这俩方法就可以了。关于这俩方法,应该是有相关文档的,果然,在MSDN找到说法:http://msdn.microsoft.com/zh-cn/library/53b8022e(v=vs.80).aspx
------------------原文:201411182108------------------
标题有点那啥,但确实能表达我掌握此法后的心情。
写自定义控件时往往会有一个需求,就是给属性指定一个默认值(就是可以在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 >= )) && (Height >= ))
{
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(, , );
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赋了
最后,晒一下成果:
美白前:

美白后:

- 文毕 -
【C#】妈妈再也不用担心自定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚的更多相关文章
- 自定义控件如何给特殊类型的属性添加默认值 z(转)
自定义控件如何给特殊类型的属性添加默认值 z 定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚 标题有点那啥,但确实能表达我掌握此法后的心情. 写自定义控件时往往会有一个需求,就 ...
- 自定义控件如何给特殊类型的属性添加默认值 z
定义控件如何给特殊类型的属性添加默认值了,附自定义GroupBox一枚 标题有点那啥,但确实能表达我掌握此法后的心情. 写自定义控件时往往会有一个需求,就是给属性指定一个默认值(就是可以在VS中右键该 ...
- 妈妈再也不用担心别人问我是否真正用过redis了
1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...
- 有了 tldr,妈妈再也不用担心我记不住命令了
引言 有一次我在培训时说「程序员要善于使用 Terminal 以提高开发效率」,一位程序员反驳道:「这是 21 世纪,我们为什么要用落后的命令行,而不是先进的 GUI?」 是的,在一些人眼里,这个黑黑 ...
- 妈妈再也不用担心我使用git了
妈妈再也不用担心我使用git了 Dec 29, 2014 git git由于其灵活,速度快,离线工作等特点而倍受青睐,下面一步步来总结下git的基本命令和常用操作. 安装msysgit 下载地址:ms ...
- 利用CH341A编程器刷新BIOS,恢复BIOS,妈妈再也不用担心BIOS刷坏了
前几天,修电脑主析就捣鼓刷BIOS,结果刷完黑屏开不了机,立刻意识到完了,BIOS刷错了.就从网上查资料,各种方法试了个遍,什么用处都没有.终于功夫不负有心人,找到了编码器,知道了怎么用.下面看看具体 ...
- python爬虫07 | 有了 BeautifulSoup ,妈妈再也不用担心我的正则表达式了
我们上次做了 你的第一个爬虫,爬取当当网 Top 500 本五星好评书籍 有些朋友觉得 利用正则表达式去提取信息 太特么麻烦了 有没有什么别的方式 更方便过滤我们想要的内容啊 emmmm 你还别说 还 ...
- 锋利的js之妈妈再也不用担心我找错钱了
用js实现收银功能. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <hea ...
- 有了jsRender,妈妈再也不用担心我用jq拼接DOM拼接的一团糟了、页面整齐了、其他伙伴读代码也不那么费劲了
写在前面 说来也很巧, 下午再做一个页面,再普通不过的分页列表,我还是像往常一样,基于MVC环境下,我正常用PagedList.MVC AJAX做无刷新分页,这时候问题就来了,列表数据中有个轮播图用到 ...
随机推荐
- 初入职场的建议--摘自GameRes
又开始一年一度的校招了,最近跑了几个学校演讲,发现很多话用短短的一堂职业规划课讲还远远不够,因为那堂课仅仅可能帮大家多思考怎样找到一份合适的工作,并没有提醒大家怎样在工作中发展自己的职业. 见过这么多 ...
- Atitit jsr规范化分类 attilax总结
Atitit jsr规范化分类 attilax总结 按照技术类别可以分成以下几类:1.Java EE (39 JSRs) --- J2EE平台规范 2.Java SE (42 JSRs) --- J2 ...
- Atitit 数据处理查询 中的异常标准化草案 jpa jdbc hb oql规范attilax总结
Atitit 数据处理查询 中的异常标准化草案 jpa jdbc hb oql规范attilax总结 Javaee6 与net 异常规范1 Jpa规范 JPA全称Java Persistence A ...
- DNS正向解析与反向解析
DNS:(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网, 而不去记住能够被机器直接读取的IP数串.通过主机名,最 ...
- JSON和JSONP的区别
先前的概念中对JSON还是比较熟悉,对JSONP不是特别的清楚,整理完相关知识发现才豁然开朗.简单的说JSON是一种数据交换格式,而JSONP是 一种非官方跨域数据交互协议.JSON是“暗号”,而JS ...
- salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
提到工厂模式,想必大家都很熟悉,工厂模式作为一种设计模式,同样在salesforce中适用. 举一个例子,笔作为基类,可以有钢笔,铅笔,圆珠笔等等.有一个笔的工厂,当你向它要钢笔,它就会生产一支钢笔; ...
- 用scrollTop制作一个自动滚动公告栏
我们在浏览网页时,经常会看到会一些滚动的栏目,比如向上滚动的公告.新闻等.其实他们的制作都不难,只要学了基础的html.css.javascript就可以做出来,用JavaScript的scrollT ...
- python开启简单webserver
python开启简单webserver linux下面使用 python -m SimpleHTTPServer 8000 windows下面使用上面的命令会报错,Python.Exe: No Mod ...
- KlayGE 4.4中渲染的改进(一):只需要SM3的TBDR
转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2736 KlayGE从4.0开始引入deferred rendering层(DR),并且这几个 ...
- Redis基础介绍及安装示例
1.基本概念 Redis是由Salvatore Sanfilippo(意大利)开发的一个开源的高性能键值存储数据库,于2009年发布第一个版本并与同一年开源,官方站点:http://www.redis ...