复合控件提供了一种可创建和重复使用自定义图形界面的方法。 复合控件本质上是具有视觉表示形式的组件。 因此,它可能包含一个或多个 Windows 窗体控件、组件或代码块,这些代码块可以通过验证用户输入、修改显示属性或执行作者所需的其他任务来扩展功能。 复合控件可以与其他控件相同的方式放置在 Windows 窗体上。 在本演练的第一部分中,您将创建一个简单控件ctlClock。 在本演练的第二部分中,您将通过继承来扩展 ctlClock 的功能。
创建项目
创建新项目时,请指定其名称以设置根命名空间、程序集名称和项目名称,并确保默认组件位于正确的命名空间中。
创建 ctlClockLib 控件库和 ctlClock 控件
在 Visual Studio 中,创建新的 Windows 窗体控件库 项目,并将其命名为 ctlClockLib。
默认情况下,项目名称
ctlClockLib也分配给根命名空间。 根命名空间用于限定程序集中的组件名称。 例如,如果两个程序集提供了名为ctlClock的组件,则可以使用ctlClock指定ctlClockLib.ctlClock.组件。在 解决方案资源管理器中,右键单击 UserControl1.cs,然后单击“ 重命名”。 将文件名更改为
ctlClock.cs。 当系统询问是否要重命名对代码元素“UserControl1”的所有引用时,请单击“ 是 ”按钮。注释
默认情况下,复合控件继承自 UserControl 系统提供的类。 该 UserControl 类提供所有复合控件所需的功能,并实现标准方法和属性。
在“ 文件 ”菜单上,单击“ 全部保存 ”以保存项目。
将 Windows 控件和组件添加到复合控件
可视界面是复合控件的重要组成部分。 此可视界面是通过向设计器图面添加一个或多个 Windows 控件来实现的。 在以下演示中,你将将 Windows 控件合并到复合控件中,并编写代码来实现功能。
向您的复合控件添加标签和计时器
在 解决方案资源管理器中,右键单击 ctlClock.cs,然后单击 “视图设计器”。
在 工具箱中,展开 “通用控件 ”节点,然后双击“ 标签”。
一个命名为Label的控件被添加到您的设计器界面上的控件中。
在设计器中,单击 label1。 在“属性”窗口中,设置以下属性。
资产 更改为 名称 lblDisplay文字 (blank space)TextAlign MiddleCenterFont.Size 14在 工具箱中,展开 “组件” 节点,然后双击“ 计时器”。
由于 Timer 是一个组件,因此它在运行时没有可视表示形式。 因此,它不会与设计器图面上的控件一起显示,而是显示在 组件设计器 (设计器图面底部的托盘) 中。
在 组件设计器中,单击 timer1,然后将 Interval 属性设置为
1000,并将 Enabled 属性设置为true。该 Interval 属性控制 Timer 组件的计时频率。 每次
timer1计时周期时,它都会在timer1_Tick事件中运行代码。 间隔表示刻度之间的毫秒数。在 组件设计器中,双击 timer1 转到
timer1_Tick事件ctlClock。修改代码,使其类似于以下代码示例。 请务必将访问修饰符从
private更改为protected。protected void timer1_Tick(object sender, System.EventArgs e) { // Causes the label to display the current time. lblDisplay.Text = DateTime.Now.ToLongTimeString(); }此代码将使当前时间显示在
lblDisplay中。 由于timer1间隔设置为1000,因此每隔1000毫秒发生一次该事件,从而每秒更新当前时间。修改方法以使用
virtual关键字重写。 有关详细信息,请参阅下面的“从用户控件继承”部分。protected virtual void timer1_Tick(object sender, System.EventArgs e)在“ 文件 ”菜单上,单击“ 全部保存 ”以保存项目。
向复合控件添加属性
时钟控件现在封装一个 Label 控件和一个 Timer 组件,每个控件都有自己的固有属性集。 虽然这些控件的各个属性对控件的后续用户无法访问,但可以通过编写相应的代码块来创建和公开自定义属性。 在以下过程中,你将向控件添加属性,使用户能够更改背景和文本的颜色。
向复合控件添加属性
在 解决方案资源管理器中,右键单击 ctlClock.cs,然后单击“ 查看代码”。
您的控件的代码编辑器将打开。
public partial class ctlClock找到该语句。 在左大括号下面({)键入以下代码。private Color colFColor; private Color colBColor;这些语句将创建用于存储要创建的属性的值的私有变量。
在步骤 2 中的变量声明下面输入或粘贴以下代码。
// Declares the name and type of the property. public Color ClockBackColor { // Retrieves the value of the private variable colBColor. get { return colBColor; } // Stores the selected value in the private variable colBColor, and // updates the background color of the label control lblDisplay. set { colBColor = value; lblDisplay.BackColor = colBColor; } } // Provides a similar set of instructions for the foreground color. public Color ClockForeColor { get { return colFColor; } set { colFColor = value; lblDisplay.ForeColor = colFColor; } }上述代码使两个自定义属性
ClockForeColor和ClockBackColor对该控件的后续用户可用。 和getset语句提供属性值的存储和检索,以及实现适用于该属性的功能的代码。在“ 文件 ”菜单上,单击“ 全部保存 ”以保存项目。
测试控件
控件不是独立应用程序;它们必须托管在容器中。 测试控件的运行时行为,并使用 UserControl 测试容器练习其属性。 有关详细信息,请参阅 如何:测试 UserControl 的 Run-Time 行为。
测试你的控制能力
按 F5 生成项目并在 UserControl 测试容器中运行控件。
在测试容器的属性网格中,找到该
ClockBackColor属性,然后选择该属性以显示调色板。点击颜色来选择。
控件的背景色将更改为所选颜色。
使用类似的事件序列来验证属性是否
ClockForeColor按预期运行。在本部分和前面的部分中,你已了解组件和 Windows 控件如何与代码和打包相结合,以复合控件的形式提供自定义功能。 你已了解如何在复合控件中公开属性,以及如何在控件完成后对其进行测试。 在下一部分中,你将了解如何使用
ctlClock基构造继承的复合控件。
从复合控件继承
在前面的部分中,你学习了如何将 Windows 控件、组件和代码合并到可重用的复合控件中。 复合控件现在可以用作构建其他控件的基础。 从基类派生类的过程称为 继承。 在本部分中,你将创建名为 <ctlClock 通过重写父方法和添加新方法及属性,你将了解如何扩展 ctlClock 的功能。
创建继承控件的第一步是从父控件派生出它。 此作将创建一个新控件,该控件具有父控件的所有属性、方法和图形特征,但也可以作为新增或修改功能的基础。
创建继承的控件
在 解决方案资源管理器中,右键单击 ctlClockLib,指向 “添加”,然后单击“ 用户控件”。
此时将打开“添加新项”对话框。
选择 继承的用户控件 模板。
在 “名称 ”框中,键入
ctlAlarmClock.cs,然后单击“ 添加”。此时会显示 “继承选取器 ”对话框。
在 组件名称下,双击 ctlClock。
在 解决方案资源管理器中,浏览当前项目。
注释
已将名为 ctlAlarmClock.cs 的文件添加到当前项目中。
添加警报属性
添加属性到继承控件上的方法与添加到复合控件上的方法相同。 现在,你将使用属性声明语法将两个属性添加到控件: AlarmTime这将存储警报的日期和时间值即将关闭,并 AlarmSet指示是否已设置警报。
向复合控件添加属性
在 解决方案资源管理器中,右键单击 ctlAlarmClock,然后单击“ 查看代码”。
public class找到该语句。 请注意,控件继承自ctlClockLib.ctlClock. 在左大括号({)语句下,键入以下代码。private DateTime dteAlarmTime; private bool blnAlarmSet; // These properties will be declared as public to allow future // developers to access them. public DateTime AlarmTime { get { return dteAlarmTime; } set { dteAlarmTime = value; } } public bool AlarmSet { get { return blnAlarmSet; } set { blnAlarmSet = value; } }
添加到控件的图形界面
您的继承控件具有与其继承自的控件相同的可视化界面。 它具备与其父控件相同的组成控件,但这些控件的属性不可用,除非它们被特别公开。 你可以以与添加到任何复合控件相同的方式添加到继承的复合控件的图形界面。 若要继续添加到闹钟的可视界面,你将添加一个标签控件,该控件将在警报响起时闪烁。
添加标签控件
在 解决方案资源管理器中,右键单击 ctlAlarmClock,然后单击“ 视图设计器”。
ctlAlarmClock设计器会在主窗口打开。单击控件的显示部分,然后查看“属性”窗口。
注释
显示所有属性时,它们都会变暗。 这表示这些属性是原生于
lblDisplay,无法在属性窗口中进行修改或访问。 默认情况下,复合控件中包含的控件是private,并且无法通过任何方式访问其属性。注释
如果希望复合控件的后续用户有权访问其内部控件,请将其声明为
public或protected。 这样,即可使用相应的代码设置和修改复合控件中包含的控件的属性。将 Label 控件添加到复合控件。
使用鼠标,紧邻显示框下方拖动 Label 控件。 在“属性”窗口中,设置以下属性。
资产 设置 名称 lblAlarm文字 报警! TextAlign MiddleCenter可见 false
添加警报功能
在前面的过程中,你添加了属性和控件,该控件将在复合控件中启用警报功能。 在此过程中,你将添加代码以将当前时间与警报时间进行比较,如果相同,则添加代码以闪烁警报。 通过重写timer1_Tick方法ctlClock并向其中添加额外代码,可以扩展ctlAlarmClock的功能,同时保留ctlClock的所有固有功能。
重写 ctlClock 控件的 timer1_Tick 方法
在 代码编辑器中,找到
private bool blnAlarmSet;该语句。 紧邻其下方,添加以下语句。private bool blnColorTicker;在 代码编辑器中,找到类的末尾处的右大括号(
}))。 在大括号之前,添加以下代码。protected override void timer1_Tick(object sender, System.EventArgs e) { // Calls the Timer1_Tick method of ctlClock. base.timer1_Tick(sender, e); // Checks to see if the alarm is set. if (AlarmSet == false) return; else // If the date, hour, and minute of the alarm time are the same as // the current time, flash an alarm. { if (AlarmTime.Date == DateTime.Now.Date && AlarmTime.Hour == DateTime.Now.Hour && AlarmTime.Minute == DateTime.Now.Minute) { // Sets lblAlarmVisible to true, and changes the background color based on // the value of blnColorTicker. The background color of the label // will flash once per tick of the clock. lblAlarm.Visible = true; if (blnColorTicker == false) { lblAlarm.BackColor = Color.Red; blnColorTicker = true; } else { lblAlarm.BackColor = Color.Blue; blnColorTicker = false; } } else { // Once the alarm has sounded for a minute, the label is made // invisible again. lblAlarm.Visible = false; } } }添加此代码可完成多个任务。 该
override语句指示控件使用此方法代替从基控件继承的方法。 当调用此方法时,它通过执行base.timer1_Tick语句来调用其所覆盖的方法,从而确保在该控件中重现原始控件中包含的所有功能。 然后,它运行其他代码以合并警报功能。 警报发生时,将显示闪烁的标签控件。您的闹钟控制几乎已完成。 剩下的唯一方法是实现关闭它的方法。 为此,需要向方法添加代码
lblAlarm_Click。
执行关闭方法
在 解决方案资源管理器中,右键单击 ctlAlarmClock.cs,然后单击 “视图设计器”。
设计器随即打开。
向控件添加按钮。 按如下所示设置按钮的属性。
资产 价值 名称 btnAlarmOff文字 禁用警报 在设计器中,双击 btnAlarmOff。
代码编辑器会打开到
private void btnAlarmOff_Click行。修改此方法,使其类似于以下代码。
private void btnAlarmOff_Click(object sender, System.EventArgs e) { // Turns off the alarm. AlarmSet = false; // Hides the flashing label. lblAlarm.Visible = false; }在“ 文件 ”菜单上,单击“ 全部保存 ”以保存项目。
在窗体上使用继承的控件
可以按照测试基类控件的方式测试继承的控件: ctlClock按 F5 生成项目并在 UserControl 测试容器中运行控件。 有关详细信息,请参阅 如何:测试 UserControl 的 Run-Time 行为。
要使用控件,您需要将其托管在窗体中。 与标准复合控件一样,继承的复合控件不能独立存在,必须托管在窗体或其他容器中。 由于 ctlAlarmClock 功能更深入,因此需要额外的代码来测试它。 在此过程中,你将编写一个简单的程序来测试其 ctlAlarmClock功能。 你将编写代码来设置和显示 AlarmTime 其属性 ctlAlarmClock,并测试其固有函数。
创建并将您的控件添加到测试窗体中
在 解决方案资源管理器中,右键单击 ctlClockLib,然后单击“ 生成”。
将新的 Windows 窗体应用程序 项目添加到解决方案,并将其命名为 “测试”。
在 解决方案资源管理器中,右键单击测试项目的 “引用 ”节点。 单击“ 添加引用 ”以显示“ 添加引用 ”对话框。 单击标记为 “项目”的选项卡。 您的项目
ctlClockLib将在 项目名称下列出。 双击项目以添加对测试项目的引用。在 解决方案资源管理器中,右键单击“ 测试”,然后单击“ 生成”。
在 工具箱中,展开 ctlClockLib 组件 节点。
双击 ctlAlarmClock 将副本
ctlAlarmClock添加到窗体。在工具箱中,找到并双击 DateTimePicker 以向窗体添加DateTimePicker控件,然后双击Label”添加控件。
使用鼠标将控件放置在窗体上方便的位置。
按以下方式设置这些控件的属性。
控制 资产 价值 label1文字 (blank space)名称 lblTestdateTimePicker1名称 dtpTest格式 Time 在设计器中,双击 dtpTest。
代码编辑器打开到
private void dtpTest_ValueChanged。修改代码,使其类似于以下内容。
private void dtpTest_ValueChanged(object sender, System.EventArgs e) { ctlAlarmClock1.AlarmTime = dtpTest.Value; ctlAlarmClock1.AlarmSet = true; lblTest.Text = "Alarm Time is " + ctlAlarmClock1.AlarmTime.ToShortTimeString(); }在 解决方案资源管理器中,右键单击“ 测试”,然后单击“ 设置为启动项目”。
在 “调试” 菜单中,单击 “启动调试” 。
测试程序开始。 请注意,当前时间在控件中
ctlAlarmClock更新,开始时间显示在控件中 DateTimePicker 。DateTimePicker单击显示小时分钟的位置。
使用键盘,将分钟的值设置为比
ctlAlarmClock显示的当前时间大一分钟。lblTest中显示警报设置的时间。 等待显示的时间到达设定警报时间。 当显示的时间到达设置警报的时间时,lblAlarm将闪烁。通过单击
btnAlarmOff关闭警报。 现在可以重置警报。
本文介绍了许多关键概念。 你已了解如何通过将控件和组件组合到复合控件容器来创建复合控件。 你已了解如何将属性添加到控件,并编写代码来实现自定义功能。 在最后一节中,你学习了通过继承扩展给定复合控件的功能,并通过重写这些方法来更改主机方法的功能。