热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#中的自定义控件中的属性、事件及一些相关特性的总结

今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实验了一些用于开发自定义属性和事件的特性(Attribute)。在这里先说一下我的环境:操作系统:Win

今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实验了一些用于开发自定义属性和事件的特性(Attribute)。

在这里先说一下我的环境:

操作系统:Windows7旗舰版(Service Pack 1)

VS版本:Microsoft Visual Studio Ultimate 2012,版本 11.0.50727.1 RTMREL

.NET Framework版本:4.5.50938

C#版本:Visual C# 2012

一、准备工作

1、建立一个C#窗体应用程序,主窗体起名为FormMain,向解决方案中再加入一个用户控件,起名为TestUserControl

技术分享

2、在TestUserControl中放一个按钮,取名为btnTest

技术分享

3、控件做好后,会出现在【工具箱】内

技术分享

4、将控件拖拽到一个窗体(Form)上就可以使用了,取名testUserControl1。这个名字是VS默认取的,即首字母小写,最后补上数字作为序号。

技术分享

二、添加自定义属性

在TestUserControl类中,添加下面的代码:

/// 
/// 按钮名称
/// 
public string ButtonName
{
    get
    {
        //TODO
        return btnTest.Text;
    }
    set
    {
        //TODO
        btnTest.Text = value;
    }
}

代码添加完毕后,在FormMain上加入的testUserControl1的属性中,就会出现BtnName了

技术分享

三、添加自定义事件

在TestUserControl类中,添加下面的代码:

/// 
/// 事件
/// 
public event EventHandler BtnTestClick;
/// 
/// 测试按钮
/// 
/// 
/// 
private void btnTest_Click(object sender, EventArgs e)
{
    if (BtnTestClick != null)
    {
        //TODO
        BtnTestClick(sender, e);
    }
}

代码添加完毕后,在FormMain上加入的testUserControl1的事件中,就会出现BtnTestClick了

技术分享

在FormMain的代码中实现这个函数

private void testUserControl1_BtnTestClick(object sender, EventArgs e)
{
    MessageBox.Show(sender.ToString() + "\r\n" + e.ToString());
}

这时运行程序,点击控件testUserControl1内的按钮btnTest,就会有下面的效果:

技术分享

四、几个特性(Attribute)

1)DefaultEvent和DefaultProperty:指定自定义控件的默认事件和默认属性

DefaultEventAttribute(MSDN)可以用来指定组件的默认事件,如在TestUserControl类上面加入代码

[DefaultEvent("BtnTestClick")]

那在Form编辑界面,双击控件testUserControl1就会自动进入testUserControl1_BtnTestClick事件。

这里再说明一下,C#中的System.Windows.Forms.Control类代码如下:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
[DefaultEvent("Click")]
[DefaultProperty("Text")]
[Designer("System.Windows.Forms.Design.ControlDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[ToolboxItemFilter("System.Windows.Forms")]
public class Control : Component, IDropTarget, ISynchronizeInvoke, IWin32Window, IBindableComponent, IComponent, IDisposable
{ /* ... */ }

这里可以看到DefaultEvent的值为“Click”,这也就是为什么拖入Form的按钮(Button),在双击后会进入它的Click事件:

private void button1_Click(object sender, EventArgs e)

对于不希望以Click事件作为默认事件的控件来说,要手动指定该控件的DefaultEvent特性,如复选框(CheckBox)的声明:

[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
[DefaultBindingProperty("CheckState")]
[DefaultEvent("CheckedChanged")]
[DefaultProperty("Checked")]
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public class CheckBox : ButtonBase
{ /* ... */ }

这里的DefaultEvent被写上了“CheckedChange”,因此在Form的编辑界面,双击复选框时默认进入的编辑事件为

private void checkBox1_CheckedChanged(object sender, EventArgs e)

自定义的控件(直接继承自UserControl),如果不添加这个属性,在编辑界面双击后进入的事件是Load事件。

类似的特性还有DefaultProperty,DefaultPropertyAttribute(MSDN)可以被用来指定组件的默认属性。指定默认属性后,当用户在Form里单击这个控件时,将在属性浏览窗口中自动选定该属性:

[DefaultProperty("BtnName")]

2)Browsable:设置控件某一属性或事件是否出现在“属性”窗口中

BrowsableAttribute(MSDN)指定某一属性或事件是否应在“属性”窗口中显示,如在属性BtnName上添加代码:

[Browsable(false)]

则控件testUserControl1的属性界面就不会出现BtnName的设置了,下图红线部分为之前BtnName所在的位置

技术分享

如果某属性或事件没有添加Browsable特性,那么该属性或事件也可以在“属性”窗口中看到。这里还要说明以下,Browsable只能决定某属性或事件在“属性”窗口内的可见性,Browsable被置为false的属性和事件,仍可以在编辑器中通过代码中使用。

3)Description:指定控件某一属性或事件出现在“属性”窗口中的说明文字

DescriptionAttribute(MSDN)用于指定控件的某一属性或事件出现在“属性”窗口中的说明文字

如在BtnName上添加下面代码:

[Description("设置按钮上显示的文字")]

也可以带上Browsable特性一起使用:

[Browsable(true)]
[Description("设置按钮上显示的文字")]

或写在一对方括号里,用逗号隔开:

[Browsable(true), Description("设置按钮上显示的文字")]

在“属性”界面中看到的说明文字,效果如下:

技术分享

4)EditorBrowsable:指定某一属性或方法在编辑器中可见

