WPF系列 Application=Code + MarkUp (1) 应用程序和窗体
用WPF撰写的应用程序一般需要一些时间来创建应用程序和窗体的对象。一个简单的WPF应用程序应该是这样的:
SayHello.cs |
//----------------------------------------- // SayHello.cs (c) 2006 by Charles Petzold //----------------------------------------- using System; using System.Windows; namespace Petzold.SayHello { class SayHello { [STAThread] public static void Main() { Window win = new Window(); win.Title = "Say Hello"; win.Show(); Application app = new Application(); app.Run(); } } } |
我假设你对System的命名空间熟悉。(如果没有,你应该读我的在线书籍.NET Book Zero ,在http://www.charlespetzold.com/网站可以得到)
SayHello程序也使用了System.Window,它包含了WPF的全部基础类,数据结构,接口,委托和枚举类型,包括类Application和Window。其它WPF命名空间以System.Windows前缀开始,就像System.Windows.Controls,System.Window.Input和System.Window.Media.一个值得注意的特例是System.Window.Forms命名空间,一个主要的Winform Form 命名空间。以System.Winforms开头的命名空间都是Window Form的命名空间,但Winform.Forms不是。它包括的类会帮助你把Winform和WPF代码融为一体。
书里展示的例子都包括了一个固定的命名空间。每个程序都与一个Microsoft Visual Studio project关联。全部项目的代码都封装在一个定义好的命名空间下。我的名字的后半部分总会跟在project 的后面。....(省略)...
在任何的WPF程序中,[STAThread]属性必须位于Main之上,否则C#编译器会报错。这个属性指定了初始程序的线程的线程模式是一个单线程的模式,它需要与COM(the Component Object Model )交互。 “单线程”就旧的CMO时代,在.net时代前的编程模式,但是你可以猜想它意味着我们的应用程序不会使用多线程组织我们的运行环境。
在SayHello程序中,Main首先创建了Window类型的一个实例,那个是你用来创建一个标准Window应用程序窗口的类型。标题文本将会显示在窗体程序的标题栏。Show方法将会把窗体显示在屏幕上。
最后重要的一步是,调用一个新的Application实例的Run方法。在Window开发中,这个方法创建了消息循环,让程序能接收用户在键盘或鼠标的输入。如果程序运行在写字板PC上,应用程序也能接收写字板的输入。
创建程序的步骤:
1.在Visual Stdio 创建一个空项目。
2.在空项目中添加一个WPF应用程序。
3.再添加一个代码文件,把代码拷贝进去。
4.点击运行。
我在测试时,发现已经新增的WPF程序的Entry point有以下代码:
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
FrmProject.App app = new FrmProject.App();
app.InitializeComponent();
app.Run();
}
注释掉这段代码程序就能跑了。
在这本书第一部分展示的例子程序,创建对象的处理将会是基于同样的步骤,除非那些有多个源码的文件的project.
...省略一段(不重要).....
SayHello使用的对象Window和Application都是继承DispatcherObject类,但是Window继承的类层次更深,以下是类的继承层次:
Object
DispatcherObject (abstract)
Application
DependencyObject
Visual (abstract)
UIElement
FrameworkElement
Control
ContentControl
Window
当然我们不需要对类的继承顺序很熟悉,但是能使你了解Windows Presentation Foundation。
一个应用程序只能创建一个Application 对象,它就像余下程序的固定标记。Application对象是不可以显示的。Window对象在屏幕上展现出一个标准的Window窗口。...(省略不重要部分)..
在这些限制之下,你可以混淆sayHello程序的Main方法的语句顺序,程序将会正常运行。 比如你可以把设置Title属性的值放在Show方法之后。在理论上,
这样修改会导致窗口在显示没有标题显示。但是设置标题这个操作出现得太快了,快到连肉眼也看不这变化。
创建Application对象、Window对象的顺序可以不定,但是调用Run的方法一定要在最后。
我们可以不调用Window的Show方法,而是把Window对象当作参数传入Run方法中。
app.Run(win);
这样,Application对象就有责任调用Window对象的Show方法。
一个程序只有它调用了Run方法,程序才会启动运行;直到程序启动时,Window对象才会响应用户的输入。当用户关闭Window窗口并且Run方法返回,程序就准备结束了。一个程序花费了它的大部分时间在Run方法里。但是在Run期间,程序是怎么完成任何事情的?
在初始化之后,一个程序做的所有事情几乎都是对事件的响应。那些事件通常是用户从键盘、鼠标或写字版的输入。UIElement类定义了许多用户键盘、鼠标和写字版相关的事件。Window类继承了那些事件。有个事件的名称叫“MouseDown”。当用户在Window的显示区域按下鼠标,将会引发一个窗体的MouseDown事件。例子:
我喜欢用类名+On+事件名字来定义事件的名字。但是你可以使用你自己的命名方式去定义事件的名字。
MouseDowm事件是按照MouseButtonEventHandler来定义的,第一个参数是一个object类型,第二个参数是MouseButtonEventArgs类型。第二个参数是在System.Window.Input命名空间定义的。程序之所以要把the event handler 定义为static是因为Main是一个static方法。
书里面大多数的例子都使用了System.Windows.Input ,甚至有时候不需要使用它;
...(省略函数说明部分,这些都比较简单)....
HandleAnEvent里的事件handler使用object对象转换为Window对象,但是这里有另外一种方法去在event handler中获取到Window object对象。在Main方法中创建的对象能存放在一个static字段中,这样event handler就可以使用它了。或者,可以使用Application 类的一个静态属性Current 来返回程序创建的Applicatiion(正如我提到的一个程序只能创建一个Application)。Application对象一个名叫MainWindow的对象返回一个Window对象。那么event handler能够像这样设置一个局部变量:
Window win = Application.Current.MainWindow;
获取Window的目的是为了能在message box显示Window的标题栏位的值。
Application类定义的几个方法或许会有用。当调用了Application的Run方法后,protected OnStartUp方法事件就会很快地被通过StartUp事件。当Run方法返回时,将会调用OnExit方法(并且执行相应的Exit事件)。你能使用这两个方法进行Application的初始化和清空.
OnSessionEnding方法和SessionEnding事件说明了用户已经选择注销应用程序或者关闭计算机。事件传递了一个SessionEndingCancelEventArgs类型的变量。一个类来源于CancelEventArgs ,它包括了一个叫Cancel的属性。如果你的想阻止应用程序关闭,设置Cancel的值为true;
如果你的应用程序想处理Application的一些事件,它能从众多事件中,设置一些事件。 但是最方面的是定义一个类继承于Application. 在下个例子中,InheritTheApp类是继承了Applcaition。并override 了一些方法。
....(省略掉解释代码部分)...
你可以从命令提示窗口中运行程序,并且如果你这样你能把命令行参数给程序。Window程序也没有不同。为了处理命令行参数,你可以定义Main方法如下:
public static void Main(string[] args)
任何命令行参数传递以数组的方式传到Main。在OnStartUp方法中,StartupEventArgs 关联到命令行的字符数组。
Application对象的MainWindow属性,暗示一个程序能有多个窗体;这当然是对的。
Example:
...(省略程序说明部分)..
Application对象包含了一个名叫Windows 的属性,类型为WindowCollection。WindowCollection类型实现了ICollection接口和IEnumerable接口存储多个Window对象。WindowCollection有Count属性和Indexer属性,让你获取你的程序中调用了Show方法和仍然存在的对象。
运行程序后,我们发现三个程序都显示在Windows taskbar中。我们可以通过设置属性ShowInTaskbar 使窗口不在Windows taskbar中出现。
在额外生成的窗体中,设置如下属性:
win.ShowInTaskbar = false;再运行,你会发现其余两个窗体没有显示在Windows taskbar中;
但是你会发现其他更特别的事情;你先关闭了“Main Window”窗体,你会看到任务栏中的程序已经消失了,但是程序依然在运行并显示其它两个窗口。当Run方法返回后,程序会结束。默认地,用户关闭了最后一个窗体,Run方法才会返回。这个属性被Application的ShutdownMode 控制。默认情况下ShutdownMode 的值是OnLastWindowClose 。我们可以指定ShutdownMode 的值为OnMainWindowClose。设置如下属性:
app.ShutdownMode = ShutdownMode.OnMainWindowClose;
我们也可以设置最后一个显示的窗体为主窗体,那么我们关闭了当前窗体口,整个程序就会结束;
MainWindow = win;
这里还有第三种关闭窗体的模式,ShutdownMode.OnExplicitShutdown. 只有当程序调用了Application的ShutDown方法后才会返回;
这里还有另外一种方式建立多个窗体的层次关系,通过使用Window类型的Owner属性。默认情况下,这个属性的值会是null,意味着窗体没有没有Owner;你可以在循环中加入以下代码并把之前加入的代码删掉;
win.Owner = winMain;
技术交流群:17035471
路途遥远 2011.4