目標(biāo):
1.一個(gè)簡(jiǎn)單的WPF應(yīng)用
2.WPF中的主窗體
3.Application的生命周期
4.單實(shí)例運(yùn)行WPF應(yīng)用
WPF應(yīng)用程序是一種包含Application對(duì)象的Windows進(jìn)程,Application對(duì)象提供了生命周期服務(wù),因此要了解WPF應(yīng)用的生命周期我們就需要從Application開(kāi)始。
首先我們建立一個(gè)WPF應(yīng)用,在默認(rèn)情況下我們運(yùn)行這個(gè)應(yīng)用程序。
我們使用默認(rèn)WPF Application創(chuàng)建了一個(gè)WPF應(yīng)用,默認(rèn)情況下我們什么都不做,點(diǎn)擊運(yùn)行就會(huì)看到上面的窗口。那么這背后Visual Studio為我們做了什么呢?我們知道在Winform中有一個(gè)Program.cs,其中定義了Main函數(shù),程序從Main開(kāi)始執(zhí)行,那么WPF有沒(méi)有類(lèi)似的函數(shù)呢?我們的MainWindow又是在何處指定運(yùn)行的?
我們可以看到VS為我們自動(dòng)生成了一個(gè)App.xaml及其對(duì)應(yīng)的隱藏文件App.xaml.cs。在App.xaml.cs中我們可以看到它沒(méi)有創(chuàng)建任何類(lèi),更沒(méi)有啟動(dòng)MainWindow,那么打開(kāi)App.xaml呢?打開(kāi)App.xaml文件代碼如下:
1 <Application x:Class="WPFLifeCycle.App"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 StartupUri="MainWindow.xaml" ShutdownMode="OnLastWindowClose">
5 <Application.Resources>
6
7 </Application.Resources>
8 </Application>
在這里我們看到x:Class="WPFLifeCycle.App",事實(shí)上這樣的代碼等同于創(chuàng)建了一個(gè)名為App的Application對(duì)象。而根節(jié)點(diǎn)Application的StartupUri屬性指定了啟動(dòng)的窗口(StartupUri="MainWindow.xaml"),這就相當(dāng)于創(chuàng)建了一個(gè)MainWindow類(lèi)型的對(duì)象,然后調(diào)用其Show()方法。此時(shí)可能會(huì)有朋友問(wèn),按理說(shuō)這個(gè)程序應(yīng)該有個(gè)Main函數(shù)啊,為何在此看不到Main呢?程序如何創(chuàng)建Application?
其實(shí),這一切都?xì)w功于App.xaml文件的一個(gè)屬性BuildAction
BuildAction屬性指定了程序生成的方式,默認(rèn)為ApplicationDefinition。對(duì)于WPF程序來(lái)說(shuō),如果指定了BuildAction為ApplicationDefinition之后,WPF會(huì)自動(dòng)創(chuàng)建 Main函數(shù),并且自動(dòng)檢測(cè)Application定義文件,根據(jù)定義文件自動(dòng)創(chuàng)建Application對(duì)象并啟動(dòng)它(當(dāng)然它會(huì)根據(jù)StartupUri創(chuàng)建MainWindow并顯示)。
既然如此我們將BuildAction設(shè)置為None試試,我們運(yùn)行會(huì)發(fā)現(xiàn)拋出如下錯(cuò)誤:
很明顯這時(shí)我們需要Main函數(shù)作為我們的程序入口,我們不妨手動(dòng)創(chuàng)建試試,此時(shí)我們創(chuàng)建一個(gè)Program.cs類(lèi),代碼如下:
1 using System;
2 using System.Windows;
3
4 namespace WPFLifeCycle
5 {
6 static class Program
7 {
8 [STAThread]
9 static void Main()
10 {
11 Application App = new Application();
12 MainWindow mw = new MainWindow();
13 mw.Show();
14 App.Run();
15 }
16 }
17 }
運(yùn)行之后我們看到的效果和之前完全一樣。換句話(huà)說(shuō)App.xaml文件和我們上面代碼起到的效果是相同的,事實(shí)上上面的xaml代碼在編譯時(shí)編譯器也會(huì)做出同樣的解析,這也是WPF設(shè)計(jì)的一個(gè)優(yōu)點(diǎn)--很多東西我們都可以在XAML中實(shí)現(xiàn)而不需要編寫(xiě)過(guò)多的代碼。
備注: App.xaml幫我們做的工作具體如下:
a.創(chuàng)建Application對(duì)象,并且設(shè)置其靜態(tài)屬性Current為當(dāng)前對(duì)象
b.根據(jù)StartupUri創(chuàng)建并顯示UI
c.設(shè)置Application的MainWindow屬性(主窗口)
d.調(diào)用Application對(duì)象的Run方法,并保持一直運(yùn)行直到應(yīng)用關(guān)閉
我們知道在Winform中我們有"主窗體"概念,在WPF中我們也同樣有"主窗口"。"主窗口"是一個(gè)"頂級(jí)窗口",它不包含或者不從屬于其他窗口。默認(rèn)情況下,創(chuàng)建了Application對(duì)象之后會(huì)設(shè)置Application對(duì)象的MainWindow屬性為第一個(gè)窗口對(duì)象來(lái)作為程序的"主窗口"。當(dāng)然,如果你愿意這個(gè)屬性在程序運(yùn)行的任何時(shí)刻都是可以修改的。
在Winform中我們知道,主窗體關(guān)閉之后整個(gè)應(yīng)用程序生命周期就會(huì)結(jié)束,這里我們不妨試試在WPF中是否如此。首先在應(yīng)用程中添加另一個(gè)Window對(duì)象OtherWindow,然后在MainWindow中放一個(gè)按鈕,點(diǎn)擊按鈕顯示OtherWindow。運(yùn)行效果如下:
現(xiàn)在點(diǎn)擊關(guān)閉MainWindow之后我們發(fā)現(xiàn)OtherWindow并未關(guān)閉,當(dāng)然Application并未結(jié)束:
這是不是說(shuō)明Application關(guān)閉同Winform不同呢(當(dāng)然我們調(diào)用Application.Current.Exit()是可以退出應(yīng)用的)?在WPF中Application的關(guān)閉模式同Winform確實(shí)不同,WPF中應(yīng)用程序的關(guān)閉模式有三種,它由Application對(duì)象的ShutdownMode屬性來(lái)決定
它的枚舉值如下:
枚舉名稱(chēng) | 枚舉值 | 說(shuō)明 |
OnLastWindowClose | 0 | 當(dāng)應(yīng)用程序最后一個(gè)窗口關(guān)閉后則整個(gè)應(yīng)用結(jié)束 |
OnMainWindowClose | 1 | 當(dāng)主窗口關(guān)閉后則應(yīng)用程序結(jié)束 |
OnExplicitShutdown | 2 | 只用通過(guò)調(diào)用Application.Current.Shutdown()才能結(jié)束應(yīng)用程序 |
從上圖我們也可以看到默認(rèn)情況下ShutdownMode值是OnLastWindowClose,因此當(dāng)MainWindow關(guān)閉后應(yīng)用程序沒(méi)有退出,如果要修改它可以將光標(biāo)放到App.xaml中的XAML編輯窗口中,然后修改屬性窗口中的ShutdownMode,也可以在XAML中或者程序中設(shè)置ShutdownMode屬性。
下面我們看看Application的生命周期(引用網(wǎng)上一張圖片)
上圖片描述WPF應(yīng)用的生命周期,其中值得一提的是Run方法后會(huì)調(diào)用應(yīng)用程的Starup事件,而"已激活"、"已停用"分別對(duì)應(yīng)Activated和Deactivate事件。DispatcherUnhandledException用來(lái)將事件路由到正確位置的對(duì)象,包括未處理的異常,可以用它來(lái)處理程序其他部分未處理的異常或者一些操作(例如保存當(dāng)前文檔)。當(dāng)關(guān)閉、注銷(xiāo)或者重新啟動(dòng)時(shí)則會(huì)觸發(fā)SessionEnding事件,SessionEnding事件中的SessionEndingCancelEventArgs的ReasonSessionEnding屬性可以指示你是執(zhí)行了注銷(xiāo)還是關(guān)閉(這是一個(gè)枚舉屬性)。
雖然上面我們簡(jiǎn)單介紹了WPF應(yīng)用的生命周期,但是默認(rèn)情況下我們可以打開(kāi)一個(gè)應(yīng)用程序多個(gè)實(shí)例,例如你雙擊一個(gè)exe多次。當(dāng)然有些時(shí)候這么做會(huì)帶來(lái)很多好處,但是有時(shí)我們又不希望這么做,要避免這個(gè)問(wèn)題其實(shí)很簡(jiǎn)單,同WinForm中單實(shí)例運(yùn)行一個(gè)應(yīng)用是一樣的,我們只需要在應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建一個(gè)"排他鎖",修改App.xaml.cs如下:
1 using System;
2 using System.Windows;
3 using System.Threading;
4
5 namespace WPFLifeCycle
6 {
7 /// <summary>
8 /// Interaction logic for App.xaml
9 /// </summary>
10 public partial class App : Application
11 {
12 Mutex mutex=null;
13 protected override void OnStartup(StartupEventArgs e)
14 {
15 base.OnStartup(e);
16 bool createdNew = false;
17 mutex = new Mutex(true, "WPFLifeCycle",out createdNew);
18 if (!createdNew)
19 {
20 MessageBox.Show("程序正在運(yùn)行中,無(wú)法啟動(dòng)另一個(gè)實(shí)例!", "系統(tǒng)提示", MessageBoxButton.OK, MessageBoxImage.Warning);
21 this.Shutdown();
22 }
23 }
24 }
25 }
此時(shí)如果我們已經(jīng)運(yùn)行了WPFLifeCycle.exe,當(dāng)再雙擊此應(yīng)用則會(huì)給出提示:
聯(lián)系客服