EditorBrowsableAttribute(MSDN)指定某个属性或方法在编辑器中可以查看。

EditorBrowsableAttribute的构造函数如下:

public EditorBrowsableAttribute(EditorBrowsableState state);

其中,EditorBrowsableState是一个枚举(enum),这个枚举共有三个值,分别是Always、Never和Advanced

Always:该属性或方法在编辑器中始终是可浏览的

Never:该属性或方法始终不能在编辑器中浏览

Advanced:该属性或方法是只有高级用户才可以看到的功能。 编辑器可以显示或隐藏这些属性

前面两个都好理解,第三个Advanced着实会让人一头雾水(什么才叫“高级用户”?)。后来查了一些资料,才知道对于高级成员的可见性,可以在“工具”菜单下的“选项”中进行配置。

技术分享

(在这里感谢大神在social.msdn.microsoft.com上的 解答 )

如果勾选了“隐藏高级成员”,那么用代码“[EditorBrowsable(EditorBrowsableState.Advanced)]”标记的属性,将不能在IDE中自动显示。但这也仅仅是不自动显示而已,如果在代码中真的调用了不可见的属性,编译不会报错,运行也不会有问题。

如下图:BtnName被标记为“EditorBrowsableState.Never”,因此这个属性不会出现在VS的智能提示(学名叫IntelliSense)中,但如果写到代码里,却没有问题。

技术分享

需要注意的是,这种隐藏只有在该控件代码为当前解决方案不可见时有效,也就是说,如果这个控件的实现代码就在你的解决方案内,EditorBrowsable并不能保证用户看不见这个属性。但如果这个控件时被放在一个dll文件中添加引用到当前的解决方案中,EditorBrowsable特性才能按其文字描述中说明的那样起作用。

5)DesignerSerializationVisibility:代码生成器生成组件相关代码的方式

DesignerSerializationVisibilityAttribute(MSDN)用于指定在设计时序列化组件上的属性时所使用的持久性类型。

参数为DesignerSerializationVisibility类型的枚举:

Hidden:代码生成器不生成对象的代码

Visible:代码生成器生成对象的代码

Content:代码生成器产生对象内容的代码,而不是对象本身的代码

这个说法一眼看上去并不易理解,因此我决定还是用两个具体例子说明一下:

1、Hidden与Visible、Content的不同

还是以我们上面的BtnName属性为例,参数为【DesignerSerializationVisibility.Hidden】的情况

