【WPF學習】第四章 加載和編譯XAML

  前面已經介紹過,儘管XAML和WPF這兩種技術具備相互補充的做用,但他們也是相互獨立的。所以,徹底能夠建立不使用XAML和WPF應用程序。數據庫

  總之,可以使用三種不一樣的編碼方式來建立WPF應用程序:app

  • 只使用代碼。這是在Visual Studio中爲Windows窗體應用程序使用的傳統方法。它經過代碼語句生成用戶界面。
  • 使用代碼和未經編譯的標記(XAML)。這種具體方式對於某些特殊狀況是頗有意義的,例如建立高度動態化的用戶界面。這種方式在運行時使用System.Windows.Markup名稱空間中的XamlReader類,從XAML文件中加載部分用戶界面。
  • 使用代碼和編譯過的標記(BAML)。對於WPF而言這是一種更好的方式,也是Visual Studio支持的一種方式。這種方式爲每一個窗口建立了一個XAML模板,這個XAML模板被編譯爲BAML,並嵌入到最終的程序集中。編譯過的BAML在運行時被提取出來,用於從新生成用戶界面。

1、只使用代碼函數

  對於編寫WPF應用程序,只使用代碼進行開發而不適用任何XAML的作法並不常見(可是仍然徹底支持)。只使用代碼進行開發的明顯缺點在於,可能會使編寫WPF應用程序成爲極端乏味的工做。WPF控件沒有包含參數化的構造函數,所以即便爲窗口添加一個簡單按鈕也須要編寫幾行代碼。ui

  只使用代碼進行開發的一個潛在的有點是能夠隨意定製應用程序。例如,可根據數據庫記錄中的信息生成充滿輸入控件的窗體,或可根據當前的用戶酌情添加或替換控件。須要的全部內容只不過是少許的條件邏輯。相比之下,若是使用XAML文檔,它們只能做爲固定不變的資源嵌入到程序集中。this

  下面代碼用於生成一個普通窗體,該窗體包含一個按鈕和一個時間處理程序。在建立窗口時,構造函數調用InitializeComponent()方法,該方法實例化並配置這個按鈕和窗體,並鏈接(hook up)事件處理程序.編碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace WPFCompileByCode
{
    public class Window1:Window
    {
        private Button button1;
        public Window1()
        {
            InitializeComponent();
        }
        private void InitializeComponent()
        {
            this.Width = 300;
            this.Height = 300;
            this.Left = 100;
            this.Top = 100;
            this.Title = "Code Only Window";

            DockPanel panel = new DockPanel();
            button1 = new Button();
            button1.Content = "Click Me";
            button1.Margin = new Thickness(30);
            button1.Click += button1_Click;
            IAddChild container = panel;
            container.AddChild(button1);

            container = this;
            container.AddChild(panel);
        }

        void button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Click Me");
        }
    }
}

  從概念上講,本例中的Windows1類更像傳統的Windows窗體硬功程序中的窗體。它繼承自Window基類,併爲每一個控件添加一個私有程序變量。爲了清晰起見,該類在專門的InitializeComponent()方法中執行初始化操做。spa

  爲啓動該應用程序,可在Main()方法中添加以下代碼:設計

