程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF基礎到企業應用系列5——WPF千年輪回 續前緣

WPF基礎到企業應用系列5——WPF千年輪回 續前緣

編輯:關於.NET

一,摘要

這篇課程主要是對上幾次課程的回顧和簡單深化,所以沒有講什麼比較新的概念,不過掌握好了這篇,對後面的很多文章都有幫助,同時這一篇文章做Demo、構思、研究等也花費了不少時間,所以希望對大家有所幫助。

二,本文提綱

· 1.摘要

· 2.本文提綱

· 3.前篇回顧

· 4.Xaml基礎

· 5.脫離VS工具CSC編譯WPF

· 6.XamlReader與XamlWriter

· 7.本文總結

. 8.系列進度

三,前篇回顧

在我們日常的開發中,軟件企業的開發人員一般會有兩種類型的工作:

1,一類是用戶界面設計人員,他們關心的是軟件和用戶之間的交互,就是如何讓用戶體驗更好;

2,另一類是軟件開發人員,他們關心的是軟件的架構設計、業務邏輯的處理和軟件功能的實現;

在BS中,用戶界面設計人員使用HTML及其工具來設計界面,開發人員使用Java,C#,VB或其他語言來實現其中的邏輯,HTML網頁可以用到最終的產品中。

在CS中,過去我們一直沒有分開這兩種不同性質的工作。用戶界面設計人員通常和開發人員使用不同的工具,當界面設計人員設計好用戶界面時,他們的工作並沒有用到最終的產品中,而只是用來展現某種概念或工作流程。

XAML實現了互聯網應用程序和桌面應用程序的統一,界面設計人員可以使用XAML或基於XAML的工具(如微軟的Design和 Blend) 來設計CS或BS應用程序的界面。程序開發人員則可以在此基礎上使用C#或VB.NET等來開發相應的功能,這樣,界面設計人員的工作便自然過渡到最終產品中。

在XAML中,用戶界面用XML的元素或屬性來表示。WPF引擎把XAML描述的UI元素解釋為相應的.NET對象,從而在桌面程序或Silverlight網頁上創建相應的控件。如下圖所示:

上面這副就是傳統的WinForm開發模式,這兩種人沒有分離開來,所以在很多企業裡就形成了開發人員既要做UI也要做程序的境地。

上圖就是現在的WPF和Silverlight程序的開發模式,這兩類人可以分開來工作,他們都可以對Window1.xaml進行修改和加載,所以這樣就使分工更專業了,由於大家專注於某一個方面,分工協作的同時,質量和效率也逐漸提高了。

前幾篇介紹了一些基礎知識,那麼這篇也簡單的回顧一下,下面第一幅圖是WPF的執行順序,第二副圖是WPF的一個項目的構成,第三幅圖是WPF所對應的IL代碼(這些圖處理得不好,還望各位見諒)。

WPF的執行順序

WPF的一個項目的構成

WPF所對應的IL代碼,通過Reflector查看

四,Xaml基礎

這個部分要講的東西就太多了,由於這篇文章篇幅有限,同時我覺得用代碼诠釋能讓大家可以更清晰地理解,所以就講得隨意一些,通過一個Demo介紹 WPF對資源、類、控件的調用和處理,對Dictionary資源、Application資源、window資源以及控件資源的應用等,如下圖所示(本篇所有代碼在評論的第一條):

由於這些概念比較簡單並且較多,如果全部寫完,也得專門寫一長篇,還好大家都喜歡看代碼,所以我就不花費大的篇幅來講它們,感興趣或者對這些知識還有不清楚的朋友可以下載這個Demo進行查看或調試,我覺得對初學者很有幫助。

五,脫離VS工具CSC編譯WPF

為了更好的認識WPF的編譯和執行過程,我們可以暫時棄用我們熟悉的VS工具,選用記事本寫如下的代碼:

