使用 microsoft.net frameworks 创建基于 windows 的应用程序
shawn burke
microsoft corporation
2000年9月
摘要: :本文介绍了 win 表单这一新的窗体程序包,借助这一程序包,开发人员能够充分利用 microsoft windows 操作系统所提供的 ui 功能。
目录
简介
介绍 win forms
更好的易学易用性
布局
gdi+
访问底层系统
结论
--------------------------------------------------------------------------------
简介
目前 web 已成了街谈巷议的话题,看起来好像 microsoft® visual studio® 开发系统对创建基于 microsoft windows® 的传统应用程序的支持有所减弱。实际上,microsoft 对基于 windows 的应用程序开发方面的投资在不断加大。
win 表单是一个新的窗体程序包,借助这一程序包,开发人员能够充分利用 microsoft windows® 操作系统所提供的丰富的用户界面功能,创建基于 windows 的应用程序。win forms 是新的 microsoft®.net 平台的一个组成部分,它提供了许多新技术,包括通用的应用程序框架、可管理的执行环境、一体化的安全性以及面向对象的设计原则。而且,win forms 全面支持快速简便地接入 web services 以及建立丰富的基于 ado+ 数据模型的数据感知应用程序。得益于 visual studio 中新的共享开发环境,开发人员能够使用包括 microsoft visual basic® 和 c# 在内的任何支持 .net 平台的语言创建 win forms 应用程序。
介绍 win forms
就像刚才所说的,win forms 是专用于 windows 客户机 ui 编程的 .net framework 的命名空间。它与 asp+ ui 程序包(即 web forms)共享同样的设计原则,但其类和实现却全然不同。在 microsoft win32® api 和 web 组件之间没有魔术般变形的类。就像所有的 .net frameworks 一样,一致性已成为优先考虑的问题。其目的是为了 win forms 开发人员能够迅速适应在 web forms 中编写代码,反之亦然。例如,所有命名空间都有 button 类,每一个都有文本、默认的 onclick 事件以及 forecolor、backcolor 和 font 属性。
win forms 的所有控件都基于 system.winforms.control 类。control 已内置了所有基本的 hwnd 功能,并且它能处理我们已经熟悉并喜爱的绝大多数通用 wm_xxxx 消息。richcontrol 由 control 派生而来,其中添加了布局逻辑和绘图代码。system.winforms 命名空间中的绝大多数控件实际上都由 richcontrol 派生而来。scrollablecontrol 能够支持窗口客户区域的滚动。一般情况下,对滚动功能的支持是通过 containercontrol 实现的,后者由 scrollablecontrol 派生而来,并增加了对管理子控件、焦点问题和跨栏的支持。form 由 containercontrol 派生而来,是 win form 的顶级控件,它带有控制标题栏、系统菜单、非矩形窗口和默认控件的属性。usercontrol 也由 containtercontrol 派生而来,是开发人员能够创建的控件的基本类。usercontrol 一般用于托管其它子控件,但对于外部客户机来说,它又是作为单个单元出现的。usercontrol 和 form 在 microsoft® visual studio.net 中都有可视设计器,您会找到用于添加和设计由其所派生的类的项。
图 1. win forms 控件层次结构
既然我们已了解 win forms 的(最)基本方面,让我们揭开它的面纱,看看其表面下的一些相当不错的功能。
更好的易学易用性
win forms 的主要目的是尽可能地提高定位到 win32 平台的开发人员的工作效率。无论是图形设备界面 (gdi) 还是窗口状态管理,为 win32 编程通常都是很困难的。例如,类似 ws_border 或 ws_caption 的一些窗口样式只能在创建窗口时指定或修改。而 ws_visible 或 ws_child 等其它窗口样式则可以对已创建的窗口进行修改。win forms 尽力消除了这些细微的差别,并确保操作过程始终保持一致性。可以随时地、不限次序地对 win forms 控件的属性进行设置,总能产生预期效果。如果改动过程需要创建新的 hwnd,win forms 框架能够自动地、透明地重新生成窗口,并为其应用相适宜的所有设置。
由控件获得通知或事件在 win forms 中也要容易得多。win forms 事件都基于称为 delegates 的一个通用语言运行时功能。delegates 从本质上讲是对类型安全的、可靠的函数指针。对于任一控件的任一事件,都可以添加代理处理程序;绝不会强迫您创建派生类以通过替代处理事件,创建事件映射,或仅为处理一个事件而为类的所有事件实施一个接口。也可以通过替代派生类处理事件,但这种方式一般用于控件创建者或更为高级的应用。汇集某一按钮的 click 事件相当简单:
public class buttonclickform: system.winforms.form {
private system.winforms.button button1;
public buttonclickform() {
// 创建按钮
button1 = new system.winforms.button();
// 添加处理程序
button1.addonclick(new system.eventhandler(button1_click));
// 将按钮添加到窗体中
this.controls.add(button1);
}
private void button1_click(object sender, eventargs e) {
messagebox.show("button1 clicked!");
}
}
这里,我们创建了一个按钮,并添加了一个名为 button1_click 的处理程序方法,通过短短几行代码,在单击该按钮后,将调用这一方法。请注意,即使处理程序方法被标记为专用,创建这一挂钩的代码仍可以使用该方法,单击按钮后,按钮将能够激活这一方法的事件。
启动 win forms 项目的过程也得到了简化。使用 visual studio.net 创建 win forms 项目的过程只会创建一个要编译的项目文件:form1.cs。没有头文件,没有接口定义文件,没有引导程序文件,没有资源文件,没有库文件。项目所需的所有信息都包含在窗体的代码中。这样做有一个好处:项目由一个简单的单窗体应用程序扩展到复杂的、带有多个代码文件的多窗体应用程序要方便得多。链接过程不需要中间对象文件,只有代码和已构建的、受管理的所有 dll。只要您习惯了这一方法,就能明显地感觉到创建 .net framework 应用程序和创建 c/c++ 应用程序之间复杂性的不同。因为信息仅仅包含在代码文件中,在 visual studio.net 环境外创建版本的过程也非常容易,无论是 visual basic 代码、c# 代码,还是任何其它语言编写的针对 .net framework 的代码。
因为 win forms 建立在通用语言运行时的基础之上,开发人员可以任选目前针对通用语言运行时的众多语言中的一种,构建 win32 应用程序。开发人员现在可以使用多种语言编写 win forms 应用程序(或 web forms 应用程序或 data 应用程序):从 c# 到 cobol 到 eiffel 再到 perl 等等,中间还有很多种(上一次计数是 17 种)。方便易用再加上广泛的应用场合相得益彰,为开发人员提供了深厚的基础,使他们能够迅速有效地使用 win forms 构建实用的应用程序。
布局
如果您曾尝试创建能够正常调整大小的窗体,您就会知道这一过程有多么困难。microsoft foundation classes (mfc) 或早期的 visual basic 版本没有对这一功能提供内置的支持。然而现在只需几行代码(通常情况下您甚至不需要编写这些代码,因为在设计时就能通过 property browser 实现这些功能!),即可创建能够正常调整大小的对话框。
基本布局由两条组成:anchoring 和 docking。richcontrol 有一个 anchor 属性,它是一种枚举类型,可以用“或”操作将这些值组合在一起,以说明控件将与其父控件的某一边保持恒定距离。例如,如果您将一个按钮置于窗体上,并将 anchor 属性设置为 anchorstyles.bottomright,则在调整按钮的大小时,按钮将与窗体的底边和右边保持同一距离。此外,如果将 anchor 设置为 anchorstyles.all,则按钮的各个边都与窗体的对应边保持同一距离,在调整按钮大小时仍要满足这些约束条件。
docking 实际上是 anchoring 的一个特殊情况。richcontrol 的 dock 属性说明控件要将自身固定到其父控件的哪一边。docking 可以是 top、left、right、bottom 或 fill。在每种情况下,控件都将移动到尽量靠近指定边,并调整其大小,以填满那一边。如果父控件的大小有所调整,这一状况仍将保持。将一个控件移动到父控件的底端,并将 anchor 设置为 anchorstyle.bottomleftright,可以模拟 docking bottom。在此处的示例中,列表框是 docked left,按钮与窗体的顶端、左边和右边保持恒定距离,由此它们保持了相对位置和大小。下面的示例对话框(图 2)完全使用 visual studio.net 中的 win forms 设计器创建,只花了两分钟的时间,没有编写一行代码。
图 2. 使用 win forms 设计器所创建的可调整大小的对话框
// resizablesample.cs
namespace resizablesamplenamespace {
using system;
using system.drawing;
using system.componentmodel;
using system.winforms;
/// <summary>
/// resizablesample 的摘要说明。
/// </summary>
public class resizablesample : system.winforms.form {
/// <summary>
/// 为 win forms 设计器所要求
/// </summary>
private system.componentmodel.container components;
private system.winforms.button button3;
private system.winforms.button button2;
private system.winforms.button button1;
private system.winforms.listbox listbox1;
public resizablesample() {
// 为 win form 设计器支持所要求
initializecomponent();
}
/// <summary>
/// 释放正在使用的所有资源
/// </summary>
public override void dispose() {
base.dispose();
components.dispose();
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
public static void main(string[] args) {
application.run(new resizablesample());
}
/// <summary>
/// 设计器支持所要求的方法 — 不要用编辑器
/// 修改这一方法的内容
/// </summary>
private void initializecomponent()
{
this.components = new system.componentmodel.container();
this.button2 = new system.winforms.button();
this.button3 = new system.winforms.button();
this.button1 = new system.winforms.button();
this.listbox1 = new system.winforms.listbox();
//@design this.traylargeicon = false;
//@design this.trayheight = 0;
this.text = "resizable dialog";
this.imemode = system.winforms.imemode.off;
this.autoscalebasesize = new system.drawing.size(5, 13);
this.clientsize = new system.drawing.size(256, 173);
button2.location = new system.drawing.point(152, 60);
button2.size = new system.drawing.size(92, 32);
button2.tabindex = 2;
button2.anchor = system.winforms.anchorstyles.topleftright;
button2.text = "cancel";
button3.location = new system.drawing.point(152, 120);
button3.size = new system.drawing.size(92, 44);
button3.tabindex = 3;
button3.anchor = system.winforms.anchorstyles.all;
button3.text = "filler";
button1.location = new system.drawing.point(152, 8);
button1.size = new system.drawing.size(92, 32);
button1.tabindex = 1;
button1.anchor = system.winforms.anchorstyles.topleftright;
button1.text = "ok";
listbox1.size = new system.drawing.size(120, 173);
listbox1.dock = system.winforms.dockstyle.left;
listbox1.tabindex = 0;
listbox1.items.all = new object[] {"item one",
"item two",
"item three",
"item four"};
this.controls.add(button3);
this.controls.add(button2);
this.controls.add(button1);
this.controls.add(listbox1);
}
}
}
gdi+
win forms 全面利用了 gdi+ 这一 microsoft 下一代的二维图形系统。win forms 中的图形编程模式完全是面向对象的,各式各样的画笔、笔刷、图像和其它图形对象与 .net framework 的其它部分一样,遵循了简单易用的指导方针。开发人员目前可以使用相当不错的一些绘图新功能,如 alpha 混色、渐变色、纹理、消除锯齿以及采用除位图外的其它图像格式。与 windows 2000 操作系统分层和透明的窗口功能配合使用,开发人员能够毫不费力地创建丰富的、更为图形化的 win32 应用程序。
如果触发了控件的 onpaint 事件,能够由 painteventargs 访问的 system.drawing.graphics 对象就成为一个 gdi+ 图形对象。图形对象能够执行的所有操作都通过 gdi+ 实施。作为一个示例,使用 gdi+ 创建一个绘制渐变背景的按钮。
图 3. 使用 gdi+ 创建的按钮
以下是实现这一按钮的代码:
public class gradientbutton : button {
// 保留颜色设置的成员
private color startcolor;
private color endcolor;
// 书写文字时我们将需要它
private static stringformat format = new stringformat();
public gradientbutton() : base() {
// 初始化颜色
startcolor = systemcolors.inactivecaption;
endcolor = systemcolors.activecaption;
format.alignment = stringalignment.center;
format.linealignment = stringalignment.center;
}
/// <summary>
/// 渐变色的终止颜色
// </summary>
public color endcolor {
get {
return this.endcolor;
}
set {
this.endcolor = value;
// 如有必要,则导致重新绘制
if (this.ishandlecreated && this.visible) {
invalidate();
}
}
}
/// <summary>
/// 渐变色的起始颜色
// </summary>
public color startcolor {
get {
return this.startcolor;
}
set {
this.startcolor = value;
// 如有必要,则导致重新绘制
if (this.ishandlecreated && this.visible) {
invalidate();
}
}
}
protected override void onpaint(painteventargs pe) {
// 绘制按钮的常规背景以形成
// 边框,等等。
base.onpaint(pe);
graphics g = pe.graphics;
rectangle clientrect = this.clientrectangle;
// 缩小矩形,以免绘制时出界
clientrect.inflate(-1,-1);
// 创建渐变笔刷,从
// 左上角运行到右下角。
brush backgroundbrush = new lineargradientbrush(
new point(clientrect.x,clientrect.y),
new point(clientrect.width, clientrect.height),
startcolor,
endcolor);
// 以渐变色填充背景....
g.fillrectangle(backgroundbrush, clientrect);
// 在客户机区域的中间书写文字。
g.drawstring(this.text,
this.font,
new solidbrush(this.forecolor),
clientrect,
format);
}
}
就像您所看到的,这并不是非常困难。得益于 win forms 和 gdi+ 面向对象的设计,无需编写任何复杂的代码,即可实现我们的 gradientbutton,并且在设计器中,可以通过 property browser 操作 text、font、startcolor 和 endcolor。
访问底层系统
许多框架的一个缺点就是:如果人们编写的应用程序类型与示例和演示中的严格一致,则这些框架的效果相当不错,但有时开发人员发现,一旦他们希望用框架进行一些有创造性的工作,某些情况下就会碰到障碍或遭到失败。如果遇到这一情况,win forms 框架自始至终都能够允许开发人员访问系统基础结构。当然,希望 win forms 这样一个设计优良的框架不会使用户遭遇这种情况,但可能发生的情况几乎是无限的。所有的控件都有 handle 属性,允许访问控件的窗口句柄 (hwnd),gdi 对象也提供了类似的句柄访问过程。而且,control 实际上拥有一个名为 wndproc 的受保护的虚拟方法,对于少数 win forms 尚不能支持的消息,可以替代该方法,添加处理方式。
例如,假设您的应用程序是资源密集型的,需要响应 wm_compacting。如果系统检测到内存不足,会向所有高层窗口广播 wm_compacting,您就会知道 win forms 框架对这一消息没有提供内置支持,由此,可以添加如下处理过程:
/// <summary>
/// win32form1 的摘要说明。
/// </summary>
public class compactableform : system.winforms.form {
private eventhandler handler;
public void addoncompacting(eventhandler h) {
handler = (eventhandler) delegate.combine(handler, h);
}
protected override void oncompacting(eventargs e) {
// 查看运行时系统能否释放任何东西
system.gc.collect();
// 调用任一处理程序。
if (handler != null) handler(this, e);
}
public void removeoncompacting(eventhandler h) {
handler = (eventhandler) delegate.remove(handler, h);
}
protected override void wndproc(ref message m) {
case (m.msg) {
case win.wm_compacting:
oncompacting(eventargs.empty);
break;
}
base.wndproc(m);
}
}
只需数行代码,当系统试着收集未用资源时,利用新的 compactableform 类或由此派生的类即可得到通知,并作出响应。
结论
尽管在许多开发人员的计划中,针对 web 的开发是当前工作的重点,而定位于熟悉的 win32 平台仍然是一个不得不面对的情况。有了 win forms,windows 开发人员无论是新手还是老手,都会发现使用丰富的接口创建复杂的应用程序是一个很方便的过程,而这些接口与 .net framework 中具有 web 和数据功能的许多技术配合良好。
通过利用跨语言继承、碎片收集和安全性等通用语言运行时提高工作效率的优秀功能,开发人员将从 .net framework 和 win forms 中获益。
新闻热点
疑难解答
图片精选