public class Program:Application
    {
        [STAThread]
        public static void Main()
        {
            Program app = new Program();
            app.MainWindow = new Window1();
            app.MainWindow.ShowDialog();
        }
    }

  運行效果圖以下所示:code

 

 2、使用代碼和未編譯的XAMLxml

   使用XAML最有趣的方式之一是使用XamlReader類隨時解析它。例如,假設建立一個Window1.xaml的文件,且內容以下所示:

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Button Name="button1" Margin="30">Click Me</Button>
</DockPanel>

   編寫一個類,用來加載xaml文件。以下代碼所示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace WPFCompileByXaml
{
    public class Window1:Window
    {
        private Button button1;
        public Window1(string xamlFile)
        {
            this.Width = 300;
            this.Height = 300;
            this.Left = 100;
            this.Top = 100;

            DependencyObject rootElement;
            using (FileStream fs = new FileStream(xamlFile, FileMode.Open))
            {
                rootElement = (DependencyObject)XamlReader.Load(fs);
            }

            this.Content = rootElement;

            button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1");

            /*
            FrameworkElement frameworkElement = (FrameworkElement)rootElement;
            button1 = (Button)frameworkElement.FindName("button1");
             */

            button1.Click += button1_Click;
        }

        void button1_Click(object sender, RoutedEventArgs e)
        {
            //MessageBox.Show("Hello ,you click me!");
            button1.Content = "Thank you!";
        }
    }
}

  在此,構造函數接收XAML文件名做爲參數。而後構造函數打開一個FileStream對象,並使用XamlReader.Load()方法將這個文件中的內容轉換爲DependencyObject對象,DependencyObject是全部WPF控件繼承的基類。DependencyObject對象可放在任意類型的容器中,但在這個示例中它被用做整個窗體的內容。

  爲操做元素——如Windows1.xaml文件中的按鈕,須要在動態加載的內容中查找相應的控件對象。LogicalTreeHelper類可達到該目的,由於它具備查找一顆完整控件對象的能力,它能夠查找所需的許多層,直到找到具備指定名稱的對象。而後將一個事件處理程序關聯到Button.Click事件。

  另一種方法是使用FrameworkElement.FindName()方法,在這個示例中,根元素是DockPanel對象,與WPF窗口中的全部控件同樣,DockPanel類繼承自FrameworkElment類,這意味着可以使用以下等效的方法:

FrameworkElement frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");

代替下面的代碼:

button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1");

  在這個示例中,Window1.xaml文件和可執行的應用程序位於同一文件夾中,並一同發佈。然而,儘管該文件沒有被編譯爲應用程序的一部分,但仍能夠將其添加到Visual Studio項目中。這樣能夠更方便地管理文件,並使用Visual Studio設計用戶界面(假定使用.xaml文件擴展名,從而使用Visual Studio可以識別出該文檔是XAML文檔)。

  若是使用這種狀況,確保鬆散的XAML文件不會像傳統的XAML文件那樣被編譯或嵌入到項目中。將文件添加到項目後,在解決方案中選中該文件,而後使用屬性窗口,將Build Action設置爲None,並將Copy to Output Directory 設置爲Copy Always。

  顯然,先將XAML編譯爲BAML,再在運行時加載BAML,比動態加載XAML的效率高,當用戶界面比較複雜時尤爲如此。然而,這種編碼模式爲構建動態的用戶界面提供了多種可能。例如,可建立通用的檢測應用程序,從Web服務中讀取窗體文件,而後顯示相應的檢測控件(標籤、文本框和複選框等)。窗體文件能夠是具備WPF標籤的普通XML文件,使用XamlReader類將該文檔加載到一個已經存在的窗體中。檢測以後,爲了收集結果,只須要枚舉全部輸入控件並提取他們的內容便可。

3、使用代碼和編譯過的XAML

  當編譯WPF應用程序時,Visual Studio使用分爲兩個階段的編譯處理過程。第一階段將XAML文件編譯爲BAML。例如,若是項目中包含名爲Window1.xaml的文件,編譯器將建立名爲Window1.baml的臨時文件,並將該文件放在項目文件夾的obj/Debug字文件夾中。同時,使用選擇的語言爲窗口建立部分類。例如,若是使用C#語言,編譯器將在obj/Debug文件夾中建立名爲Window1.g.cs的文件。g表明生產的(generated)。

  部分類包括以下三部份內容:

  • 窗口中全部控件的字段。
  • 從程序集中加載BAML的代碼,由此建立對象樹。當構造函數調用InitializeComponent()方法時將發生這種狀況。
  • 將恰當的控件對象指定給各個字段以及鏈接全部事件處理程序的代碼。該過程是在名爲Connect()的方法中完成,BAML解析器在每次發現一個已經命名的對象時調用該方法一次。

  部分類不包含實例化和初始化控件的代碼,由於這項任務由WPF引擎在使用Application.LoadComponent()方法處理BAML時執行。

  具體實例,能夠查看Visual Studio編譯後的WPF應用程序。

相關文章
相關標籤/搜索