.net用户控件概述及实例

来源:互联网 发布:农村淘宝下载2017 编辑:程序博客网 时间:2024/05/17 04:51

一、什么是用户控件

使用 .NET Framework 可以开发和实现新的控件。可以通过继承来扩展熟悉的用户控件和现有控件的功能。还可以编写自定义控件,这些控件执行自己的绘制功能。

Windows 窗体支持三种用户定义的控件:复合、扩展和自定义。

1.  复合控件

复合控件是封装在公共容器内的 Windows 窗体控件的集合。这种控件有时称为“用户控件”。包含的控件称为“构成控件”。

复合控件包含与每个包含的 Windows 窗体控件相关联的所有固有功能,允许您有选择地公开和绑定它们的属性。复合控件还提供了大量的默认键盘处理功能,您不需要任何额外的开发。

例如,可以生成复合控件以显示来自数据库的客户地址数据。此控件可以包括一个用来显示数据库字段的 DataGridView 控件、一个用来处理到数据源的绑定的 BindingSource 和一个用来在记录之间移动的 BindingNavigator 控件。可以有选择地公开数据绑定属性,还可以将整个控件打包并在不同应用程序之间重复使用。有关这种复合控件的示例,请参见如何:应用 Windows 窗体控件中的属性

若要创作复合控件,请从 UserControl 类派生。基类 UserControl 为子控件提供了键盘路由并使子控件可以作为一个组进行工作。有关更多信息,请参见开发复合 Windows 窗体控件

2.  扩展控件

可以从任何现有的 Windows 窗体控件导出继承控件。此方法使您得以保留 Windows 窗体控件的所有固有功能,然后通过添加自定义属性、方法或其他功能扩展此固有功能。可以使用此选项重写基控件的绘制逻辑,然后更改该控件的外观以扩展其用户界面。

例如,可以创建一个由 Button 控件派生的控件,用于跟踪用户的单击次数。

在某些控件中,也可以通过重写基类的 OnPaint 方法为图形用户界面添加自定义外观。对于跟踪单击次数的扩展按钮,可以重写 OnPaint 方法来调用 OnPaint 的基实现,然后在 Button 控件的工作区的一角绘制单击计数。

3.  自定义控件

创建控件的另一种方法是通过从 Control 继承从头开始创建一个控件。Control 类提供控件所需的所有基本功能(包括鼠标和键盘处理事件),但不提供控件特定的功能或图形界面。

与通过从 UserControl 或现有 Windows 窗体控件继承创建控件相比,通过从 Control 类继承创建控件需要耗费更多的心思和精力。由于大量的实现将留给您进行,因此,您的控件可以具有比复合控件或扩展控件更大的灵活性,而且您可以使控件完全满足自己的需要。

若要实现自定义控件,必须编写该控件的 OnPaint 事件的代码,以及所需的任何功能特定的代码。还可以重写 WndProc 方法并直接处理窗口消息。这是创建控件的最强大的方法,但是要有效地使用此技术,需要熟悉 Microsoft Win32® API

时钟控件即是一个自定义控件,它复制模拟时钟的外观和行为。自定义绘制将被调用来促使时钟指针走动,以响应内部 Timer 组件的 Tick 事件。有关更多信息,请参见如何:开发简单的 Windows 窗体控件

 

二、创建步骤

1.   新建一个“Windows 控件库”项目。

创建的新项目中包含一个空白的用户控件。

2.   将一个控件从“工具箱”的“Windows 窗体”选项卡中拖到设计器上。

3.   应将这些控件定位到和设计成您希望它们在最终用户控件中出现的样子。如果要使开发人员得以访问构成控件,则必须将它们声明为公共的,或有选择地公开其属性。有关详细信息,请参见如何:公开构成控件的属性。

4.   实现将合并到控件中的任合自定义方法或属性。

5.   F5 生成该项目,然后在“用户控件测试容器”中运行控件。有关更多信息,请参见如何:测试 UserControl 的运行时行为

 

三、属性

当我们把一个控件拖入窗体时,将显示该控件的属性,这些属性可以设置成在窗体可见或不可见,属于哪一类等各种性质,这就涉及到.net framework的设计时结构,如下图所示。下面介绍.net framework的设计时结构以及如何使用这些属性:

.NET Framework 为在设计时环境中自定义组件行为和用户界面提供了接口和类。设计时环境通常包括用于排列组件的窗体设计器和用于配置组件的属性值的属性浏览器。设计时环境通常还会提供设计时机制可以访问和使用的设计时服务。

.NET Framework 定义了开发人员可用来实现自定义设计时支持的接口。扩展设计时支持的主要机制可分为以下几类:设计器、类型转换器和 UI 类型编辑器。属性会应用到类型和类型成员,以使它们与这些设计时支持提供程序关联。

属性

属性将类型或类型成员与扩展设计时行为的类关联。DesignerAttribute 使类型与设计器关联。TypeConverterAttribute 使类型或类型成员与类型转换器关联。EditorAttribute 使类型或类型成员与 UI 类型编辑器关联。有关 .NET Framework 中的属性的更多信息,请参见组件的设计时属性 (Attribute) 属性与设计时支持

设计器

设计器可以在设计时自定义组件的行为,包括它的外观、初始化以及与用户的交互。对于选定的组件,设计器可以添加、移除或替换属性浏览器中列出的属性。设计器可以提供用户定义的方法,这些方法可以链接到某些组件事件,或从自定义菜单命令或 DesignerVerb 中执行。设计器还可以使用由设计时环境提供的服务。有关设计器的更多信息,请参见自定义设计器

类型转换器

通过实现类型转换器,可在该转换器的既定支持类型和其他数据类型(在这些数据类型之间该转换器可以来回翻译值)之间转换值。类型转换器还可提供逻辑,以便在设计时能够在属性浏览器内配置属性。对于类型转换器在属性浏览器中在设计时支持的属性类型,类型转换器可以为该属性提供标准值的列表。类型转换器还可以生成初始化代码,以便在设计时初始化属性。有关实现类型转换器的信息,请参见如何:实现类型转换器

UI 类型编辑器

UI 类型编辑器可以提供一个自定义用户界面 (UI),以便在设计时编辑属性的值并显示属性值的表示形式。UI 类型编辑器是特定于具体类型的,并且提供了用户界面,以便在设计时配置该编辑器既定支持的属性类型或不具有重写属性的派生类型的属性。UI 类型编辑器可以显示“Windows 窗体”或下拉配置界面以便配置属性。有关实现 UI 类型编辑器的更多信息,请参见如何:实现用户界面类型编辑器

与设计相关的类型

下表列出了与设计相关的命名空间中的一些重要的类。

System.Drawing.Design

类型

说明

UITypeEditor

提供用于实现值编辑器的基类。

IToolboxService

提供管理和查询开发环境中的“工具箱”的方法和属性。

ToolboxItem

提供工具箱项的基实现。

System.ComponentModel.Design

类型

说明

DesignerActionList

为类型提供基类,这些类型定义用于创建智能标记面板的项目的列表。

DesignSurface

为设计组件提供一个用户界面。

IDesigner

提供构建自定义设计器的基本框架。

IRootDesigner

提供对根级设计器视图技术的支持。

IExtenderProviderService

提供一个接口,用于在设计时添加和移除扩展程序提供程序。

UndoEngine

指定一般撤消/重复功能。

System.Windows.Forms.Design

类型

说明

IWindowsFormsEditorService

提供一个接口,供 UI 类型编辑器用来在设计模式下显示 Windows 窗体,或显示 PropertyGrid 控件中的下拉区域中的控件。

ControlDesigner

基设计器类,用于扩展 Control 的设计模式行为。

DocumentDesigner

基设计器类,用于扩展支持嵌套控件并接收滚动消息的 Control 的设计模式行为,并为其提供根级设计模式视图。

System.Windows.Forms.Design.Behavior

类型

说明

BehaviorService

管理设计器中的用户界面。

Behavior

表示由 BehaviorService 管理的 Behavior 对象。

Adorner

管理与用户界面相关的 Glyph 对象的集合。此类不能被继承。

Glyph

表示一个由 Adorner 管理的用户界面 (UI) 实体。

设计时服务

.NET Framework 提供一组可以扩展设计器功能的设计时服务。使用设计模式中的组件的 GetService 方法可获取这些服务。您可以使用 IDesignerHost 服务接口的 AddService 方法来添加您自己的可从设计模式项目中访问的服务类型。

