首先先通过控制台直接生成一个WPF窗口,从低层去认识WPF窗口的实质。新建一个c#控制台程序,添加引用:Windowsbase,PresentationCore,PresentationFramework以及System.Axml。这样我们才能通过控制台去生成一个WPF应用程序,然后在cs文件里面引入using System.windows; 和using System.windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace ConsoleForWPF
{
class Program
{
[STAThread]
static void Main( string[] args)
{
Window window = new Window();
window.Title = "ConsoleForWPF Application";
window.Show();
}
}
}
如上面的代码所示,运行该程序,可以看到两个窗口一闪而过,要注意得是在Main方法前面我们要声明[STAThread],因为应用程序的COM线程模型是单线程单元[STA],如果不声明这个你可以看到会发生异常。为了让窗口停留,我们声明一个APP应用程序对象。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace ConsoleForWPF
{
class Program
{
[STAThread]
static void Main( string[] args)
{
Window window = new Window();
window.Title = "ConsoleForWPF Application";
window.Show();
Application app = new Application();
app.Run(window);
}
}
}
这时,我们可以看到可以通过控制台生成我们需要的WPF程序了。接下来深入的去了解Application这个类以及app.Run函数的执行过程。
首先来了解下WPF中Application这个类,Application是一个地址空间,在WPF中应用程序就是在System.Windows命名空间下的一个Application实例。一个应用程序只能对应一个Application的实例,而Application的生命周期自然是从应用程序启动到终止的周期。
Application类的创建有两种方式,一种是显式的,一种是隐式的,像上面的控制台那个是显式的创建,在创建Application对象的时候,会对Application静态属性Current赋予新创建的对象,所以app.run()也可以替换成Application.Current.Run()。而隐式的创建我们早已接触到,就是我们通过VS新建一个WPF项目时,我们可以看到的那个App.xaml,打开App.xaml的后台文件我们就可以看到,在代码中定义一个继承于Application的类,并在类中重写Application启动时(Run方法调用时)出发的OnStartup事件对应的方法,将主窗体的实例化放在该方法中.事实上WPF中会自动创建Main函数,并依照工程文件中指定的相关Application对应所在文件app.xaml,实例化该应用程序类,并调用Run方法.除了在OnStartup时间对应分方法中指定实例化的主窗体,也可以在app.xaml文件中直接指定程序运行时实例化并显示的主窗口。
然后我们看下那个app.Run函数,函数原型是:
EventTrace.NormalTraceEvent(EventTraceGuidId.APPRUNGUID, 0);
return this.Run( null);
}
在上面的代码中我们可以看到EventTrace.NormalTraceEvent,这里启动了信息路由(路由的详细说明,再次强烈地觉得MSDN是个好东西)。再看下面的代码:
[SecurityCritical]
public int Run(Window window)
{
base.VerifyAccess();
if (InBrowserHostedApp())
{
throw new InvalidOperationException(SR.Get("CannotCallRunFromBrowserHostedApp"));
}
return this.RunInternal(window);
}
internal static bool InBrowserHostedApp()
{
return (BrowserInteropHelper.IsBrowserHosted && !(Current is XappLauncherApp));
}
其中,VerifyAccess判断访问这个WPF的线程是不是主线程,如果不是就抛出异常。通过InBrowserHostedApp函数判断当前程序是否是一个浏览器应用程序而不是一个Xaml应用程序。如果,是一个浏览器应用程序不是一个Xaml应用程序,就抛出一个异常。
到这里还只是进行到检测运行环境而已,更重要的代码在RunInternal函数中,这个函数为应用程序构造了一个消息循环。对于上面的程序你在运行的时候他会弹出两个窗口,一个是WPF窗口,一个是控制台窗口。这是因为编译器自己为这个程序添加的,我们可以重写代码让程序只弹出一个WPF窗口。
这时,我想,如果在Main函数里面定义两个Window实例,那么app.Run会执行哪一个window实例呢?事实上当你这样做的时候,两个实例都会被执行,并且,只要你关闭了其中任意一个窗口另外一个窗口也会随之关闭。这时我们需要去了解Application类的关闭模式。Application的关闭模式有三种,分别为OnMainWindowClose、OnLastWindowClose和OnexplictShutDow。通过查看MSDN可以得知,三者的属性如下:
而ShutdownModel默认的枚举类型是OnLastWindowClose,所以当我们定义了两个窗口时,只要关闭其中一个或者在其中一个响应了Shutdown函数,整个应用程序就会关闭,而当我们设定app.ShutdownMode =ShutdownMode.OnMainWindowClose;或者OnExplicitShutdown时,关闭了其中一个窗口另外一个则不受影响。
以上就是大体上对于一个Application的创建及退出的一些深层次的分析,再接下来分析一下WPF窗口的响应事件。一旦初始化了,窗口做的事情就是响应事件,这些事件通常是一些用户输入、点击之类的。其中,应用程序在UIElement中实现了这些事件的委托,而Windows继承了它的事件。如下面的代码,我们为新建立的window添加一个响应事件:
static void Main( string[] args)
{
Window window1 = new Window();
window1.Title = "ConsoleForWPF Application 1";
window1.MouseDown += new MouseButtonEventHandler(win_MouseDown);
window1.Show();
Application app = new Application();
app.ShutdownMode =ShutdownMode.OnMainWindowClose;
app.Run();
}
static void win_MouseDown( object sender, MouseButtonEventArgs e)
{
Window win = sender as Window;
if (win != null)
{
MessageBox.Show( "mousebuttonDown", win.Title);
}
}
我们通过Window win = sender as Window来获取发出这个事件的窗口实例,其实,除了这个方式,我们还可以通过MessageBox.Show("mousebuttonDown", Application.Current.MainWindow.Title);,即Application.Current.MainWindow来获得当前运行程序的窗口实例。
对于Application类来说,一旦Application的run方法被调用,就会发出一个onStartup事件,一旦run方法返回就会发出一个onExit事件.如果程序需要处理这些事件,可以给它添加对应的响应,或者你直接继承Application,然后再重写这些函数,如下面的代码,我们重要了解onStartup函数,直接在该函数里面生成WPF窗口。
{
base.OnStartup(e);
//定义在初始化的时候调用一下代码
//如果此处初始化了多个窗口,系统会默认第一个调用show方法的窗口时主窗口
Window window = new Window();
window.Title = "ConsoleForWPF Application";
window.Show();
}
/// <summary>
/// 在操作系统选择注销或者关闭的时候会触发应用程序调用这个事件
/// 默认情况下是直接退出应用程序
/// /// </summary>
/// /// <param name="e"></param>
protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
{
base.OnSessionEnding(e);
MessageBoxResult mb = MessageBox.Show( "Do you want to save your data?", MainWindow.Title, MessageBoxButton.YesNo, MessageBoxImage.Question);
e.Cancel = (mb == MessageBoxResult.Yes);
}
正如前面我们演示过那样,通过定义多个Window我们可以获取多个窗口实例,这时我们可以留意下Windows这个属性,Windows属性则是存储了所有的窗体的一个集合,它包含应用程序中所有的窗体资源。如下我们定义多个窗口,则可以通过下面的代码来实现依次访问各个窗口:
{ foreach (Window win in Windows)
{ MessageBox.Show( String.Format( "Sender window is {0},Main window is {1}", win.Title, MainWindow.Title));
}
}
到这里对WPF窗口的讨论暂告一段落,有新的学习成果再记录下来。
本文来自Ellic的博客,原文地址:http://www.cnblogs.com/libenqing/archive/2011/04/07/2007817.html