日期:2013-04-02  浏览次数:20361 次

控件
发布日期: 7/19/2004 | 更新日期: 7/19/2004
Chris Tacke, Windows Embedded MVP
Applied Data Systems

适用于:
Microsoft Windows CE .NET
Smart Device Extensions for Microsoft Visual Studio .NET

摘要:学习如何使用 Smart Device Extensions for Microsoft Visual Studio .NET (SDE) 创建自定义控件。



本页内容
简介
问题
对象模型
构建自定义连接器

简介
Smart Device Extensions for Microsoft Visual Studio .NET (SDE) 提供了一种可以在应用程序中使用的很好的基本控件。遗憾的是,嵌入式设备应用程序涉及的范围非常广,这就使得开发人员几乎肯定会在某些地方缺少合适的控件,此时,基本上有两个选择:重新进行应用程序的结构设计以使用可用的控件,或者采用您自己的自定义控件。

SDE 的第一个版本不支持设计时自定义控件,这意味着为了使用它们,必须手动编写将它们放入窗体并设置其大小和属性的代码。它只需很少的额外工作量,并且只需要您接受没有可用于自定义控件的 Form Design Support 这一事实。

返回页首
问题
最近,我一直在为 Visual Studio .NET 创建类库,用于包装很多硬件的功能。通过使用一个可以为他们完成所有 P/Invoking 和资源管理工作的类库,托管代码开发人员使用这个类库来访问机载微型控制器和 Microsoft Windows CE 端口就容易多了。我开发用于 Graphics Master 设备的 I/O 的类库,以便提供对两个单独的头上的引脚的读取和写入功能。

我需要一个测试和示例应用程序,该程序能够使用户轻松地通过恰当的图形接口设置或读取数字 I/O 状态并读取模拟 I/O。我希望有某个东西看起来像示意图上的接头或类似板上的物理插头。由于我要处理两个物理上不同大小的接头,所以我需要多个控件,或最好是一个可以定义大小的控件。很显然,SDE 的工具箱中没有我想要的控件。

我本来可以使用大量的 Label、CheckBox、PictureBox 和 TextBox,但是我认为这种替代方案看起来很难看。让我们尝试编写自己的控件。

返回页首
对象模型
第一个任务是决定整个对象模型。我们需要什么样的组成部分,这些组成部分将如何融合在一起,它们如何相互交互,如何与它们的环境交互?



图 1. 我的连接器控件概念

我们将创建连接器,用来包含大小可变的引脚集合,以便能够连接不同大小的接头。每个引脚必须有可以放在被显示的“引脚”的左侧或右侧(取决于它是偶数还是奇数引脚)的标识标签。每个引脚还可以是数字的或模拟的 I/O,因此每个引脚都需要有范围从零到 0xFFFF 的单独的值。最好能够一眼即可识别每个引脚的类型和值,所以将需要使用一些颜色。当然,并非接头上的所有引脚都可用于 I/O,所以我们需要能够禁用它们中的一部分,此外,我们希望引脚是交互的,这样当我们接通一个引脚时,它可以做某些操作,比如更改状态。

图 1 是一个控件在屏幕上显示的外观的很好模型。

基于这些要求,我们提出了一个如图 2 所示的对象模型。



图 2. 控件对象模型

整体的思路是,我们将有一个 Connector 基类,然后从它派生出其他几个自定义的 Connector 类。Connector 将包含一个 Pins 类,这个类只是通过从 CollectionBase 派生,使用索引器来公开 Pin 对象的 ListArray。

实现 Pin 对象
因为此控件的骨干是 Pin 对象,所以我们首先介绍它。Pin 对象将处理控件的大多数显示属性,并处理用户交互。一旦我们可以成功地在窗体上创建、显示单个引脚并与之交互,构建一个连接器将它们组合在一起就非常简单了。

Pin 对象有四个在创建它时必须设置的属性。默认的构造函数会设置它们中的每一个,但其他构造函数还可以用来允许创建者传递非默认的值。

最重要的属性是 Alignment。这个属性确定了绘制对象时文本和引脚的位置,但更重要的是,设置属性时,它将创建和放置用于绘制引脚和文本的矩形。这些矩形的使用将在随后解释 OnDraw 时进行讨论。

清单 1 显示了基本构造函数和 Alignment 属性的代码。为引脚子组件周围所定义的偏移量和边框使用了常量,但这些常量也很容易成为控件的其他属性。

清单 1. 引脚构造函数和 Alignment 属性

public Pin()
{
showValue = false;
pinValue = 0;
type = PinType.Digital;
Alignment = PinAlignment.PinOnRight;
}
public PinAlignment Alignment
{ // determines where the pin rectangle is placed
set
{
align = value;
if(value == PinAlignment.PinOnRight)
{
this.pinBorder = new Rectangle(
this.ClientRectangle.Width - (pinSize.Width + 10),
1,
pinSize.Width + 9,
this.ClientRectangle.Height - 2);
this.pinBounds = new Rectangle(
this.ClientRectangle.Width - (pinSize.Width + 5),
((this.ClientRectangle.Height -
pinSize.Height) / 2) + 1,
pinSize.Width,
pinSize.Height);
this.textBounds = new Rectangle(
5,
5,
this.ClientRectangle.Width - (pinSize.Width + 10),
20);
}
else
{
this.pinBorder = new Rectangle(
1,
1,
pinSize.Width + 9,
this.ClientRectangle.Height - 2);
this.pinBounds = new Rectangle(
6,
this.ClientRectangle.Height - (pinSize.Height + 4),
pinSize.Width,
pinSize.Height);
this.textBounds = new Rectangle(
pinSize.Width + 10,
5,
this.ClientRectangle.Width - (pinSize.Width + 10),
20);
}
this.Invalidate();
}
get
{
return align;
}
}

由于 Pin 对象不会提供很好的用户交互或可自定义性,所以引脚的核心功能是我们将重写的绘图例程 OnDraw,重写该例程是为了可以由我们来绘制整个引脚。

每个引脚将绘制三个部分:引脚本身将是一个圆(除非它是 Pin 1,这时它将是一个方块),我们将围绕引脚绘制边框矩形,然后在引脚的左侧或右侧留出一个区域用来绘制引脚的文本。