zoukankan      html  css  js  c++  java
  • C#——写一个控件库

    转自https://blog.csdn.net/qq_33712555/article/details/80940569

    注意:控件的类和部分类(包括窗体生成类/窗体设计类)全部要加public来修饰,不然无法在工具箱里找到

    控件库,说白了,就是个类库项目。不过这个类库内定义了一系列自定义的控件。整个项目(类库+调用项目)结构大概是这个样

    这里写图片描述

    请忽略Class2ClassDiagram1.cd,这是我之前一个博文

    用到的项目,留下来也无关紧要。UserControl2.cs就是我们的一个控件了。长这个样

    这里写图片描述

    一个TextBox下面有一条线,线宽等于文本框的宽等于整个控件的宽。当然,这需要在cs代码中做一些调整(设置属性或是什么)。代码长这个样,仅供参考

    这里写图片描述

    private int width;
    • 1

    定义了一个字段,用来指示控件的宽度,private。然后定义了一个属性,也是用来指示控件宽度,public。这样在类外访问控件宽度需通过属性,而不是直接把字段暴露给外部。

    [Description("控件宽度"), Category("自定义属性")]
    public int selfWidth
    {
        get
        {
            return width;
        }
        set
        {
            this.width = value;
            this.Width = value;
            this.txtBox.Width = value;
            this.pictureBox.Width = value;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    其中

    [Description("控件宽度"), Category("自定义属性")]
    • 1

    加上这行表明这个属性代表了控件宽度,后面的Category是分组情况。这行的意义是用了这个控件之后,就能在当前窗体设计器上面方便地改动属性值,就像这样

    这里写图片描述

    还有最后一段代码

    private void UserControl1_SizeChanged(object sender, EventArgs e)
    {
        this.selfWidth = this.Width;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    尽管这个控件在主调窗体加载完之后大小就固定了,但是仍要增加控件尺寸改变事件。这样做是为了保证控件在窗体设计阶段拖拽能即时地调整自己相关的属性,而不至于外壳改变了,而里面的东西却还是老样子。

    最后在我们需要用到这个控件的项目中调用相关dll就OK了。

    写控件库一点都不难,只需要同样的过程重复N遍再加上一点创意,必要时还需要用点别人的东西。

    摘要: C#中的自定义控件中的属性(Property)、事件(Event)及一些相关特性(Attribute)的总结

    今天学习了下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类中,添加下面的代码:

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

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

    三、添加自定义事件

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

    /// <summary>
    /// 事件
    /// </summary>
    public event EventHandler BtnTestClick;
    /// <summary>
    /// 测试按钮
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    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() + "
    " + 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中看到:

    /// <summary>
    /// 设计器支持所需的方法 - 不要
    /// 使用代码编辑器修改此方法的内容。
    /// </summary>
    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()中的代码会有不同:

    /// <summary>
    /// 设计器支持所需的方法 - 不要
    /// 使用代码编辑器修改此方法的内容。
    /// </summary>
    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

  • 相关阅读:
    [LeetCode]4Sum
    [LeetCode]3Sum
    [LeetCode]Two Sum
    [LeetCode]Maximal Rectangle
    [LeetCode]Largest Rectangle in Histogram
    [LeetCode]Sudoku Solver
    [LeetCode]Group Anagrams
    jQuery验证控件jquery.validate.js使用说明+中文API
    js操作cookie,实现登录密码保存
    Java中getResourceAsStream的用法
  • 原文地址:https://www.cnblogs.com/eve612/p/14306772.html
Copyright © 2011-2022 走看看