程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF學習筆記 - 4. XAML

WPF學習筆記 - 4. XAML

編輯:關於.NET

Microsoft 將 XAML 定義為 "簡單"、"通用"、"聲明式" 的 "編程語言"。這意味著我們會在更多的地方看到它(比如 Silverlight),而且它顯然比其原始版本 XML (XAML 是一種基於 XML 且遵循 XML 結構規則的語言) 多了更多的邏輯處理手段。如果願意的話,我們完全可以拋開 XAML 來編寫 WPF 程序。只不過這有點類似用記事本開發 .NET 程序的意味,好玩不好用。XAML 的定義模式使得非編程人員可以用 "易懂" 的方式來刻畫 UI,並且這種方式我們早已熟悉,比如 WebForm,亦或者是我一直念念不忘的 Delphi Form (偶爾想起而已,其實早將 Object Pascal 忘得精光了)。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
 </Grid>
</Window>

這是一個非常簡單的 XAML,它定義了一個空白 WPF 窗體(Window)。XAML 對應於 .NET 代碼,只不過這個過程由特定的 XAML 編譯器和運行時解釋器完成。當解釋器處理上面這段代碼時,相當於:

new Window1 { Title = "Window1" };

從這裡我們可以體會兩者的區別,用 XAML 的好處是可以在設計階段就能看到最終的展現效果,很顯然這是美工所需要的。你可以從 VS 命令行輸入 "xamlpad.exe",這樣你會看到直觀的效果。

作為一種應用於 .NET 平台的 "語言",XAML 同樣支持很多我們所熟悉也是必須的概念。

1. Namespace

XAML 默認將下列 .NET Namespace 映射到 "http://schemas.microsoft.com/winfx/2006/xaml/presentation":

System.Windows

System.Windows.Automation

System.Windows.Controls

System.Windows.Controls.Primitives

System.Windows.Data

System.Windows.Documents

System.Windows.Forms.Integration

System.Windows.Ink

System.Windows.Input

System.Windows.Media

System.Windows.Media.Animation

System.Windows.Media.Effects

System.Windows.Media.Imaging

System.Windows.Media.Media3D

System.Windows.Media.TextFormatting

System.Windows.Navigation

System.Windows.Shapes

除了這個包含絕大多數 WPF 所需類型的主要命名空間外,還有一個是 XAML 專用的命名空間 (System.Windows.Markup) —— "http://schemas.microsoft.com/winfx/2006/xaml"。使用非默認命名空間的語法有點類似於 C# Namespace Alias, 我們需要添加一個前綴,比如下面示例中的 "x"。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <TextBox x:Name="txtUsername" Background="{x:Null}"></TextBox>
 </Grid>
</Window>

我們還可以引入 CLR Namespace。

<collections:Hashtable
 xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
 xmlns:sys="clr-namespace:System;assembly=mscorlib">
 <sys:Int32 x:Key="key1">1</sys:Int32>
</collections:Hashtable>

2. Property

我們可以用下面兩種方式來設置 XAML 元素的屬性。

方式1

<Label Name="label10" Foreground="Red">Label10</Label>

方式2

<Label Name="label11">
 <Label.Content>
  Label11
 </Label.Content>
 <Label.Foreground>
  Blue
 </Label.Foreground>
</Label>

WPF 會按下列順序將 XAML 中的屬性字符串轉換為實際屬性值。

(1) 屬性值以大括號開始,或者屬性是從 MarkupExtension 派生的元素,則使用標記擴展處理。

(2) 屬性用指定的 TypeConverter 聲明的,或者使用了轉換特性(TypeConverterAttribute),則提交到類型轉換器。

(3) 嘗試基元類型轉換,包括枚舉名稱檢查。

<Trigger Property="Visibility" Value="Collapsed,Hidden">
 <Setter ... />
</Trigger>

3. TypeConverter

WPF 提供了大量的類型轉換器,以便將類似下面示例中的 Red 字符串轉換城 SystemWindows.Media.Brushes.Red。

<Label Name="label10" Foreground="Red"></Label>

等價於

this.label10.Foreground = System.Windows.Media.Brushes.Red;

不過下面的代碼更能反應運行期的實際轉換行為

var typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(Brush));
this.label10.Foreground = (Brush)typeConverter.ConvertFromInvariantString("Red");