[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public string BtnName
{
    get
    {
        return btnTest.Text;
    }
    set
    {
        btnTest.Text = value;
    }
}

将控件拖入FormMain的窗体设计器中,可用在文件FormMain.Designer.cs中看到:

/// 
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// 
private void InitializeComponent()
{
    this.testUserControl1 = new ControlTest.TestUserControl();
    this.SuspendLayout();
    // 
    // testUserControl1
    // 
    this.testUserControl1.Location = new System.Drawing.Point(33, 46);
    this.testUserControl1.Name = "testUserControl1";
    this.testUserControl1.Size = new System.Drawing.Size(134, 77);
    this.testUserControl1.TabIndex = 0;
    // ...
}

将BtnName上方的特性DesignerSerializationVisibilityAttribute的参数改为【DesignerSerializationVisibility.Visible】或【DesignerSerializationVisibility.Content】后,函数InitializeComponent()中的代码会有不同:

/// 
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// 
private void InitializeComponent()
{
    this.testUserControl1 = new ControlTest.TestUserControl();
    this.SuspendLayout();
    // 
    // testUserControl1
    // 
    this.testUserControl1.BtnName = "button1";
    this.testUserControl1.Location = new System.Drawing.Point(36, 32);
    this.testUserControl1.Name = "testUserControl1";
    this.testUserControl1.Size = new System.Drawing.Size(134, 77);
    this.testUserControl1.TabIndex = 0;
    // ...
}

可用看出,区别就在下面这行代码:

this.testUserControl1.BtnName = "button1";

使用了Hidden就没有,使用了Visible就会有(使用了Content也会有)

使用了Hidden后,在“属性”界面中,无论怎么修改BtnName属性的值,编译时编译器都不会理睬这个值,而是使用默认值(这个例子里面就是button1)。使用了Hidden后,即使在FormMain.Designer.cs里手动把上面那行赋值的代码加上,这行代码在程序重新编译后还是会消失。

2、Visible与Content的不同

Content被用在可以序列化的集合,例如System.Windows.Forms.DataGridView类(数据表格)

//
// 摘要:
//     获取一个包含控件中所有列的集合。
//
// 返回结果:
//     一个 System.Windows.Forms.DataGridViewColumnCollection,包含 System.Windows.Forms.DataGridView
//     控件中的所有列。
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.DataGridViewColumnCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[MergableProperty(false)]
public DataGridViewColumnCollection Columns { get; }

IDE只是生成这些属性中包含组件的代码,而不会生成属性本身的代码。在使用IDE添加各个DataGridViewTextBoxColumn时,各个DataGridViewTextBoxColumn的代码会被放在FormMain.Designer.cs文件中,而有关Columns属性本身只会在在函数InitializeComponent()中生成这样一段代码:

this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2,
this.Column3});

6)其他特性

其他的特性还有许多(如Localizable被用于指定属性是否可本地化、DefaultValue用于为属性指定另一个“默认值”等),如只是初步了解可以去查看VS从程序集 System.Windows.Forms.dll中反射出的各控件、控件属性、控件事件的声明和摘要(就是声明上面的绿字),更详细的描述可以去参考MSDN。

END

C#中的自定义控件中的属性、事件及一些相关特性的总结


推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何使用 NSTimer 实现倒计时功能,详细讲解了初始化方法、参数配置以及具体实现步骤。通过示例代码展示如何创建和管理定时器,确保在指定时间间隔内执行特定任务。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • 高效解决应用崩溃问题!友盟新版错误分析工具全面升级
    友盟推出的最新版错误分析工具,专为移动开发者设计,提供强大的Crash收集与分析功能。该工具能够实时监控App运行状态,快速发现并修复错误,显著提升应用的稳定性和用户体验。 ... [详细]
  • 2023 ARM嵌入式系统全国技术巡讲旨在分享ARM公司在半导体知识产权(IP)领域的最新进展。作为全球领先的IP提供商,ARM在嵌入式处理器市场占据主导地位,其产品广泛应用于90%以上的嵌入式设备中。此次巡讲将邀请来自ARM、飞思卡尔以及华清远见教育集团的行业专家,共同探讨当前嵌入式系统的前沿技术和应用。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文详细介绍了如何通过命令行启动MySQL服务,包括打开命令提示符窗口、进入MySQL的bin目录、输入正确的连接命令以及注意事项。文中还提供了更多相关命令的资源链接。 ... [详细]
  • MATLAB实现n条线段交点计算
    本文介绍了一种通过逐对比较线段来求解交点的简单算法。此外,还提到了一种基于排序的方法,但该方法较为复杂,尚未完全理解。文中详细描述了如何根据线段端点求交点,并判断交点是否在线段上。 ... [详细]
author-avatar
刘旭
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有