IComponentChangeService 接口允许设计器在编程人员在设计时环境中更改、添加或移除组件时接收通知。

IDesignerEventService 接口允许设计器在编程人员在设计时环境中添加或移除设计器时以及当前组件选择发生更改时接收通知。

IDesignerFilter 接口允许设计器在属性浏览器中为其组件显示的属性集合添加属性以及筛选属性集合中的属性。

IDesignerHost 接口提供了一个用于执行以下操作的接口:添加和检索服务,处理与设计器状态相关的事件,检测设计器当前是否正在加载,以及管理组件或设计器事务。设计器事务使操作序列能够按这样一种模式发生:防止设计时视图在一系列操作完成之前刷新其显示,以便改善性能,同时,设计器事务还提供了一种机制,可使事务的组件操作能够被撤消并恢复以前的状态。

IDesignerOptionService 接口允许设计器获取和设置 Windows 窗体设计器属性网格中列出的属性值,在 Visual Studio 环境中,可从“工具”菜单中的“选项”菜单项访问 Windows 窗体设计器属性网格。DesignerOptionService 类扩展了查询选项的灵活性。

对于用户定义的数据和可由键或值访问的键对,IDictionaryService 接口允许设计器使用基于键的集合。

当选中组件时,IEventBindingService 接口允许设计器在设计时公开属性浏览器中的组件的事件。

IExtenderListService 接口允许设计器在设计时环境中获取当前活动的扩展程序提供程序。IExtenderProviderService 接口允许设计器在设计时添加或移除扩展程序提供程序。

IHelpService 接口允许设计器创建和移除帮助服务上下文、创建和移除帮助上下文属性以及按照关键字或 URL 显示帮助主题。

IInheritanceService 接口允许设计器搜索派生类的组件并标识每一组件的任何继承属性。

IMenuCommandService 接口允许设计器在设计时环境中搜索、添加、移除或调用菜单命令。

IReferenceService 接口允许设计器在当前设计模式项目中按引用获取对象名称,按名称获取对对象的引用,获取对指定组件的父级引用,或获取对指定类型的对象的引用。

IResourceService 接口允许设计器为指定的 CultureInfo 获取资源读取器或资源编写器。

IRootDesigner 接口允许设计器使用自定义设计器视图屏幕替换根设计器视图。IRootDesigner 接口必须由组件的设计器实现。

ISelectionService 接口允许设计器获取对当前所选组件的引用的集合,设置组件或组件集合的选定内容,以及确定是否选定了组件。

IServiceContainer 接口允许设计器添加或移除可被其他组件或设计器使用的服务。

ITypeDescriptorFilterService 接口允许组件或设计器在设计时筛选由任何组件公开的属性 (Attribute)、事件和属性 (Property)ITypeDescriptorFilterService 接口必须由一个类实现;而使用设计模式中设置的组件的 Site AddService 方法,应当可以将该类添加为服务。

ITypeResolutionService 接口允许设计器给项目添加对程序集的引用,按名称获取类型或程序集,以及获取指定程序集的路径。

DesignerActionService DesignerActionUIService 类实现自定义设计器的智能标记功能。有关更多信息,请参见 Windows 窗体的设计器命令和 DesignerAction 对象模型

BehaviorService 管理设计器中的用户界面。它为在设计时操作用户界面元素(例如与鼠标相关的事件、菜单命令和 OLE 拖放操作)提供了一种统一的方式。有关更多信息,请参见行为服务概述

CodeDomComponentSerializationService 类可将一组组件或可序列化的对象序列化到一个序列化存储区中。有关更多信息,请参见设计器序列化概述

设计时支持扩展通常是在与组件代码不在一起的代码中实现的。许多属性都用于将设计时支持提供程序与一个类型或一个类型的单个成员关联。

关联设计时支持属性

DesignerAttribute 使设计器与类型关联。TypeConverterAttribute 使类型转换器与类型或类型成员关联。EditorAttribute 使 UI 类型编辑器与类型或类型成员关联。

自定义组件初始化属性

通过将 DefaultValueAttribute 应用于属性,在设计时加载组件时,可以为要设置的属性指定默认值。DefaultValueAttribute 在设计时重写由组件初始化代码设置的值,但该属性 (Attribute) 不重写由设计器设置的值。

自定义“属性”(Property) 窗口行为的属性 (Attribute)

