上一章介紹了有關WPF應用程式中使用Application物件的方式,接下來看一下如何使用Application物件來處理一些更普通的情況,接下倆介紹如何初始化界面、如何處理命名行引數、如何處理支付視窗之間的互動、如何添加跟蹤檔案以及如何創建單示例應用程式,
一、顯示初始化界面
WPF應用程式的運行速度快,但并不能在瞬間啟動,當第一次啟動應用程式時,會有一些延遲,因為公共語言運行時(Common Language Runtime,CLR)首先需要初始化.NET環境,然后啟動應用程式,
這一延遲未必成為問題,通常,只需要經歷很短的時間,就會出現第一個視窗,但如果具有更耗時的初始化步驟,或者如果只是希望通過顯示打開的影像應用程式顯得更加專業,這時可使用WPF提供的簡單初始界面特性,
下面是添加初始界面的方法:
(1)為專案添加影像檔案(通常是.bmp、.png或.jpg檔案),
(2)在Solution Explorer中選擇影像檔案,
(3)將Build Action修改為SplashScreen,
下次運行應用程式時,影像會立即在螢屏中央顯示出來,一旦準備好運行時環境,而且Application_Startup方法執行完畢,應用程式的第一個視窗就將顯示出來,這時初始化界面影像會很快消失(約需300毫秒).
該特性聽起來很簡單,事實上也確實如此,只需要記住顯示的初始化界面沒有任何裝飾,在它周圍沒有視窗邊框,所有由你決定是否為初始界面影像添加邊框,也無法通過顯示一系列的多幅影像或影片讓初始化影像顯得更富有想象力的效果,如果希望得到這種效果,需要采用傳統方法:創建在運行初始化代碼的同事顯示你所希望的影像界面的啟動視窗,
順便提一下,當添加初始界面時,WPF編譯器會自動生成的App.g.cs檔案添加與下面的類似的代碼:
SplashScreen splashScreen = new SplashScreen("images/blue.jpg"); //Show the splash screen //The true parameter sets the splashScreen to fade away automatically //after the first window appears splashScreen.Show(true); //Start the application AssemblyResources.App app = new AssemblyResources.App(); app.InitializeComponent(); app.Run(); //The splash screen begins ites automatic fade-out now.
也可自行撰寫這一剪短邏輯,而不死使用SplashScreen 生成操作,但有一點需要指出,可以改變的唯一細節是初始界面褪去的速度,為此,需要項SplashScreen.Show()方法傳遞false(從而使WPF不會自動淡入初始化界面),然后由你負責通過呼叫SplashScreen.Close()方法在恰當的時機隱藏初始界面,并提供TimeSpan值來指示經過多長時間淡出初始化界面,
二、處理命令列引數
為處理命令列引數,需要相應Application.Startup事件,命令列引數是通過StartupEventArgs.Args屬性作為字串陣列提供的,
例如,假定希望加載檔案,檔案名作為命令列引數傳遞,在這種情況下,有必要讀取命令列引數并執行所需的一些額外初始化操作,在下面的示例中,通過回應Application.Startup事件實作了這一模式,在該例中,沒有在任何地方設定Application.StartupUri屬性——而是使用代碼實體化視窗,
public partial class App : Application { // The command-line argument is set through the Visual Studio // project properties (the Debug tab). private void App_Startup(object sender, StartupEventArgs e) { // At this point, the main window has been created but not shown. FileViewer win = new FileViewer(); if (e.Args.Length > 0) { string file = e.Args[0]; if (File.Exists(file)) { // Configure the main window. win.LoadFile(file); } } // This window will automatically be set as the Application.MainWindow. win.Show(); } }
<Application x:Class="LoadFromCommandLine.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Startup="App_Startup"> <Application.Resources> </Application.Resources> </Application>App.xaml
上面的方法初始化主視窗,然后當App_Startup()方法結束是顯示主視窗,上面的代碼假定FileViewer類有名為LoadFile()的共有方法,這只是一個示例,它只讀取并顯示指定檔案的文本,
三、訪問當前Application物件
通過靜態的Application.Current屬性,可在應用程式的任何位置獲取當前應用程式實體,從而在視窗之間進行基本互動,因為任何視窗都可以訪問當前Application物件,并通過Application物件,并通過Application物件獲取主視窗的作用:
Window main=Application.Current.MainWindow; MessageBox.Show("The main window is "+main.Title);
當然,如果希望訪問在自定義主視窗類中添加的任意方法、屬性或事件,需要將視窗物件轉換為正確型別,如果主視窗的自定義類MainWindow的實體,可使用與下面類似的代碼:
MainWindow main=(MainWindow)Application.Current.MainWindow;
main.DoSomething();
在視窗中還可以檢查Application.Windows集合的內容,該屬性提供了所有當前打開視窗的參考:
foreach(Window window in Application.Current.Windows) { MessageBox.Show(window.Title+" is open."); }
實際上,大多數應用程式通常使用一種更具結構化特點的方式在視窗之間進行互動,如果有幾個長時間運行的視窗同事打開,并且它們之間需要以某種方式進行通信,在自定義應用程式類中保存這些視窗的參考可能更有意義,這樣,總可以找到所需的視窗,與此類似,如果有基于檔案的應用程式,那么可選擇創建跟蹤檔案視窗的集合,而不是跟蹤其他內容,
四、在視窗之間進行互動
整如在前面已經看到的,自定義應用程式類是放置回應不同應用程式事件的代碼的好地方,應用程式類還可以很好地達到另一個目的:保存重要視窗的參考,使一個視窗可訪問另一個視窗,
例如,假設希望跟蹤應用程式使用的所有檔案視窗,為此,可在自定義應用程式類中創建專門的集合,下面是使用泛型串列集合保存一組自定義視窗物件的示例,在這個示例中,每個檔案視窗由名為Document類的實體表示:
public partial class App : Application { private List<Document> documents = new List<Document>(); public List<Document> Documents { get { return documents; } set { documents = value; } } }
現在,當創建新檔案時,只需要記住將其添加到Documents集合中即可,下面是回應按鈕單擊事件的事件處理程式,該事件處理程式完成了所需的作業:
private void cmdCreate_Click(object sender, RoutedEventArgs e) { Document doc = new Document(); doc.Owner = this; doc.Show(); ((App)Application.Current).Documents.Add(doc); }
同樣,也可在Document類中回應Window.Loaded類這些事件,以確保當創建檔案物件時,總會在Documents集合中注冊該檔案物件,
現在,可在代碼的其他任何地方進行集合來遍歷所有檔案,并使用公有成員,在該例中,Document類包含用于更新顯示的自定義方法SetContent();
private void cmdUpdate_Click(object sender, RoutedEventArgs e) { foreach (Document doc in ((App)Application.Current).Documents) { doc.SetContent("Refreshed at " + DateTime.Now.ToLongTimeString() + "."); } }
<Window x:Class="WindowTracker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WindowTracker" Height="300" Width="300" > <StackPanel> <Button Click="cmdCreate_Click" Margin="10,10,10,5" Name="cmdCreate">Click to Create a Document</Button> <Button Click="cmdUpdate_Click" Margin="10,5,10,10" Name="cmdUpdate">Click to Refresh the Documents</Button> </StackPanel> </Window>MainWindow.xaml
<Window x:Class="WindowTracker.Document" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WindowTracker" Height="78" Width="209" > A new document. </Window>Document.xaml
public partial class Document : Window { public Document() { InitializeComponent(); } public void SetContent(string content) { this.Content = content; } }Document.xaml.cs
下圖顯示了最終效果圖,最終結果談不上華美,但這種互動方式值得注意——演示了一種通過自定義應用程式類在視窗之間進行互動的安全規范的方式,這種方式比使用Window屬性要好,因為是強型別,只包含Document視窗(而不是包含視窗應用程式中所有視窗的集合),通過這種方式還可使用另一種更有用的方式對視窗進行分類,例如,可使用字典集合,通過鍵名更方便地查找檔案,在基于檔案的參考程式中,可通過檔案名來索引集合中的視窗,

五、單實體應用程式
通常,只要愿意就可以加載WPF應用程式的任意多個副本,某些情況下, 這種設計時非常合理的,但在另外一些情況下,這可能會成為問題,當構建基于檔案的應用程式時更是如此,
對于單實體應用程式,WPF本身并未提供自帶的解決方法,但可使用幾種變通方法,基本技術時當觸發Application.Startup事件時,檢查另一應用程式實體是否已在運行,最簡單的方法是使用全域的mutex物件(mutex物件時作業系統提供的用于行程間通信的同步物件),這種方法很簡單,但功能有限,最重要的是,應用程式的新實體無法與已經存在的實體進行通信,對于基于檔案的應用程式而言這確實是一個問題,因為新實體可能需要告訴已經存在的應用程式實體打開某個特定的檔案(如果該檔案是通過命令列引數傳遞的),
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/5931.html
標籤:WPF
