深入淺出學WPF窗口- [WPF學習總結]

深入淺出是個好詞,侯大俠的《深入淺出MFC》,最近還在看一本也是以深入淺出爲標題的書籍。覺得深入去了解程序實現背後的原理纔是真正的透徹掌握一門知識,最近雖然在學習WPF,但感覺沒有什麼實質性的學習成果,像一些更細節更低層的原理總是迷迷糊糊的,在研究WPF父窗口與子窗口之間的關係時,覺得應該更深入的去了解WPF窗口背後的實現技術,這樣才能熟練的淺出使用它。

  首先先通過控制檯直接生成一個WPF窗口,從低層去認識WPF窗口的實質。新建一個c#控制檯程序,添加引用:Windowsbase,PresentationCore,PresentationFramework以及System.Axml。這樣我們才能通過控制檯去生成一個WPF應用程序,然後在cs文件裏面引入using System.windows; 和using System.windows.Controls;

using System;
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;
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函數,函數原型是:

public int 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添加一個響應事件:

[STAThread]
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窗口。

protected override void OnStartup(StartupEventArgs e)     
{            
    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屬性則是存儲了所有的窗體的一個集合,它包含應用程序中所有的窗體資源。如下我們定義多個窗口,則可以通過下面的代碼來實現依次訪問各個窗口:

void wint_MouseDown(object sender, MouseButtonEventArgs e)        
{            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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章