通过向“属性”(Property) 窗口应用 BrowsableAttribute,可以指示该窗口中是否应列出属性 (Property) 或事件。您还可以在设计时使用实现 IDesignerFilter 接口的设计器来修改对“属性”窗口公开的属性和事件的集合。通过对属性 (Property) 或事件应用 CategoryAttribute,您可以指定属性或事件在“属性”窗口中应列在哪个类别下。通过对属性 (Property) 或事件应用 DescriptionAttribute,您可以指定在“属性”窗口中显示的对属性或事件的说明。

通过将 DesignOnlyAttribute 应用于属性,可以指定是否只能在设计时设置属性。通过将 ReadOnlyAttribute 应用于属性,可以指定在设计时属性是只读还是可读/写。

通过给属性 (Property) 应用 ParenthesizePropertyNameAttribute true 值,您可以指定在“属性”窗口中列出该属性时其名称是否应括在括号中。

通过将 NotifyParentPropertyAttribute 应用于应该引发通知的嵌套属性,您可以指定当嵌套属性的值改变时,是否应该通知具有嵌套属性或子级属性的属性。

通过给属性或事件应用 RefreshPropertiesAttribute 和相应的 RefreshProperties 值,您可以指定:是应该刷新组件的属性、不应刷新属性,还是应该重新绘制设计器视图。

自定义设计时序列化行为属性

通过给属性应用 DesignerSerializationVisibilityAttribute 和相应的 DesignerSerializationVisibility 枚举值,您可以指定:是要序列化属性的值,还是要序列化集合属性的值。Visual Studio 中对此任务提供了广泛的支持。 有关更多信息,请参见 演练:使用 DesignerSerializationVisibilityAttribute 序列化标准类型的集合.

通过将 SerializableAttribute 应用于类型,可以将类型指定为可序列化类型。通过实现 ISerializable 接口或提供自定义序列化程序,可以提供自定义序列化。有关序列化的更多信息,请参见 序列化。

有关常用设计时属性的更多信息,请参见组件的设计时属性 (Attribute)

应用属性

设计时属性应用于属性、事件和类,甚至应用于程序集。下面的代码示例说明了应用于类然后应用于属性和事件的属性。

C# 

复制代码

// The attribute is the element in brackets, and the parameters in

// the attribute syntax are arguments of the constructor

// of the attribute class.

//

// Attributes applied at the class level.

[DefaultEvent("ValueChanged")]

[DefaultProperty("Number")]

public class MyControl : Control {

   ...

   // Attribute applied to a property.

   [DefaultValue(false)]

   public new bool TabStop {...

   }

 

   // Attribute applied to a property.

   [CategoryAttribute("Data")]

   public int Number {...}

 

   // Attribute applied to an event.

   [Description("Raised when the Value displayed changes.")]

   public event EventHandler ValueChanged;

}

根据约定,属性 (Attribute) 类称为 AttributeName 属性 (Attribute)System.ComponentModel 命名空间包含许多基属性类。

设计时属性与继承

从具有设计时属性的基组件派生组件或控件时,组件继承基类的设计时功能。如果基本功能足以满足需要,则不必再次应用属性。但是,可以重写相同类型的属性或将其它属性应用于派生的组件。下面的代码片段显示了一个自定义控件,该控件通过重写基类中应用的 BrowsableAttribute 属性 (Attribute),重写从 Control 继承的 Text 属性 (Property)

C# 

复制代码

public class MyControl : Control {

// The base class has [Browsable(true)] applied to the Text property.

[Browsable(false)]

 public override string Text {...}

...

}

应用类型转换器、UI 类型编辑器或设计器属性

要使设计时支持提供程序与类型或类型成员关联,请应用类声明或成员声明上面一行上的相应的属性类型。下面的代码示例说明了应用于类型的 TypeConverterAttribute

C# 

复制代码

[ TypeConverter(typeof(MyColorConverter))]

[ Editor(typeof(MyColorEditor), typeof(UITypeEditor))]

struct MyColor {...}

如果属性的类型不具有与之关联的类型转换器或 UI 类型编辑器,或者您想要重写与属性的类型关联的默认类型转换器或 UI 类型编辑器,可以将属性应用于属性自身。若要将类型转换器与属性关联,请将 TypeConverterAttribute 应用于属性声明,如下面的代码示例所示。

C# 

复制代码

[ TypeConverter(typeof(PointConverter))]

        public Point MyLocation {...} 

若要使 UI 类型编辑器与属性关联,请将 EditorAttribute 应用于该属性,如下面的代码示例所示。

C# 

复制代码

[ Editor(typeof(FlashTrackBarDarkenByEditor), typeof(UITypeEditor))]

        public byte DarkenBy {...}

设计器可以与类型关联,但不能与属性关联。要使设计器与某种类型关联,请在类声明上面一行应用 DesignerAttribute,如下面的代码示例所示。

C# 

复制代码

    [Designer(typeof(HelpLabel.HelpLabelDesigner))]

    public class HelpLabel : System.Windows.Forms.Control, System.ComponentModel.IExtenderProvider {...}

 

注意

在上面的示例中,TypeConverterAttributeEditorAttribute DesignerAttribute 类的构造函数将 System.Type 对象作为它们的参数接受。如果类型与设计时类位于同一程序集中,这些属性的这种形式的构造函数将有效。如果设计时类位于其他程序集中,则需要另一种形式的属性构造函数(称为程序集限定格式),如下面的代码示例所示。

 

C# 

复制代码

[Designer("System.Windows.Forms.Design.DocumentDesigner, System.Design")]

public class MyForm : Form {...}

程序集级别设计时属性

ASP.NET 提供了一个程序集级别属性 (System.Web.UI.TagPrefixAttribute),该属性使控件开发人员能够为 ASP.NET 控件指定标记前缀。在 Register 指令中,此标记前缀由 Visual Studio .NET 为控件自动插入,所以控件可以在页中以声明方式同预先指定的标记前缀 (<tagprefix:controlname runat = server /> ) 一同使用。

下面的代码示例演示如何应用 TagPrefixAttribute。属性构造函数的第一个参数指定命名空间,第二个参数指定标记前缀。

C# 

复制代码

[ assembly:TagPrefix("SimpleControls", "simple") ]

namespace SimpleControls {

    [

        Designer("SimpleControl.Design.SimpleDesigner, SimpleControl")

    ]

    public class SimpleControl : System.Web.UI.WebControls.WebControl {}

}

 

四、事件

参见实例。

五、实例

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Text;

using System.Windows.Forms;

using System.ComponentModel.Design;

 

namespace MultiChannalResistorMeasurement

{

    public partial class DigitalPinOnOff : UserControl

    {

        [Category("Appearance")]

        [Description("The text of the pin. such as Pin1, Pin2 ... Pin13")]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

        [Localizable(true)]

        [HelpKeywordAttribute("MultiChannalResistorMeasurement.DigitalPinOnOff.TitleText")]

        public string PinText

        {

            get

            {

                return this.Pin.Text;

            }

 

            set

            {

                this.Pin.Text = value;

            }

        }

 

 

        public DigitalPinOnOff()

        {

            InitializeComponent();

        }

 

        private void Pin_CheckedChanged(object sender, EventArgs e)

        {

            if (Pin.Checked)

            {

                onOffPictureBox.Image = Properties.Resources.on;               

            }

            else

                onOffPictureBox.Image = Properties.Resources.off;

            OnPinStatusChanged(new PinStatusChangedEventArgs(Pin.Checked));

        }

 

        public delegate void PinStatusChangedEventHandler(object sender, PinStatusChangedEventArgs e);//事件所需的委托

 

        //当状态改变时触发事件

        public event PinStatusChangedEventHandler PinStatusChanged;//定义一个PinStatusChanged事件

 

        protected virtual void OnPinStatusChanged(PinStatusChangedEventArgs e)

        {//事件触发方法

            if (PinStatusChanged != null)

            {//判断事件是否为空

                PinStatusChanged(this, e);//触发事件

            }

        }

 

    }

 

    public class PinStatusChangedEventArgs : EventArgs

    {

        private bool pinsStatus;

 

        /// <summary>

        /// 状态改变事件数据

        /// </summary>

        /// <param name="status">改变后的状态</param>

        public PinStatusChangedEventArgs(bool status)

        {

            pinsStatus = status;

        }

 

        /// <summary>

        /// 获取状态

        /// </summary>

        public bool GetPinStatus

        {

            get { return pinsStatus; }

        }

 

    }

}

 

原创粉丝点击