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


1、簡單屬性和類型轉換器

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

<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屬性的聲明。

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

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

2、復雜屬性

  雖然類型轉換器便於使用,但不能解決所有的實際問題。例如,有些屬性是完備的對象,這些對象具有自己的一組屬性。盡管創建供類型轉換器使用的字符串形式是可能的,但使用這樣的方法時語法可能十分復雜,並且容易出錯。

  幸運的是,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屬性可完成這一工作。

3、標記擴展

  對大多數屬性而言,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屬性。

4、附加屬性

   除普通屬性外,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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM