- Windows程序设计与架构
- 蔺华 汤春林 蔡兴旺编著
- 2844字
- 2020-08-28 17:44:33
1.3 Windows窗体模型设计
下面开始进入Windows窗体开发的精彩世界。
像很多编程语言的讲解一样,这里也从最简单的“Hello World”程序开始,下面是Windows Form版的“Hello World”程序。
1.3.1“Hello World”程序
开始使用Windows窗体非常简单。下面示例运行后将显示一个顶级窗口(称为窗体),并将标题栏文本设置为“Hello World”。
namespace Microsoft.Samples.WinForms.Cs.SimpleHelloWorld { using System; using System.Windows.Forms; public class SimpleHelloWorld : Form { [STAThread] public static int Main(string[] args) { Application.Run(new SimpleHelloWorld()); return 0; } public SimpleHelloWorld() { this.Text = "Hello World"; } } }
没错,就是这么简单。OK,读者已经学会了最简单的Windows窗体程序,下面探讨Windows窗体应用程序模型。
1.3.2 Windows窗体应用程序模型
Windows窗体的应用程序编程模型主要由窗体、控件及其事件组成。本主题涉及Windows窗体应用程序模型的以下方面。
1.窗体
在Windows窗体中,Form类是在应用程序中显示的任何窗口的表示形式。可以使用Form类的BorderStyle属性创建标准窗口、工具窗口、无边框窗口和浮动窗口。还可使用Form类创建有模式窗口,如对话框。通过设置Form类的MDIContainer属性,可以创建一种特殊类型的窗体MDI窗体。MDI窗体可以在其工作区内包含名为MDI子窗体的其他窗体。Form类为键盘处理(Tab键顺序)和滚动窗体的内容提供内置的支持。
当为应用程序设计用户界面时,通常创建一个从Form派生的类。然后可以添加控件、设置属性、创建事件处理程序,以及向窗体添加编程逻辑。
2.控件
添加到窗体中的每个组件(如Button、TextBox或RadioButton)称为控件。Windows窗体包括通常与Windows关联的所有控件及类似Windows窗体DataGrid的自定义控件。
通常可以通过设置属性与控件进行交互,以更改其外观和行为。例如,下面的Form的派生类向窗体添加一个Button控件,并设置该控件的Size和Location。
public class HelloWorldForm : System.Windows.Forms.Form { private Button button1 = new Button() ; private TextBox textBox1 = new TextBox(); [STAThread] public static int Main(string[] args) { Application.Run(new HelloWorldForm()); return 0; } public HelloWorldForm() { this.Text = "Hello Windows Forms World"; this.AutoScaleBaseSize = new Size(5,13); this.ClientSize = new Size(392,117); this.MinimumSize = new Size(392,(117 + SystemInformation.CaptionHeight)); this.AcceptButton=button1; button1.Location = new Point(256,64); button1.Size = new Size(120,40); button1.TabIndex = 2; button1.Text = "Click Me!"; button1.Click += new System.EventHandler(button1_Click); textBox1.Text = "Hello Windows Forms World"; textBox1.TabIndex = 1; textBox1.Size = new Size(360,20); textBox1.Location = new Point(16,24); this.Controls.Add(button1); this.Controls.Add(textBox1); } }
窗体对于何时可设置控件的属性提供有限的限制。控件没有阻止更新其状态的模式。创建控件的新实例后,可以立即更改其状态。例如,下面的代码提供两个示例,演示创建Button控件的有效方法。
Button button1 = new Button(); button1.Location = new Point(256,64); button1.Size = new Size(120,40); button1.TabIndex = 1; button1.Text = "Click Me!"; this.Controls.Add(button1); Button button1 = new Button(); this.Controls.Add(button1); button1.Location = new Point(256,64); button1.Size = new Size(120,40); button1.TabIndex = 1; button1.Text = "Click Me!";
Windows窗体确保用户创建的代码是有效的。例如,如果设置一个Windows控件的Windows样式位的属性(该属性仅可在创建控件时设置),则Windows窗体控件放弃基础Windows控件,并创建一个新控件。只有在不使用Windows窗体而直接访问控件的基础HWND时,此功能才有可能不合需要。用户无法保持对HWND的引用,因为代码中所设置的属性可能使其无效。
3.事件
Windows窗体编程模型基于事件。当控件更改状态,如当用户单击按钮时,它引发一个事件。为了处理事件,应用程序为该事件注册一个事件处理方法。在Visual Basic中,有以下两种途径可以注册事件处理方法。
- 如果使用WithEvents关键字声明控件变量,可以在方法的声明中使用Handles关键字,将该方法注册为事件处理方法。
- 可使用AddHandler在运行时注册事件处理方法。
下面的代码阐释注册事件处理方法的两种途径。
…… //Create the button Button button1 = new Button() ; button1.Location = new Point(256,64); button1.Size = new Size(120,40); button1.Text = "Click Me!"; this.Controls.Add(button1); //Register the event handler button1.Click += new System.EventHandler(button1_Click); …… //The event handling method private void button1_Click(object sender,EventArgs evArgs) { MessageBox.Show("Hello Windows Forms World!"); }
只对特定控件的特定事件调用某个事件处理方法。这使得可以避免窗体中出现处理所有控件的所有事件的单个方法。此功能还使代码更易理解和维护。而且,因为Windows窗体事件结构基于委托,所以事件处理方法是类型安全的并可以声明为私有的。此功能使编译器得以在编译时检测方法签名不匹配情况。它还使Form类的公共接口不与公共事件处理方法相混淆。读者可在.NET框架SDK文档中找到关于委托的更多信息。
4.事件类
每个事件有两个支持类,如下所示:
- 用于注册事件处理方法的EventHandler委托类。EventHandler的签名指示事件处理方法的签名。
- 包含有关引发的事件的数据的EventArgs类。
EventHandler的签名为:第一个参数包含对引发事件的对象(发送方)的引用,第二个参数包含关于该事件(EventArgs的一个实例)的数据。例如,Button上的Click事件使用以下事件处理程序。
public delegate void EventHandler(object sender,EventArgs e);
因此,Click事件的任何事件处理方法都必须具有以下签名。
<access> void <name>(object sender,EventArgs evArgs)
对于强类型语言,如果事件处理方法的签名与委托签名不匹配,将发生编译时错误。
很多事件使用一般的EventHandler和EventArgs类。但是,一些事件要求包含针对所引发事件的类型的附加信息。例如,鼠标移动事件包括有关鼠标指针或鼠标按钮位置的信息。这些事件定义其自己的类,这些类必须从EventHandler和EventArgs类继承。例如,MouseDown事件使用MouseEventHandler和MouseEventArgs类。
5.事件命名约定
可以在特定种类的状态更改之前和之后引发事件。在状态更改前引发的事件通常带有后缀“ing”。在状态更改后引发的事件通常带有后缀“ed”。例如,SessionEnding事件是状态更改前引发的,SessionEnded事件是状态更改后引发的。如果某状态更改仅导致一个事件被引发,则该事件通常没有后缀。例如,Click事件。
6.可取消的事件
根据应用程序中的情况,可能需要取消某个事件。某些事件可以取消。这些事件使用CancelEventHandler和CancelEventArgs类。CancelEventArgs类包含名为Cancel的属性。如果此属性设置为true,那么当该事件处理方法返回时,将取消该事件。通常,只有在状态更改前引发的事件才是可以取消的。取消事件将取消状态更改。
7.用一个事件处理方法处理多个事件
如果要用一个事件处理程序处理多个事件,可通过将同一方法注册到多个事件来实现。每个事件都必须具有相同的签名。当对多个事件使用一个事件处理方法时,可以从sender参数确定哪个控件引发了事件。下面的示例阐释处理来自两个按钮控件的事件的单个事件处理方法。
…… Button button1 = new Button() ; Button button2 = new Button() ; …… button1.Click += new System.EventHandler(button_Click); button2.Click += new System.EventHandler(button_Click); …… //The event handling method private void button_Click(object sender,EventArgs evArgs) { if (sender==button1) { MessageBox.Show("Button1 Pushed!"); } else if (sender==button2) { MessageBox.Show("Button2 Pushed!"); } }
1.3.3 Windows窗体中的动态布局
在调整窗体大小时,可动态调整该窗体上控件的大小。
Windows窗体提供三种控制窗体布局的方法,如下所示:
- 锚定
- 停靠
- 自定义
如果将控件锚定到其容器的边缘,当调整该容器的大小时,该控件与指定边缘间的距离保持不变。控件可锚定到任意组合的容器边缘。如果将控件锚定到其容器的相对边缘,则在调整该容器大小时调整该控件的大小。
例如,如果将TextBox控件锚定到窗体的左右边缘,则当调整窗体的大小时,TextBox的宽度发生变化。
下面的代码将TextBox控件锚定到其容器的上、左和右边缘。
textBox1.Anchor = AnchorStyles.Top BitOr AnchorStyles.Left BitOr AnchorStyles. Right
如果控件停靠到其容器的边缘,则当调整该容器的大小时,该控件仍与该边缘保持接触。例如,在Windows资源管理器中,TreeView控件停靠在窗口的左边缘,ListView控件停靠在窗口的右边缘。如果多个控件停靠到一个边缘,则第一个控件停靠到容器的边缘,而其他控件在不覆盖其他控件的情况下停靠得尽可能离该边缘近。
下面的代码将Panel控件停靠在容器的左边缘。
Panel1.Dock = DockStyle.Left;
可以使用在Control类上公开的Layout事件编写自己的布局管理器。当任何导致控件显示子控件的事件发生时,都引发此事件。这些事件包括Resize、显示/隐藏子控件,以及添加/移除子控件。应使用此事件执行任何自定义布局逻辑。