在繼續總結后面的內容之前,我們先來看一下前面漏掉的一個知識點,那就是分部類,下面我們來看一下什么是分部類,以及為什么要用分部類。
一,分部類:
我們還是拿之前的例子來看,代碼如下。
XAML代碼:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<!--http://schemas.microsoft.com/winfx/2006/xaml/presentation:核心Silverlight命名空間
http://schemas.microsoft.com/winfx/2006/xaml:是XAML命名空間
-->
<Grid x:Name="LayoutRoot" Background="White">
<Button
x:Name="button"
Width="200"
Height="25"
Click="button_Click"
>
Click me,baby,one more time!
</Button>
</Grid>
</UserControl>
Code-behind代碼:
public partial class MainPage : UserControl//分部類關鍵字partial
{
public MainPage()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
//todo:添加Click事件處理代碼
MessageBox.Show("button被單擊了!");
}
}
大家可能已經注意到MainPage類名前的關鍵字partial,沒錯,它在這邊聲明的就是分部類,從這個例子中我們能夠了解到什么是分部類以及分部類的作用。
1,什么是分部類?
將一個類的定義拆分到兩個或多個源文件中。在類聲明前添加關鍵字partial.
2,分部類的作用?
一個類分布在多個獨立源文件中可以讓多位程序員同時對該類進行處理,在這里,實現了界面和行為的分離。
關鍵字partial使得編譯器將XAML文件生成的類(在這里指XAML文件)與人為生成的類(在這里指Code-behind文件)結合起來形成一個完整的類。這兩部分代碼是成對出現並且相互依賴的。XAML文件中定義的部分MainPage類依賴於隱藏代碼中的類來調用InitializeCompoent方法,並處理事件。隱藏代碼中的類則依賴於XAML文件中定義的分部MainPage類來實現InitializeCompoent,從而得到了主窗體的"外觀"(同時包含了相關的子控件).
二,子元素:
在講解子元素之前,我們先來看一下完整的XAML代碼:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<Ellipse/>
<TextBlock>
Name:<TextBlock Text="{Binding Name}"/>
</TextBlock>
</Grid>
</Window>
其中子元素的XAML代碼為:
<Grid>
<Ellipse/>
<TextBlock>
Name:<TextBlock Text="{Binding Name}"/>
</TextBlock>
</Grid>
不管什么時候提供嵌套內容,XAML編譯器都需要父類型(在這里是Window)或者是通過ContentPropertyAttribute來注釋的基類。在這個示例中,雖然Windows沒有這個屬性,但是它的ContentControl基類有這個屬性,如下。
處理子內容:
[ContentProperty("content"),...]
public class ContentControl:Control
{
...
public Object Content{get{...} set{...}}
}
這個屬性將會為XAML編譯器提供包含子內容的屬性名。因此,在這個示例中,編譯器將會為一個將要創建並且指派到Content屬性上的Grid對象進行排列,如下是相應的代碼:
窗口內容:
MainWindow myWindow=new MainWindow(); //創建MainWindow類的對象
Grid g=new Grid(); //創建Grid類對象
myWindow.Content=g;
Content屬性是屬性Object類型,這就意味着Window元素只支持單個子元素。諸如面板之類的可以包含多個子元素的元素只需指定一個帶有集合類型的元素作為內容屬性,如:
處理多個子元素:
[ContentProperty("Children"),...]
public class Panel:FrameworkElement
{
...
public UIElementCollection Children{get{...}}
}
由於Panel是Grid的一個基類,因此這就告訴了我們XAML編譯器是如何在這個示例中將子元素添加到Grid的,說明代碼如下。
為Grid添加內容:
Grid g=new Grid(); //創建Grid類對象
myWindow.Content=g;
Ellipse e=new Ellipse();//創建Ellipse類對象
TextBlock t=new TextBlock();//創建TextBlock 類對象
...
g.Children.Add(e);
g.Children.Add(t);
TextBlock有着更深層的嵌套內容(一些文本和第二個TextBlock)。這個特定的示例變得有點復雜了,因為在這個情況下,XAML編譯器將會為子元素自動生成封裝對象。如果在文本元素里加入普通的文本,比如TextBlock或Paragraph,它就會自動被封裝進一個Run對象中。同樣地,子元素FrameworkElement將會被自動地封裝進InlineUIContainer元素里。下面的代碼對這些進行了解釋。
隱式的Run和InlineUIContainer:
<TextBlock>
Name:<TextBlock Text="{Binding Name}"/>
</TextBlock>
顯式的Run和InlineUIContainer:
<TextBlock>
<Run Text="Name"/>
<InlineUIContainer>
<TextBlock Text="{Binding Name}"/>
</InlineUIContainer>
</TextBlock>
以上代碼之所以能夠運行是因為一對自定義屬性。TextBlock通過ContentPropertyAttribute進行了注釋,這表示其子元素內容應該加入到其內嵌的屬性中。而這個屬性屬於InlineCollection類型,並且通過一對ContentWrapperAttribute自定義屬性進行了注釋。
ContentWrapperAttribute自定義屬性:
[ContentWrapper(typeof(Run)),ContentWrapper(typeof(InlineUIContainer)),WhitespaceSignificantCollection]
public class InlineCollection:TextElementCollection<Inline>,...
通過以上代碼我們就可以發現上例等價於以下代碼:
等價的TextBlock代碼:
TextBlock t=new TextBlock();
t.Inlines.Add(new Run("Name:"));
TextBlock t2=new TextBlock();
t.Inlines.Add(new InlineUIContainer(t2));
t2.SetBinding(new Binding("Name"));
三,屬性和類型轉換器:
在上面的例子中我們設置了Window元素的Title屬性。這個屬性屬於String類型。純文本屬性本身就與XML兼容,因為XML是一種基於文本的格式。不過其它屬性是怎么樣的呢?請看下面代碼:
<!--非字符串屬性-->
<Rectangle Width="100"
Height="20"
Stroke="Black"
Fill="#80FF40EE"/>
以上設置的屬性中沒有一個是字符串類型,其中Width和Height兩者都是Double型,而Stroke和Fill兩者都需要一個Brush。為了支持不同的屬性類型,XAML依賴於.NET的TypeConverter(類型轉換器)系統。一個TypeConverter可以在不同類型的值之間產生映射,最常見的是在String和原始類型之間。
Width和Height屬性是通過LengthConverter類型來進行轉換。其中BrushConverter類是為其它兩個屬性所用,因為盡管它們沒有TypeConverterAttribute,但它們屬於Brush類型,而Brush類型擁有TypeConverterAttribute,這表示應該使用到BrushConverter.以下代碼說明了屬性如何在每種情況下進行應用。
public class FrameworkElement:UIElement,...
{
...
[TypeConverter(typeof(LengthConverter))...]
public double Width{...}
[TypeConverter(typeof(LengthConverter))...]
public double Height{...}
}
[TypeConverter(typeof(BrushConverter))]
public abstract class Brush:Animatable,...
由於Stroke設置了一個標准的已命名顏色,因此這將促使相關的SolidColorBrush從Brushes類那里取得。
上面示例等同於以下代碼:
Rectangle r = new Rectangle();//創建Rectangle類對象
r.Width = 100.0;//設置屬性值(中間經過了LengthConverter轉換器轉換)
r.Height = 20.0;
r.Stroke = Brushes.Black;
r.Fill = new SolidColorBrush(Color.FromArgb(0x80,0xff,0x40,0xEE));
當然,如果自義定組件允許帶有非標准類型的屬性通過XAML進行設置的話,它們就會提供自身的類型轉換器。
四,屬性元素語法:
盡管使用類型轉換器系統一般可以通過XAML中的屬性來指定屬性值,但是它也有一定的限制,比如BrushConverter就不能提供一種漸變填充的方式,對於這些情況,就可以使用XAML所支持的屬性元素語法(property element syntax)。
通過屬性元素語法,你可以使用嵌套的元素來設置屬性,而不直接使用屬性。這個嵌套元素的名字形式可以是Parent.PropertyName,其中Parent是作為進行屬性設置的元素名稱,而PropertyName則是屬性名。以下這種縮進式的語法將元素作為屬性值進行標記而不是子內容。
屬性元素語法:
<Button>
<Button.Background>
<SolidColorBrush Color="#FF4444FF"/>
</Button.Background>
Click me
</Button>
使用屬性元素語法來設置一個Button元素的Background屬性,它等價於以下代碼:
Button btn = new Button();//創建Button類對象
SolidColorBrush brush = new SolidColorBrush();//創建SolidColorBrush類對象
brush.Color = Color.FromArgb(0xFF,0x44,0x44,0xFF);
btn.Background = brush;
btn.Content = "Click me";
這個示例的效果與屬性Background="#FF4444FF"一樣,也就是說如果將數字顏色指定成一個畫刷的話,BrushConverter就會將其轉換為一個SolidColorBrush。盡管這種屬性元素版本的代碼有點冗余,不過它清楚地展示了這里所創建的畫刷類型。
盡管這種更詳細的語法有助於揭示后台類型轉換器的工作原理,但這並不是使用屬性元素語法的理由,其主要原因是因為我們需要在屬性里嵌套一些更加復雜的定義。如下示例。
嵌套的屬性元素:
<Button VerticalAlignment="Center" HorizontalAlignment="Center">
<Button.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="#800"/>
<GradientStop Offset="0.35" Color="Red"/>
<GradientStop Offset="1" Color="#500"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
Click me
</Button>
XAML效果為:

