首先先通過控制檯直接生成一個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