1. 程式人生 > >【WPF學習】第二十七章 Application類的任務

【WPF學習】第二十七章 Application類的任務

  上一章介紹了有關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物件時作業系統提供的用於程序間通訊的同步物件)。這種方法很簡單,但功能有限。最重要的是,應用程式的新例項無法與已經存在的例項進行通訊。對於基於文件的應用程式而言這確實是一個問題,因為新例項可能需要告訴已經存在的應用程式例項開啟某個特定的文件(如果該文件是通過命令列引數傳遞的)。

&n