上例中的等價代碼為:
Button b = new Button();//創建Button類對象
b.VerticalAlignment = VerticalAlignment.Center;//設置屬性
b.HorizontalAlignment = HorizontalAlignment.Center;
LinearGradientBrush brush = new LinearGradientBrush();
brush.StartPoint = new Point(0,0);
brush.EndPoint = new Point(0,1);
GradientStop gs = new GradientStop();
gs.Offset = 0;
gs.Color = Color.FromRgb(0x80, 0, 0);
brush.GradientStops.Add(gs);
gs = new GradientStop();
gs.Offset = 0.35;
gs.Color = Colors.Red;
brush.GradientStops.Add(gs);
gs = new GradientStop();
gs.Offset = 1;
gs.Color = Color.FromRgb(0x50,0,0);
brush.GradientStops.Add(gs);
b.Background = brush;
b.Content = "Click me";
五,附加屬性:
XAML不僅允許設置普通的.NET屬性,而且還支持附加屬性。一個附加屬性指其屬性是由一個不同的類定義的,而不是由進行應用的元素來定義。示例如下。
附加屬性:
<Button Grid.Row="1" Name="myButton"/>
其中Grid.Row="1"就是附加屬性,附加屬性的語法簡單易懂,通常總是DefineType.PropertyName的形式,其中DefineType是定義屬性的類型名稱,而PropertyName則是屬性名。XAML會將其解釋為對靜態DefineType.SetPropertyName方法的調用,並且會傳入目標對象和值。以下代碼解釋了這個過程。
通過代碼設置一個附加屬性
Grid.SetRow(myButton,1);
以上就是今天總結的內容,下一篇將會總結擴展標記以及其它一些主題,歡迎大家指正,因為以前沒有學習過XAML,這次是看了書后總結的,如果大家有一些好的關於XAML方面的資料的話希望可以分享一下。
今天是大年初二,在這里祝大家新年快樂,龍年大吉。