有關轉換器列表,可參考:ms-help://MS.MSDNQTR.v90.chs/fxref_system/html/35bffd5f-b9aa-1ccd-99fe-b0833551e562.htm

4. MarkupExtension

對 XAML 的一種擴展,以便支持復雜的屬性值。這些標記擴展通常繼承自 MarkupExtension,並使用大括號包含。WPF 提供了一些常用的標記擴展,諸如 NullExtension、StaticExtension、DynamicResourceExtension、StaticResourceExtension、Binding 等。和 Attribute 規則類似,我們通常可以省略 Extension 這個後綴。需要注意的是某些標記擴展屬於 System.Windows.Markup,因此我們需要添加命名空間前綴。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <TextBox Background="{x:Null}"></TextBox>
 </Grid>
</Window>

我們可以為標記擴展提供其所需的構造參數。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <Label Content="{x:Static SystemParameters.IconHeight}" />
 </Grid>
</Window>

這個例子中,我們將 System.Windows.SystemParameters.IconHeight 值作為參數傳遞給 "public StaticExtension(string member)" 構造方法,這種參數通常被稱作定位參數。而另外一種參數是將特定的值傳給標記擴展對象屬性,語法上必須指定屬性名稱,故被稱之為命名參數。下面的例子表示將 textBox1.Text 參數綁定到 Label.Content 上,這樣當編輯框內容發生變化時,標簽內容自動保持同步。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <TextBox Name="textBox1" />
  <Label Content="{Binding ElementName=textBox1, Path=Text}" />
</Grid>
</Window>

標記擴展允許嵌套,並可以引用自身。我們看另外一個例子。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <TextBox Name="textBox2" Width="128" Text="{Binding RelativeSource={RelativeSource Self}, Path=Width}" />
 </Grid>
</Window>

這個例子的意思是將 TextBox.Text 內容綁定為其自身(Self)的高度值(Width)。

標記擴展帶來一個問題就是大括號的轉義,畢竟很多時候我們需要在內容顯示中使用它。解決方法是在前面添加一對額外的大括號。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <Label Content="{}{Hello, World!}" />
 </Grid>
</Window>

如果覺得難看,也可以寫成下面這樣。

<Window x:Class="Learn.WPF.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Window1">
 <Grid>
  <Label>{Hello, World!}</Label>
 </Grid>
</Window>

5. Content

XAML 這點和 HTML 非常類似,我們可以將任何內容添加到元素內容項中,這帶來更加豐富的 UI 表達能力,再也不像 WinForm 那樣 "能做什麼,不能做什麼"。

<Button>
 <Hyperlink>Click</Hyperlink>
</Button>

有一點需要注意,內容項並不一定就是 Content。像 ComboBox、ListBox、TabControl 使用 Items 作為內容項。

6. XamlReader & XamlWriter

通常情況下,XAML 在項目編譯時會被壓縮成 BAML (Binary Application Markup Language) 保存到資源文件中。BAML 只是包含 XAML 的純格式聲明,並沒有任何事件之類的執行代碼,切記不要和 MSIL 相混淆。XAML 運行期解釋器解讀 BAML 並生成相應的元素對象。

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

var window = (Window)XamlReader.Parse("<Window xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"></Window>");
window.ShowDialog();

當然,我們還可以從文件流中讀取。

using (var stream = new FileStream(@"test.xaml", FileMode.Open))
{
 var window = (Window)XamlReader.Load(stream);
 var button = (Button)window.FindName("btnOK");
 button.Click += (s, ex) => MessageBox.Show("Hello, World!");
 window.ShowDialog();
}

test.xaml

<Window
 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 x:Name="btnOK">OK</Button>
 </Grid>
</Window>

需要注意的是 XamlReader 載入的 XAML 代碼不能包含任何類型(x:Class)以及事件代碼(x:Code)。

我們可以用 XamlWriter 將一個編譯的 BAML 還原成 XAML。

var xaml = XamlWriter.Save(new Window2());

MessageBox.Show(xaml);

輸出:

<Window2
 Title="Window2" Width="300" Height="300"
 xmlns="clr-namespace:Learn.WPF;assembly=Learn.WPF"
 xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 <av:Grid>
  <av:Button Name="btnOK">OK</av:Button>
 </av:Grid>
</Window2>

XAML 的動態載入在使用動態皮膚場景時非常有用,現在只要了解一下即可。

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