using System;
using System.Windows;
namespace KnightsWarrior.HelloWorld
{
   class HelloWorld
   {
     [STAThread]
     public static void Main()
     {
       Window win = new Window();
       win.Height = 300;
       win.Width = 400;
       win.Title = "Hello,KnightsWarrior!";
       win.Show();
       Application app = new Application();
       app.Run();
      }
    }
}

然後保存到D:\HelloWorld.cs 這個位置,通過CMD或者VS cmomand Line中輸入以下編譯命令:

csc.exe /out:D:\HelloWorld.exe D:\HelloWorld.cs /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\presentationframework.dll"  /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\windowsbase.dll"  /reference:"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\presentationcore.dll"

然後就可以手動編譯成功了。

那麼通過Reflector可以查看到它的IL代碼,如果感興趣的朋友也可以進行詳細的分析。

如果對MSIL比較熟悉的朋友,也可以用記事本寫同樣功能的IL代碼,由於沒有對WPF窗體的IL做具體研究,所以用Console程序代替,等過一段時間再研究WPF控件的IL代碼.

.assembly extern mscorlib { auto }
.assembly HelloApp {}
.module HelloApp.exe
.namespace HelloApp
{
   .class public Program extends [mscorlib]System.Object
   {
     .method static private void Main(string[] args)
     {
       .entrypoint
       ldstr "Hello, KnightsWarrior!"
       call void [mscorlib]System.Console::WriteLine(string)
       ret
     }
   }
}

然後打開 Visual Studio Command Prompt,使用 ILASM 開始編譯,

這樣你就更能看清楚編譯器背後的秘密,同時也能跟蹤每一步執行的操作,同時對一些簡單的內存洩露問題也比較容易察覺到。當然現在也有很多工具可以跟蹤這些問題,我這裡只是寫一種思路,大家可以根據自己的愛好取捨。

六,XamlReader與XamlWriter

System.Windows.Markup 命名空間中提供了 XamlReader、XamlWriter 兩個類型,允許我們手工操控 XAML和BAML 文件。

XamlReader類除了定義Load的實時加載之外,也定義了異步方法,可以異步解析XAML中的內容。我們可以在XamlReader對象的實例裡調用它們。如果在讀取一個大文件時要保持用戶UI的響應性,就可以使用異步讀取的方法。和異步讀取方法匹配的還有一個CancelAsync方法,用於停止讀取操作。XamlReader 還定義了LoadCompleted事件,在讀取完成後會觸發該事件,那麼我們就可以把讀完後要做的事情都在這裡進行處理。

XamlWriter 供一個靜態 Save 方法(多次重載),該方法可用於以受限的 XAML 序列化方式,將所提供的運行時對象序列化為 XAML 標記。這句話似乎有點難懂,其實簡單的說就是把它序列化為我們需要的類型。

具體功能代碼如下:

通過XamlReader 動態構建並實例化一個Window

//XamlReader
StringBuilder strXMAL = new StringBuilder("<Window ");
strXMAL.Append("xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" ");
strXMAL.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" ");
strXMAL.Append("Title=\"Window2\" Height=\"600\" Width=\"600\">");
strXMAL.Append("</Window>");
var window = (Window)XamlReader.Parse(strXMAL.ToString());
window.ShowDialog();

同時我們還可以從文件流中讀取並操作。

//XamlReader
using (var stream = new FileStream(@"Window2.xaml", FileMode.Open))
{
   var window2 = (Window)XamlReader.Load(stream);
   var button = (Button)window2.FindName("btnTest");
   button.Click += (x, y) => MessageBox.Show("Knights Warrior");
   window2.ShowDialog();
}

Window2.xaml 的代碼:

<Window x:Class="XamlReaderWriterDemo.Window2"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Window2" Height="300" Width="300">
   <Grid>
     <Button Height="23" Name="btnTest" Margin="98,72,105,0" VerticalAlignment="Top">Button</Button>
   </Grid>
</Window>

這裡我們需要特別注意的是 XamlReader 載入的 XAML 代碼不能包含任何類型(x:Class)以及事件代碼(x:Code),也就是說要XAML自身的代碼才受支持(這個也在WPF揭秘這本書講到過)。那麼我們可以用 XamlWriter 將一個編譯的 BAML 還原成 XAML了,具體代碼如下:

//XamlWriter
var xaml = XamlWriter.Save(new Window2());
MessageBox.Show(xaml);

輸出的Message如下(為了效果更好看一些,我粘貼到了VS):

<Window2 Title="Window2" Width="300" Height="300" xmlns="clr-namespace:XamlReaderWriterDemo;assembly=XamlReaderWriterDemo" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
   <av:Grid>
     <av:Button Name="btnTest" Height="23" Margin="98,72,105,0" VerticalAlignment="Top">Button</av:Button>
   </av:Grid>
</Window2>

XAML 的動態載入在使用動態換膚以及運行時加載等場景頗為有用,以後也會慢慢接觸。

由於使用XamlReader和XamlWriter有很多限制,比如我想把一批Baml轉化為Xaml,再比如我想指定Baml的路徑,然後通過 Load的方式載入,那麼這些場景就無法通過XamlReader和XamlWriter完成了,這個讓我也做過不少的Demo,也跟蹤了很長時間的IL 代碼,在百思不得其解之後和周永恆、Virus等討論了一下,最後終於找到了一個方案,如下代碼所示:

public static class BamlWriter
  {
    public static void Save(object obj, Stream stream)
    {
      string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
      Directory.CreateDirectory(path);
      try
      {
        string xamlFile = Path.Combine(path, "input.xaml");
        string projFile = Path.Combine(path, "project.proj");
        using (FileStream fs = File.Create(xamlFile))
        {
          XamlWriter.Save(obj, fs);
        }
        Engine engine = new Engine();
        engine.BinPath = RuntimeEnvironment.GetRuntimeDirectory();
        Project project = engine.CreateNewProject();
        BuildPropertyGroup pgroup = project.AddNewPropertyGroup(false);
        pgroup.AddNewProperty("AssemblyName", "temp");
        pgroup.AddNewProperty("OutputType", "Library");
        pgroup.AddNewProperty("IntermediateOutputPath", ".");
        pgroup.AddNewProperty("MarkupCompilePass1DependsOn", "ResolveReferences");
        BuildItemGroup igroup = project.AddNewItemGroup();
        igroup.AddNewItem("Page", "input.xaml");
        igroup.AddNewItem("Reference", "WindowsBase");
        igroup.AddNewItem("Reference", "PresentationCore");
        igroup.AddNewItem("Reference", "PresentationFramework");
        project.AddNewImport(@"$(MSBuildBinPath)\Microsoft.CSharp.targets", null);
        project.AddNewImport(@"$(MSBuildBinPath)\Microsoft.WinFX.targets", null);
        project.FullFileName = projFile;
        if (engine.BuildProject(project, "MarkupCompilePass1"))
        {
          byte[] buffer = new byte[1024];
          using (FileStream fs = File.OpenRead(Path.Combine(path, "input.baml")))
          {
            int read = 0;
            while (0 < (read = fs.Read(buffer, 0, buffer.Length)))
            {
              stream.Write(buffer, 0, read);
            }
          }
        }
        else
        {
          throw new System.Exception("Baml compilation failed.");
        }
      }
      finally
      {
        Directory.Delete(path, true);
      }
    }
  }
  public static class BamlReader
  {
    public static object Load(Stream stream)
    {
      ParserContext pc = new ParserContext();
      return typeof(XamlReader)
        .GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static)
        .Invoke(null, new object[] { stream, pc, null, false });
    }
  }

上面的代碼,大家可以試一下運行效果。或者有更好的方式也請告知。

七,本文總結

本篇主要對前幾次的課程進了一些簡單的回顧,同時用一個比較全的Demo介紹了Xaml中引用各種控件和類等,另外對脫離VS工具CSC編譯WPF以及 XamlReader與XamlWriter 做了比較詳細的介紹。下篇我們將進入WPF布局的世界進行漫游,爭取和布局控件及應用來一個全接觸!

出處:http://www.cnblogs.com/KnightsWarrior/

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved