【WPF學習】第二章 XAML的屬性和事件

一、簡單屬性和類型轉換器app

  在添加文本框時,常常會設置文本框對齊方式、字體、字體大小和頁邊距等信息, 以下文本框示例所示:ide

<TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch"  Margin="10,10,13,10" TextWrapping="Wrap" Grid.Row="0" FontFamily="Verdana" FontSize="24">
     [Please Question here!]
</TextBox>

  爲使上面的設置起做用,System.Windows.Controls.TextBox類必須提供如下屬性:VerticalAlignment、HorizontalAlignment、FontFamily、FontSize、Foreground。爲使這些功能起做用,XAML解析器須要執行比表面上看起來更多的工做。XML特性中的值老是純文本字符串。但對象的屬性能夠是任何.NET類型。在上面的示例中,有兩個屬性是枚舉類型(VerticalAlignment屬性和HorizontalAlignment屬性),一個爲字符串類型(FontFamily屬性),一個爲整形(FontSize屬性),還有一個爲Brush對象(Foreground屬性)。佈局

  爲了關聯字符串值和非字符串屬性,XAML解析器須要執行轉換。由類型轉換器執行轉換,類型轉換器是從.NET 1.0就已經引入的.NET基礎結構的一個基本組成部分。字體

  實際上,類型轉換器在這個過程當中扮演着重要角色——提供了實用的方法,這些方法可將特定的.NET數據類型轉換爲任何其餘.NET類型,或將其餘任何.NET類型轉換爲特定的數據類型。XAML解析器經過如下兩個步驟來查找類型轉換器:編碼

  (1)檢查屬性聲明,查找TypeConverter特性(若是提供了TypeConverter特性,該特性將制定哪一個類可執行轉換)。例如,當實用諸如Foreground這樣的屬性時,.NET將檢查Foreground屬性的聲明。spa

  (2)若是在屬性聲明中沒有TypeConverter特性,XAML解析器將檢查對應數據類型的類聲明。例如,Foreground屬性實用一個Brush對象。因爲Brush類使用TypeConverter(typeof(BrushConverter))特性聲明進行了修飾,所以Brush類及其子類使用BrushConverter類型轉換器。code

  若是屬性聲明或類聲明都沒有與其關聯的類型轉換器,XAML解析器會生成錯誤。xml

二、複雜屬性對象

  雖然類型轉換器便於使用,但不能解決全部的實際問題。例如,有些屬性是完備的對象,這些對象具備本身的一組屬性。儘管建立供類型轉換器使用的字符串形式是可能的,但使用這樣的方法時語法可能十分複雜,而且容易出錯。blog

  幸運的是,XAML提供了另一種選擇:屬性元素語法(property-element syntax)。使用屬性元素法,可添加名稱形式爲Parent.PropertyName的子元素。例如,Grid控件有一個Backgroud屬性,該屬性容許提供用於繪製控件背景區域的畫刷。若是但願使用更復雜的畫刷——比單一固定顏色填充更高級的畫刷——就須要添加名爲Grid.Background的子標籤。以下所示:

<Grid.Background>
            <LinearGradientBrush>
                <LinearGradientBrush.GradientStops>
                    <GradientStop  Offset="0.00" Color="Red"/>
                    <GradientStop Offset="0.5" Color="Indigo"/>
                    <GradientStop Offset="1.0" Color="Violet"/>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
</Grid.Background>

  爲了定義所須要的漸變色,須要建立LinearGraduebtBrush對象。LinearGraduebtBrush類是WPF名稱空間集合中的一部分,因此可爲標籤繼續使用默認的XML名稱空間。

  可是,只是建立LinearGraduebtBrush對象還不夠——還須要爲其指定漸變的顏色。經過使用GradientStop對象的結合填充LinearGraduebtBrush.GradientStops屬性可完成這一工做。

三、標記擴展

  對大多數屬性而言,XAML屬性語法能夠工做的很是好。可是有些狀況下,不可能硬編碼屬性值。例如,可能但願將屬性值設置爲一個已經存在的對象,或者可能但願經過將一個屬性綁定到另一個控件來動態地設置屬性值。這兩種狀況都須要使用標記擴展——一種以很是規的方法設置屬性的專門語法。

  標記擴展可用來嵌套標籤或XML特性中(用於XML特性的狀況更常見)。當用在特性中時,它們老是被花括號{}包圍起來。例如,下面的標記演示瞭如何使用標記擴展,它容許引用另一個類中的靜態屬性:

<Button Foreground="{x:Static SystemColors.ActiveCaptionBrush}"></Button>

  標記擴展使用{標記擴展類 參數}語法。在上述的示例中,標記擴展是StaticExtension類(根據約定,在引用擴展類時能夠省略最後一個單詞Extension)。x前綴指示在XAML名稱空間中查找StaticExtension類。還有一些標記擴展是WPF名稱空間的一部分,它們不須要x前綴。

  全部標記擴展都由繼承自System.Windows.Markup.MarkupExtension基類的類實現。MarkupExtension基類十分簡單——它提供了一個簡單的ProvideValue()方法類獲取所指望的數值。換句話說,當XAML解析器遇到上述語句時,它將建立StaticExtension類的一個實例,而後調用ProvideValue()方法獲取靜態對象。最後找到按鈕的Foreground屬性。

四、附加屬性

   除普通屬性外,XAML還包括附加屬性(attached property)的概念——附加屬性是可用於多個控件但在另一個類中定義的屬性。在WPF中,附加屬性經常使用語控件佈局。

  每一個控件都有各自固有的屬性。當在容易中方式控件時,根據容器的類型控件會得到額外特徵(例如,若是在網格中放置一個文本框,就須要選擇文本框放在網格控件中的哪一個單元格中),使用附加屬性設置這些附加的細節。

  附加屬性始終使用包含兩個部分的命名格式:定義類型.屬性名。這zho9ng包含兩部分的命名語法使XAML解釋器能區分開普通屬性和附加屬性。

以下代碼中,Grid.Row=「0」就是屬於附加屬性。

<TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch"  Margin="10,10,13,10" TextWrapping="Wrap" Grid.Row="0" FontFamily="Verdana" FontSize="24">
            [Please Question here!]
        </TextBox>
        
        <Button  VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,0,0,20" Grid.Row="1" Width="127" Height="23" Name="btnAnswer" Click="btnAnswer_Click">
            Ask the Eight Ball
        </Button>
        <TextBox Grid.Row="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TextWrapping="Wrap" IsReadOnly="True" Margin="10,10,13,10" FontFamily="Verdana" FontSize="24" Foreground="Green">
            [Answer will appear here!]
        </TextBox>

  附加屬性根本不是真正的屬性。它們實際上被轉換爲方法調用。XAML解析器採用如下形式調用靜態方法:DefiningType.SetPropertyName()。例如,在上面的XAML代碼段中,定義類型是Grid類,而且屬性是Row,因此解析器調用Grid.SetRow()方法。

  當調用SetPropertyName()方法時,解析器傳遞兩個參數:被修改的對象以及指定的屬性值。例如,當爲Textbox控件設置Grid屬性時,XAML解析器執行如下代碼:

Grid.SetRow(txtQuestion,0);

  這種方式(調用定義類型的一個靜態方法)隱藏了實際發生的操做,使用起來很是方便。

  這樣的技巧之因此可以湊效,是由於與其餘全部WPF控件同樣,TextBox控件繼承自DependencyObject基類。

2.5 特殊字符與空白

  XAML受到XML規則的限制。例如,XML特別關注一些特殊字符,如&、<和>。若是使用使用這些字符設置元素的內容,將遇到麻煩,由於XAML解析器認爲您正在處理其餘事情——例如,建立嵌套的元素。

  例如,假設須要建立一個包含<Click Me>文本的按鈕,下面的標記是沒法奏效的。

<Button>
    <Click Me>
</Button>

  此處的問題在於,上面的標記看起來像正在試圖建立一個名爲Click,而且帶有<Click>文本的元素。解決問題的方法是用實體引用代替那些特殊字符,實體引用是XAML解析器可以正確解析的特定字符編碼。以下表列出了可能選用的字符實體。主義,只有當使用特殊設置屬性值時,才須要使用引號字符實體,由於引號用於指示特殊指的開始和結束。

特殊字符 字符實體
小於號(<) &lt;
大於號(>) &gt;
&符號(&) &amp;
引號(") &quot;

  能夠將上面的XAML代碼段修改成:

<Button>
    &lt;Click Me &gt;
</Button>

  特殊字符並不是使用XAML的惟一障礙。另外一個問題是空白的處理。默認狀況下,XAML摺疊全部空白,這意味着包含空格、Tab鍵以及硬回車的長字符串將被轉換爲單個空格。並且,若是在元素內容以前或者以後添加空白,將徹底忽略這個空格。在Eight BallAnswer示例中您將看到這種情形。在按鈕和兩個文本框中的文本,使用硬回車字符從XAML標籤中分離出來,並使用Tab字符使標記更加清晰易讀。但多餘的空格不會再顯示在用戶界面中。

  有時這並非全部指望的結果。例如,可能但願在按鈕文本中包含一系列空格。在這種狀況下,須要爲元素使用xml:space="preserve"特性。

  xml:space特性是XML標準的一部分,是一個要麼包括所有、要麼什麼都不包括的設置。一旦使用了該設置,元素內的全部空字符串都將被保留。好比下面的標記:

<TextBox xml:space="preserve">
    [There is a lot  of space inside these marks"        ".]
</TextBox>

2.6 事件

  用於關聯事件處理程序的語法爲:事件名=「事件處理程序方法名」。

  例如,Button控件提供了Click事件。可以使用以下所示的標記關聯事件處理程序:

<Button  Click="btnAnswer_Click">
            Ask the Eight Ball
</Button>

  上面的標記假定在代碼隱藏類中有名爲btnAnswer_Click的方法。事件處理程序必須具備正確的簽名(也就是說,必須匹配Click事件的委託)。下面是一個符合要求的方法:

private void btnAnswer_Click(object sender, RoutedEventArgs e)
        {

        }

  許多狀況下, 將使用特性爲同一元素設置屬性和關聯事件處理程序。WPF老是遵循如下順序:首先設置Name屬性(若是設施的話),而後關聯任意事件處理程序,最後設置其餘屬性。這意味着,全部對屬性變化做出響應的事件處理程序在第一次設置屬性時都會被觸發。

  

  完整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="Eight Ball Answer" Height="328" Width="412">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch"  Margin="10,10,13,10" TextWrapping="Wrap" Grid.Row="0" FontFamily="Verdana" FontSize="24">
            [Please Question here!]
        </TextBox>
        <Button  VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,0,0,20" Grid.Row="1" Width="127" Height="23" Name="btnAnswer" Click="btnAnswer_Click">
            Ask the Eight Ball
        </Button>
        <TextBox Grid.Row="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TextWrapping="Wrap" IsReadOnly="True" Margin="10,10,13,10" FontFamily="Verdana" FontSize="24" Foreground="Green">
            [Answer will appear here!]
        </TextBox>
        <Grid.Background>
            <LinearGradientBrush>
                <LinearGradientBrush.GradientStops>
                    <GradientStop  Offset="0.00" Color="Red"/>
                    <GradientStop Offset="0.5" Color="Indigo"/>
                    <GradientStop Offset="1.0" Color="Violet"/>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Grid.Background>
        

    </Grid>
</Window>
相關文章
相關標籤/搜索