zoukankan      html  css  js  c++  java
  • 关于从开始到工具箱创建自定义Windows窗体控件的教程

    内容 介绍 创建 一个

      

    新的解决方案 继承 从现有的控制 添加 属性和访问器 压倒一切的 继承的方法 添加 属性描述,文档支持 添加 工具箱支持 添加 一个简单的设计器类 组装 属性,签署 使用 控制 总结 介绍 在本教程中,我们将走过创建a的过程 自定义Windows窗体控件从项目开始一直到 包含在Visual Studio工具箱中。控件将是一个简单的分隔面板- an 具有可选择边框外观和边框的继承面板控件 两侧。在完成本教程后,读者应该有一个基本的 基于类继承,创建自定义属性,重写base 方法,使用属性注释,创建工具箱图标,创建 简单的设计器类,并将自定义控件集成到Visual Studio中 工具箱。在此过程中,我们将讨论一些最佳实践并详细说明其中一些 Visual Studio中帮助简化控件开发的快捷方式。 创建一个新的解决方案 在Visual Studio中创建用于控件开发的新项目时,通常需要 最好从一个新的空白解决方案开始,而不是跳跃 直接使用项目向导导入新的控件库项目。在做 因此,您可以在一个解决方案内创建多个项目- 这允许您的测试应用程序和控制库保持独立 项目,还添加了轻松共享链接类和 包括全局解决方案项。 要创建一个新的空白解决方案,选择File > 新比;空白的解决方案。在新的解决方案中 对话框中,输入解决方案名称作为Windows窗体分隔符面板 并单击OK。 创建新解决方案后,右键单击 解决方案标题,选择Add >新 项目。当Add New Project对话框打开时,选择 窗口控件库选项,并输入分隔面板 作为项目名称。 向导将创建带有两个文件的控件库 默认:UserControl1.cs和AssemblyInfo.cs。对于本教程,我们将删除 并在一个新的空类中创建我们的控件。 突出显示UserControl1.cs,然后右键单击和 选择Delete将其从项目中删除。 接下来,右键单击解决方案中的分隔面板项目 资源管理器,然后选择添加>添加类 上下文菜单。在Add类对话框中,输入 作为类名,并单击 好的。 从现有的继承 控制 继承是实现面向对象的主要因素之一 编程如此强大。当我们从一个已有的类继承时,我们会自动地 获得基类的所有功能并获得扩展的能力 在此基础上创建一个更专门化的类。所有的Windows窗体控件在一些 point必须从System.Windows.Forms继承。控件,因为它封装了所有 框架需要宿主类的基本属性和方法 控件在窗体上的。 幸运的是,继承现有控件是一件轻而易举的事情 决定了具有您希望扩展的基本功能的控件 只需要在类声明行中添加一个即可创建类 继承: 隐藏,复制Code

    public class DividerPanel : System.Windows.Forms.Panel
    {
    }

    对于我们的分隔面板控件,我们已经指定了我们正在继承我们的 来自标准System.Windows.Forms的基本功能。面板控制。在 这样,我们的新控件现在就拥有了属性和方法的所有属性和方法 我们现在可以添加自己的自定义属性到 ,并覆盖一些面板控件方法,以便 实现我们的定制。 添加属性和 访问器 为了实现我们的divide erpanel,我们将添加两个新的 属性:BorderSide和Border3DStyle。为 边界财产 对System.Windows.Forms.Border3DSide中的值使用引用 枚举,对于Border3DStyle属性,我们将使用 对System.Windows.Forms中值的引用。Border3DStyle枚举。 虽然有几种不同的方式可以公开这些属性,但有几种 只有一个好方法——创建一个私有变量,供我们的方法使用 控件和用于公开的补充公共访问器 属性设置为使用该控件的其他类,如: 隐藏,收缩,复制Code

    // This system of private properties with public accessors is a
    // best practice coding style.
    // Note that our private properties are in camelCasing -
    // the first letter is lower case, and additional word 
    // boundaries are capitalized.
    
    private System.Windows.Forms.Border3DSide borderSide;
    private System.Windows.Forms.Border3DStyle border3DStyle;
    
    // Next we have our public accessors, Note that for public accessors
    // we use PascalCasing - the first letter is capitalized and additional
    // word boundaries are also capitalized.
    
    public System.Windows.Forms.Border3DSide BorderSide
    {
        get { return this.borderSide; }
        set
        {
            if( this.borderSide != value )
            {
                this.borderSide = value;
                this.Invalidate();
            }
        }
    }
    
    public System.Windows.Forms.Border3DStyle Border3DStyle
    {
        get { return this.border3DStyle; }
        set
        {
            if( this.border3DStyle != value )
            {
                this.border3DStyle = value;
                this.Invalidate();
            }
        }
    }

    在上面的代码中,我们首先定义了两个私有属性:borderSide和 这些是我们将在类中使用的变量。因为这些 都是用私有属性定义的,它们不能被任何代码访问 在我们的控制类之外。您还会注意到,我已经指定了full 属性对象的路径- 隐藏,复制Code

    // good
    private System.Windows.Forms.Border3DSide borderSide;

    因为我已经包含了一个using System.Windows。表单指令为我的类I 可以直接声明属性a吗史: 隐藏,复制Code

    // bad
    private Border3DSide borderSide;

    但是,包含缓解可能的完整路径始终是一个好的实践 命名朦胧。 接下来,我们有两个公共属性,都使用get和 设置访问器。在这两种情况下,get访问器都返回 值,而设置访问器首先检查该值是否为 被设置不同于current,只有这样它才会更新我们的私有属性 并调用Invalidate()方法强制控件 重画。 我们可以像样例中那样简单地创建两个只公有的属性 但是下面我们不能做任何处理,比如 在set上调用Invalidate(),这样就没有办法了 初始化以知道值何时被更改。 隐藏,复制Code

    // This is bad coding, never expose public properties like this!
    // Always use private variables with complementary public accessors
    
    public System.Windows.Forms.Border3DSide BorderSide;
    public System.Windows.Forms.Border3DStyle Border3DStyle;

    即使您不打算在公共访问器中执行任何处理,您也可以 仍然应该始终坚持高质量的编码约定,例如 这一点。下一点是我选择的命名约定——很多人 仍然使用其他语言的命名约定,例如 m_BorderSide而不是我选择的borderSide。我的 个人选择是基于三个不同的来源:首先是名字 Visual Studio将自动分配控件,从这些控件中拖放 工具箱,第二个是框架本身使用的事实 它的大多数命名约定 私有财产(快速浏览任何 带有反编译器或反射器的框架类将确认这一事实)。 如果微软认为这是。net的最佳命名惯例 框架本身,很自然地假设它是编写代码时的最佳实践 . net框架。 不使用的第三个原因 表示成员变量的前缀如m_是不能使用的 充分利用Visual Studio的智能感知 如果你这样做,特性。这就引出了访问器中另一点值得注意的地方 代码,我的用法 这个前缀-技术上没有必要也没有优势 使用上述访问器代码中的this指令。然而,通过 使用这个指令,你可以利用Intellisense的auto 文字补全系统填写您的变量名,节省编码 节省时间和打字错误。 您定义的任何变量都应该在前面显式地指定一个值 他们正在使用。始终在类的构造函数中设置初始值 方法调用构造函数fom。没有赋初值的变量 有时会产生一些问题,需要几个小时才能发现。为 我们的divide erpanel类的构造函数是这样的: 隐藏,复制Code

    // This is the Constructor for our class. Any private variables 
    // we have defined should have their initial values set here. It 
    // is good practice to always initialize every variable to a specific 
    // value, rather then leaving it as an inferred value. For example, 
    // all bool's should be set to false rather than leaving them as an 
    // inferred false, and any objects referenced that are initially null 
    // should be explicitly set to null. (eg. myObject = null)
    
    public DividerPanel()
    {
        // Set default values for our control's properties
        this.borderSide = System.Windows.Forms.Border3DSide.All;
        this.border3DStyle = System.Windows.Forms.Border3DStyle.Etched;
    }

    构造函数是任何类中被调用的第一个方法 实例化。您通常会执行任何必要的初始化工作 这里,确保在任何其他方法之前,一切都准备就绪 由使用类的代码调用。构造函数的名称总是与其名称相同 父类,而要用作控件的每个类都必须具有 如果要与Visual Studio设计器一起使用,则使用无参数公共构造函数, 或者对COM客户端可见。 覆盖继承的方法 当您覆盖基类中的方法时,CLR将运行您的代码 而不是通常包含在基类对应方法中的代码。 这允许您轻松地更改行为并扩展功能 框架中的大多数基本控件。为了给我们的 我们将重写。中的OnPaint方法 基础面板控件。一旦我们覆盖了基类的方法,我们就可以 仍然使用base关键字调用base功能。 当然,每个方法都有自己独特的参数 我们需要确切地知道这些参数是什么。 幸运的是Visual Studio 使它为我们快速添加覆盖时 创建自定义控件。在Visual Studio窗口的右侧有 控件的底部显示的解决方案资源管理器 解决方案资源管理器面板,单击标签为“类视图”的选项卡。 接下来展开divide panel类,您将看到与 如上图所示。 类视图对于理解您的继承路径也很方便 控制使用。在本例中,我们可以看到继承路径为:divide erpanel & lt;面板& lt;ScrollableControl & lt;控制& lt; 组件& lt;MarshallByRefObject & lt;对象。 对于我们的分隔面板控件,我们需要覆盖 方法,你会看到它一直继承下来 控件类中的树。找到OnPaint方法, 右键单击并选择Add > 覆盖。 然后Visual Studio将插入一个覆盖的隐藏版本。将Code

    OnPaint 

    method复制到我们的类中,然后我们可以添加将给出我们的 控制它的扩展功能 隐藏,复制Code

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        // allow normal control painting to occur first
        base.OnPaint(e);
    
        // add our custom border
        System.Windows.Forms.ControlPaint.DrawBorder3D (
            e.Graphics,
            this.ClientRectangle,
            this.border3DStyle,
            this.borderSide );
    }

    第一行我们我将添加到overriden方法是 base.OnPaint (e);-这行执行被重写的代码 面板控件,传递PaintEventArgs值。如果我们排除这个 base.OnPaint (e);方法调用,我们将不得不考虑 油漆我们的分隔面板需要我们自己的一切,这当然意味着 我们必须编写代码来处理a所需的所有绘画 面板控制。 在基本控件被绘制之后(仅在之后),我们使用 System.Windows.Forms.ControlPaint。DrawBorder3D方法 在面板的顶部绘制我们的自定义边框 控件,使用来自我们先前添加的自定义属性的值。作为一个侧 注意,System.Windows.Forms。ControlPaint类值得你 是时候研究一下了,因为它包含了一堆简化的静态方法 绘制许多窗口类型控件,如按钮、复选框、 RadioButtons、网格等。 在这个阶段,我们现在有一个功能齐全的分隔面板控制,那可以 在应用程序中编译和手动引用,但现在不要停止 因为我们还远远没有完成! 添加属性 描述,文档支持 为了使我们的控件看起来和感觉像来自框架的控件 ,我们需要向新的公共访问器添加描述,并创建 新的属性组。如果没有这个,我们的新属性将被列为Misc, 我们还不清楚我们的新属性到底要做什么 其他开发人员。再一次,添加属性描述非常简单,而while 我们在这方面做得很好,包括对创建Xml文档文件的支持 稍后使用工具(如NDoc)创建。 为了实现这些,我们将使用代码修改我们的公共访问器 下面的示例。& lt; summary>行用于生成Xml文档 要向任何属性或类添加摘要,只需定位 将插入符号插入到属性/类正上方的一行中,并输入3个前向字符 斜杠(/ / /)。Visual Studio将自动构建所需的摘要结构 你所要做的就是填空。 我们在这里做的下一个添加是设计器属性行,从 绑定属性。我们将逐个介绍这里添加的属性 一: Bindable(true) -当设置为true时,对该属性的任何更改都将引发 属性更改通知,使您的设计器类(稍后讨论) 知道在设计时所做的更改。 类别(“边框选项”)-类别属性指定了它的位置 属性应在窗体设计器的属性面板中分组。如果 此属性未设置,您的新属性将列在“Misc”下。 DefaultValue(System.Windows.Forms.Border3dSide.All)——这个属性告诉我们 属性的默认值是什么,以及用于什么 用粗体文本突出显示默认值的更改。在我们的截图 可以看到值“Etched”不是粗体,因为我们使用 默认值,但值“Top”是粗体,因为我们改变了它 默认值为“All”。注意,此属性与 指定给属性的默认值-仍然必须为 控件构造函数中的所有属性。 描述(“指定应用三维的面板的侧面 属性的底部显示描述值 每当属性被选中时,属性面板都应该清楚地传达给 开发商是做什么的。 隐藏,收缩,复制Code

    /// <summary>
    /// Specifies the sides of the panel to apply a three-dimensional border to.
    /// </summary>
    [Bindable(true), Category("Border Options"), 
    DefaultValue(System.Windows.Forms.Border3DSide.All),
    Description("Specifies the sides of the panel to apply a 
    three-dimensional border to.")]
    public System.Windows.Forms.Border3DSide BorderSide
    {
        get { return this.borderSide; }
        set
        {
            if( this.borderSide != value )
            {
                this.borderSide = value;
                this.Invalidate();
            }
        }
    }
    
    /// <summary>
    /// Specifies the style of the three-dimensional border.
    /// </summary>
    [Bindable(true), Category("Border Options"), 
    DefaultValue(System.Windows.Forms.Border3DStyle.Etched),
    Description("Specifies the style of the three-dimensional border.")]
    public System.Windows.Forms.Border3DStyle Border3DStyle
    {
        get { return this.border3DStyle; }
        set
        {
            if( this.border3DStyle != value )
            {
                this.border3DStyle = value;
                this.Invalidate();
            }
        }
    }

    添加工具箱支持 向我们的新控件添加工具箱支持非常简单,并且涉及到 创建一个位图,用作控件的工具箱图标,并设置 属性,以便Visual Studio知道如何显示控件 工具箱。 要为控件创建图标,请右键单击我们的分隔面板项目 进入解决方案资源管理器,然后单击Add >新 项。在打开的对话框中,选择位图文件和 将名称设置为divide erpanel .bmp。 现在,在解决方案资源管理器中突出显示分隔面板.bmp并设置构建 对嵌入资源的操作。 接下来,打开divide erpanel .bmp进行编辑,并设置 高度和宽度为16,并设置 颜色属性为16。 现在绘制你的图标位图并保存: 最后一步是向我们的divide erpanel类添加两个新属性,因此 编译器知道关联位图并允许控件 包含在Visual Studio工具箱中: 隐藏,复制Code

    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(DividerPanel))]
    public class DividerPanel : System.Windows.Forms.Panel
    {
    }

    我们添加的第一个属性允许我们的类被用作 工具箱项——如果省略此属性,Visual Studio将暗示它已经存在 设置为true,但像往常一样,显式设置这个值是良好的编码实践 属性,这样 还原代码时,您的原始意图总是很清晰。第二行 告诉编译器关联分隔符面板。bmp文件与我们的控制。的 在此属性中指定的名称只是位图的资源名, 排除.bmp扩展。最后要注意的是,工具箱图标必须始终是 位图文件,最大颜色深度为16种颜色,尺寸为 16 x16。 添加一个简单的设计器 类 现在我们可以编译控件,将其添加到工具箱并开始拖动 但有一个潜在的问题我们需要解决 第一: 我们派生的面板控件有一个BorderStyle属性 哪个可以设置为None, FixedSingle或Fixed3D -正如我们将要做的 我们应该在标准面板控件的顶部绘制我们的自定义边界 删除BorderStyle属性,这样就不会产生混淆 开发人员应该使用哪些属性,以及避免丑陋的视觉工件 设置这两组属性的样式。 有几种方法可以达到我们想要的结果,但最干净的方法是 要创建筛选属性列表的简单设计器类,请删除 属性窗口中的BorderStyle。做这个 way保留了完整的BorderStyle属性,以便开发人员可以 如果他们希望这样做,仍然在他们的代码中以编程方式设置它。 要启动我们的设计器类,右键单击中的分隔面板项目 解决方案资源管理器,然后选择添加>添加 从上下文菜单中初始化。在Add类对话框中,输入 作为类名,并单击 好的。 要实现设计器类,我们的项目需要引用 从系统。设计。dll框架。要添加引用,右键单击 “解决方案资源管理器”中的引用,然后单击添加 参考。在“添加引用”对话框中,选择System.Design.dll为 如上图所示,然后单击OK。 DividerPanelDesigner开放。改变班级路线,这样我们就可以 继承的 System.Windows.Forms.Design。ScrollableControlDesigner类: 隐藏,复制Code

    public class DividerPanelDesigner : 
       System.Windows.Forms.Design.ScrollableControlDesigner
    {
    }

    现在切换到类视图并展开我们的divide erpaneldesigner类,直到您 进入ControlDesigner类,继承路径为: DividerPanelDesigner & lt;ScrollableControlDesigner & lt;ParentControlDesigner & lt;ControlDesigner。现在找到PreFilterProperties方法,右键单击它 选择Add >覆盖。 与前面一样,Visual Studio将自动将重写的方法添加到我们的 设计类的代码。现在我们所要做的就是添加代码来过滤我们的 多余的边框样式属性: 隐藏,复制Code

    protected override void PreFilterProperties(
       System.Collections.IDictionary properties)
    {
        properties.Remove("BorderStyle");
    }

    最后,向我们的divide erpanel类添加一个designer属性来链接我们的 设计器类如下: 隐藏,复制Code

    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(DividerPanel))]
    [DesignerAttribute(typeof(DividerPanelDesigner))]
    public class DividerPanel : System.Windows.Forms.Panel
    {
    }

    对于本教程,这就是我们的设计器类需要做的全部工作,所以我们将离开 就现在而言。创建设计器类是一本书的好主题,而不是 像这样一个简单的教程,但至少你现在有一个 介绍如何创建和使用它们。 组件的属性, 签署 在释放对上的新控件之前,我们应该做的最后步骤 毫无戒心的公众与编译过程有关。的添加 这里讨论的都是不必要的,但确实代表了最佳实践编码和a 工作量非常小,所以没有理由忽略它们。 首先,我们将打开AssemblyInfo.cs文件并提供一些值 默认的程序集属性如下: 隐藏,复制Code

    [assembly: AssemblyTitle("Divider Panel")]
    [assembly: AssemblyDescription(
      "A Panel control with selectable border appearance")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("CodeShack")]
    [assembly: AssemblyProduct("Divider Panel Tutorial")]
    [assembly: AssemblyCopyright("Copyright (c) 2003-2004 CodeShack")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]

    接下来,我们将添加不包含的更多程序集属性 默认(我将让我的代码注释自己说话): 隐藏,复制Code

    // Flagging your assembly with the CLS Compliant attribute
    // lets the Framework know that it will work with all
    // CLS Compliant languages, and theoretically with all 
    // future framework platforms (such as Mono on Linux).
    // If possible you should avoid using any non-CLS compliant code 
    // in your controls such as unsigned integers (uint) and
    // calls to non-framework assemblies.
    [assembly: System.CLSCompliant(true)] 
    
    // The ComVisible attribute should always be explicitly set for controls.
    // Note that in order for your control to be consumed by
    // COM callers, you must use parameter-less constructors
    // and you cannot use static methods.
    [assembly: System.Runtime.InteropServices.ComVisible(true)]

    现在是使用新控制器前的最后一步,给你的签名 组装。对程序集进行签名始终是一种很好的练习,因为这会防止 避免加载修改或损坏的库,保护应用程序 使用它来防止下载错误或篡改。 首先,我们必须使用Visual Studio提供的强名称工具来创建 为我们的程序集创建新的公钥/私钥对。这样做可以打开视觉 Studio命令提示符,点击开始>所有程序都在视觉Studio.Net 比;Visual Studio .Net Tools >命令提示符。 在命令提示符下,输入: 隐藏,复制Code

    sn -k [outputfile].snk

    如果没有指定项目目录中的输出文件,请复制 它现在在那里然后修改汇编info .cs文件如下,使Visual Studio 知道你的密钥文件的相对路径: 隐藏,复制Code

    [assembly: AssemblyKeyFile("..\\..\\..\\DividerPanel.snk")]

    使用控制 现在剩下的就是构建我们的控件,并将其添加到工具箱中,因此我们 可以在我们的应用程序中使用。一旦你满意你的控制 没有bug且特性完成后,在Visual Studio中将构建配置更改为 Release并按F5 to来编译它。 接下来,创建一个新的Windows应用程序项目,并在设计视图中打开一个窗体 使Windows窗体工具箱可见。右键单击工具箱和 在“自定义工具箱”对话框中选择“自定义工具箱” 打开,单击。net Fram“ework组件”选项卡,单击 浏览后,在bin\版本中找到沛可窗格.dll文件 目录并单击OK。 现在,您将在工具箱项列表中看到分隔面板控件,而您 准备开始将其拖放到应用程序中。 总结 在本教程中,我们已经触及了以下主题: 创建一个空白解决方案,作为控制项目的一个更好的起点 从现有控件继承功能 向控件类添加新属性和访问器 属性的命名约定 覆盖继承的方法 添加属性描述和文档支持 添加Visual Studio工具箱支持 简单的设计器类创建和使用 指定程序集属性并使用强名称为库签名 工具 向Visual Studio工具箱中添加自定义控件 版本历史 版本1.0:2003年8月29日-最初发布版本1.01:2004年9月12日-编辑了一些术语,增加了额外的提示。 本文转载于:http://www.diyabc.com/frontweb/news185.html

  • 相关阅读:
    Delphi 2009增强之Exit函数
    带小数的10进制转16进制
    产生指定长度的随机字符串
    在delph 2009中,利用Build Events调用UPX
    WMI信息获取
    MYSQL 存储过程学习笔记
    将窗体透明化
    倒计时
    通过程序开启XP的ClearType显示效果
    使用ODAC调用ORACLE的自定义函数和存储过程
  • 原文地址:https://www.cnblogs.com/Dincat/p/13431047.html
Copyright © 2011-2022 走看看