Application=Code+Markup:Styles @ 錫安主機 :: 隨意窩 Xuite日誌
  • 關鍵字
  • google
  • 2007-05-09 11:31 Application=Code+Markup:Styles
    平均分數:0 顆星    投票人數:0
    我要評分:

     

    Style最重要的屬性就是SettersSetter屬性是一種SetterBaseCollection型態,也就是SetterBase物件的集合。SetterBase是一個抽象類別,SetterEventSetter都有繼承。

    這些物件都叫setters,因為他們都會導致屬性的設定或是event handler

     

    SetterStyle的內容屬性,所以SetterEventSetter都是Style元件的子元件,

     

    <Style ...>
        <Setter ... />
        <EventSetter ... />
        <Setter ... />
    </Style>

    根據Style的定義,Setter物件通常會比 EventSetter 物件出現更多次。其中有兩個比較重要的屬性: Property (是一種 DependencyProperty型態) 以及 Value ( object型態).

     

    XAML中會是這樣寫:

    <Setter Property="Control.FontSize" Value="24" />

    雖然Property 屬性通常是參考到 dependency property,但是你可以注意到上面是指定到FontSize 而不是FontSizeProperty。

     

    假如你需要指定某個Value的屬性為null,可以用下面表示:

     

    Value="{x:Null}"

    FrameworkElement 和FrameworkContentElement 類別定義了一個屬性叫做Style ,他的型態是Style, 所以他可以:但是這通常不是很有用:也就是拿這個屬性來套用他的樣式!舉例來說,下面是一個範例,會用style來定義一個按鈕的樣式!

     

    ButtonWithLocalStyle.xaml

     

    <Button xmlns="http://schemas.microsoft.com/winfx/
    2006/xaml/presentation"    HorizontalAlignment="Center" VerticalAlignment="Center"
            Foreground="Red">
        <Button.Style>
            <Style>
                <Setter Property="Button.FontSize" Value="18pt" />
                <Setter Property="Control.Foreground" Value="Blue" />
            </Style>
        </Button.Style>
        Button with Local Style
    </Button>

     

     

    上面可以看到Style元素包含了兩個setter元素來定義按鈕的字型大小和前景顏色屬性。注意到第一個設定是按鈕字形大小,第二個是前景顏色!這裡並不介意你怎麼命名,因為Button會從Control繼承 Foreground 屬性。你也可以用 TextBlock.FontSize 和 TextBlock.Foreground且他也會正常運作。雖然按鈕和textblock沒有互相繼承,但是這兩個元件都定義了基於TextElement為基礎的這兩個屬性。 (你可以這樣驗證:觀察這些屬性的Owner 類別,可以用第16章中的 ExploreDependencyProperties程式。)

     

    你將可以看到Style定義了前景是Blue,但是前面一點的Style定義了紅色,你會懷疑是用哪一個顏色,答案是這按鈕的顏色是紅色,因為比較靠近元件的設定會取代之前的設定,也就是所謂的local setting,這會比style更高優先權!

     

    而把style放在資源片段中更是常見,因為這樣就可以在不同的地方與不同的元件分享!和其他的資源一樣,style也是使用字串的鍵值。且也有繼承觀念。若把style定義在Application 物件的Resources 片段,則可以整個程式都得取的到。

     

    下面是一個StackPanel 有包含一個資源片段來定義style! Style 有定義一個鍵值:normal !

     

     

    StyleWithMultipleButtons.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel.Resources>
            <Style x:Key="normal">
                <Setter Property="Control.FontSize" Value="24" />
                <Setter Property="Control.Foreground" Value="Blue" />
                <Setter Property="Control.HorizontalAlignment" Value="Center" />
                <Setter Property="Control.Margin" Value="24" />
                <Setter Property="Control.Padding" Value="20, 10, 20, 10" />
            </Style>
        </StackPanel.Resources>

        <Button Style="{StaticResource normal}">

            Button Number 1
        </Button>

        <Button Style="{StaticResource normal}">
            Button Number 2
        </Button>

        <Button Style="{StaticResource normal}">
            Button Number 3
        </Button>
    </StackPanel>

     

    因為元件和控制項都可以共用相同的屬性,所以你可用來定義不同型態的元件。下面是Button 控制項和 TextBlock element 共用style!

     

    StyleWithMultipleElements.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style x:Key="normal">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.Foreground" Value="Blue" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

                <Setter Property="Control.Padding" Value="20, 10, 20, 10" />

            </Style>

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button on top of the stack

        </Button>

     

        <TextBlock Style="{StaticResource normal}">

            TextBlock in the middle of the stack

        </TextBlock>

     

        <Button Style="{StaticResource normal}">

            Button on the bottom of the stack

        </Button>

    </StackPanel>

     

     

     

    事實上,你也可以在style中針對某個元件去做特別的設定。例如:

    <Setter Property="Button.IsDefault" Value="true" />

    程式一樣可以執行,TextBlock 元件會忽略該 Setter ,因為 TextBlock 並沒有 IsDefault 屬性。

    但是若你針對

                <Setter Property="Button.Foreground" Value="Black" />

    去做設定,則整個就會變成黑色!

     

     

    和其他資源一樣,你可以在多個資源片段中使用相同的鍵值,而這種情形會套用第一個遇到的style!

     

    下面就是一個例子,

     

     

    StylesWithSameKeys.xaml

     

    <Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <Grid.Resources>

            <Style TargetType="{x:Type Button}">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.Foreground" Value="Blue" />

            </Style>

        </Grid.Resources>

     

        <StackPanel>

            <StackPanel.Resources>

                <Style TargetType="{x:Type Button}">

                    <Setter Property="Control.Foreground" Value="Red" />

                </Style>

            </StackPanel.Resources>

            <Button>

                Button Number 1

            </Button>

            <Button>

                Button Number 2

            </Button>

            <Button>

                Button Number 3

            </Button>

        </StackPanel>

    </Grid>

     

     

    這些按鈕會使用定義在stackpanel中的stackpanel! 若使用相同的style name,不能覆寫所選擇的屬性

     

    假如你無法一下子把property的值給描述用字串表示完畢,可以使用Value屬性來設定。如下:

     

    StyleWithPropertyElement.xaml

     

    [View full width]

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style x:Key="normal">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

                <Setter Property="Control.Background">

                    <Setter.Value>

                        <LinearGradientBrush StartPoint="1,0" EndPoint="1,1">

                            <LinearGradientBrush.GradientStops>

                                <GradientStop Color="LightBlue" Offset="0" />                           

                                    <GradientStop Color="Aquamarine" Offset="1" />

                            </LinearGradientBrush.GradientStops>

                        </LinearGradientBrush>

                    </Setter.Value>

                </Setter>

            </Style>

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 2

        </Button>

        <Button Style="{StaticResource normal}">

            Button Number 3

        </Button>

    </StackPanel>

     

     

    另外一個解法就是把漸進色變成資源,並且在style定義中參考到這個資源,就像你在第21章所學到的 Application, FrameworkElement, 和 FrameworkContentElement類別都定義一個Resources 屬性且是ResourceDictionary型態. Style也是一樣!定義了一樣的 Resources 屬性,所以你可以把這些來源放入style定義中,若資源沒有要在其他地方再使用的話,這會是結構性比較好的好解法:

     

    StyleWithResource.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

     

            <Style x:Key="normal">

                <Style.Resources>

                    <LinearGradientBrush x:Key="gradbrush" StartPoint="1,0" EndPoint="1,1">                   

                        <LinearGradientBrush.GradientStops>

                            <GradientStop Color="LightBlue" Offset="0" />                   

                            <GradientStop Color="Aquamarine" Offset="1" />

                        </LinearGradientBrush.GradientStops>

                    </LinearGradientBrush>

                </Style.Resources>

     

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

                <Setter Property="Control.Background"  Value="{StaticResource gradbrush}" />

     

            </Style>

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 2

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 3

        </Button>

    </StackPanel>

     

    這個Style類別有定義六個屬性,且你已經看過了其中兩個,(Setters and Resources). Style 也定義了一個屬性叫做TargetType 可以讓你指定要套用此style的元件型態,這語法需要如下 x:Type, 會包在一個中括弧中,如下:

     

    <Style TargetType="{x:Type Button}" ...>
        ...
    </Style>

    你可以把 x:Type 想成是 c#中的typeof。假如你有設定 TargetType, 你就不需要指定 x:Key 的值。鍵值是由 TargetType所組成。沒有了 x:Key,則這個style會套用在之前範例中的所有按鈕。若你不想要這樣,當然也可以包一個 x:Key 屬性,並且可以讓按鈕元件參考到。

     

    當你使用TargetType ,該樣式會套用到這些元件並且需要指明為確切的型態。舉例來說,你不能使用Control 當成是TargetType ,來讓按鈕和Label來共用!

     

    使用TargetType 的一個優點就是你不需要完全去檢查Setter元件中的屬性名稱。正常情況下你需要這樣做 

     

    <Setter Property="Button.FontSize" Value="24" />

    而當你使用了  TargetType ,你就可以這樣輕易的指定:

     

    <Setter Property="FontSize" Value="24" />

    這個簡化也許就有值得讓你改用TargetType 屬性了!TargetType 也可以讓你的程式讓別人看的比較清處,而不需要看檔案。

     

    下面是一個範例。

     

     

    StylesWithTargetTypes.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

     

            <Style TargetType="{x:Type Button}">

                <Setter Property="FontSize" Value="24" />

                <Setter Property="Foreground" Value="Blue" />

            </Style>

     

            <Style TargetType="{x:Type TextBlock}">

                <Setter Property="Foreground" Value="Red" />

            </Style>

     

        </StackPanel.Resources>

     

        <Button>

            Button with Text Content

        </Button>

     

        <TextBlock>

            TextBlock Text

        </TextBlock>

     

        <Button>

            <TextBlock>

                Button with TextBlock Content

            </TextBlock>

        </Button>

    </StackPanel>

     

     

     

     

    The first button gets the style with a TargetType of Button. The button displays blue text with a font size of 24. The TextBlock element gets the style with the TargetType of TextBlock. The text is red and the font size is the default. The bottom button gets the first stylea Foreground property of Blue and a FontSize of 24. However, the TextBlock inside the button gets the second stylea Foreground property of Red. Through normal property inheritance, the TextBlock inherits the FontSize property of its Button parent. The button displays red text with a font size of 24. The style normally takes precedence over property inheritance, but the TextBlock has no style setting for the font size, so it inherits the property from its visual parent.

     

    因為TextBlock沒有設定字形大小,所以他會繼承visual 下來的一些屬性!

    假如你的程式裡有一堆dialog,且這些dialog中有一些radio button,且是在stackpannel中,而你想要設定這些元件的邊界,你可以設定RadioButton的TargetType 來設定這些style!

     

    你可以在TargetType 規格中定義鍵值。既然這樣的話,當元件的型態符合style中的TargetType 且鍵值也對的話, 特定的元件就會取得特定的樣式。而若有許多個style定義在任何的資源片段中的時候,這些鍵值必須要唯一。

     

    而每個特定的元件,只會有一個style來套用,有時候你會想要針對某個style做新增修改並定義成新的style,這種情形,你可以定義一個style,並且有一個  BasedOn 屬性來參考到原本的style!  下面是一個例子:

     

    BasedOnStyle.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

     

            <Style x:Key="normal">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.Foreground" Value="Blue" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

                <Setter Property="Control.Padding" Value="20, 10, 20, 10" />

            </Style>

     

            <Style x:Key="hotbtn" BasedOn="{StaticResource normal}">

                <Setter Property="Control.Foreground" Value="Red" />

            </Style>

     

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource hotbtn}">

            Button Number 2

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 3

        </Button>

    </StackPanel>

     

     

    BasedOn 語法和用元件來參考style很像!規則也一樣。要說第二個按鈕是用normal style來初始化並且使用hotbtn style來覆寫並不完全正確。比較正確的說法是第二個按鈕 hotntn來使用,並且用normal style 來當成初始化的一些值!一個元件至多只有一個style物件,所以第二個按鈕是用了hotbtn style! 當我們在寫xaml時,最容易忘記你還是正在處理屬性和物件,style是一個定義在FrameworkElement的屬性。style的預設值為null, 且可設定到這個屬性的style物件只有一個setter集合,包含了Style 設定的屬性的Setter物件。

     

    若你要定義一個style可以有TargetType 屬性,則語法會比較複雜一點:

     

    BasedOn="{StaticResource {x:Type Button}}"

    新的style也會有自己的TargetType ,和基本style一樣。

     

     

     

    BasedOnTargetType.cs

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

     

            <Style TargetType="{x:Type Button}">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.Foreground" Value="Blue" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

            </Style>

     

            <Style x:Key="hotbtn" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">

                <Setter Property="Control.Foreground" Value="Red" />

            </Style>

     

        </StackPanel.Resources>

     

        <Button>

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource hotbtn}">

            Button Number 2

        </Button>

     

        <Button>

            Button Number 3

        </Button>

    </StackPanel>

     

    第二個style需要定義一個x:Key屬性,否則將會分不清處!

     

    然而你可以嘗試這樣:你可以把第一個style改成Control,並且把第二個styleBasedOn也改成Control,但是把第二個style的TargetType 保留還是Button,這樣是合法的,一個style包含了BasedOn 屬性以及一個 TargetType 屬性會參考到一樣的類別或是他所繼承的類別。此時第二個style有一個TargetType 指向到Button,並且有一個鍵值叫做hotbtn,所以第二個按鈕會使用該style。第一個和第三個就沒有style。假如你除去第二個style的 x:Key 定義以及第二個按鈕的 Style 屬性的話,則三個按鈕都會套用到第二個style!

     

     

     

    若你使用TargetType ,而讓元件的特定型態都有特定的style,可以定義一種有階層性的style

     

     

     

    TargetTypeDerivatives.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel.Resources>

            <Style TargetType="{x:Type Control}">
                <Setter Property="Control.FontSize" Value="24" />
                <Setter Property="Control.Foreground" Value="Blue" />
                <Setter Property="Control.HorizontalAlignment" Value="Center" />
                <Setter Property="Control.Margin" Value="24" />
            </Style>

            <Style TargetType="{x:Type Button}"   BasedOn="{StaticResource {x:Type Control}}">
                <Setter Property="Control.Foreground" Value="Red" />
            </Style>

            <Style TargetType="{x:Type Label}"   BasedOn="{StaticResource {x:Type Control}}">
                <Setter Property="Control.Foreground" Value="Green" />
            </Style>

            <Style TargetType="{x:Type TextBox}"
                   BasedOn="{StaticResource {x:Type Control}}">
            </Style>
        </StackPanel.Resources>

        <Button>
            Button Control
        </Button>

        <Label>
            Label Control
        </Label>

        <TextBox>
            TextBox Control
        </TextBox>
    </StackPanel>

     

    最後一個textboxstyle元件並沒有定義任何新的屬性,所以textbox會去取得一個前景為藍色的顏色。

     

    當然,style元件也可以為獨立的控制項類型定義屬性。Style有一些特定的繼承限制。舉例來說,你可以為一個StackPanel來定義Style,而他會包含一些很多元件的型態。

     

    下面這樣並不能執行:
    <Style TargetType="{x:Type StackPanel}">
        <Setter Property="Children">
            <Setter.Value>
                ...
            </Setter.Value>
        </Setter>
    </Style>

     

    最主要的問題就是Children 屬性並不會回傳一個 dependency property; 因此,你不能設定該屬行的style。你也許是想要定義一個新的類別繼承自StackPanel來玩成一些事情,或是你可以用template來做。

     

    下面是一個XAML檔案,有定義一個按鈕且包含一個影像檔。


    <Style TargetType="{x:Type Button}">
        <Setter Property="Content">
            <Setter.Value>
                <Image ... />
            </Setter.Value>
        </Setter>
    </Style>

    這程式會有一個錯誤訊息:"Setter does not support values derived from Visual or ContentElement." 任何物件參考到一個特定的style,只會被建立一次,並且當成是Style的一部份,所以因此可以在使用這style的一些元件之間共用。而像影像這種元件,任何繼承自Visual或是 ContentElementcan 只會有一個父係元件,且將使Style不會共享,所以若要這樣做的話可以使用自訂類別或是template來解決。

     

    Setter 元件中的Value屬性可以是一個data binding。如下:

     

     

     

    SetterWithBinding.xaml

     

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <StackPanel.Resources>
            <Style TargetType="{x:Type Button}">

                <Setter Property="FontSize" Value="{Binding ElementName=scroll, Path=Value}" />

                <Setter Property="HorizontalAlignment" Value="Center" />
                <Setter Property="Margin" Value="24" />
            </Style>
        </StackPanel.Resources>

        <ScrollBar Name="scroll" Orientation="Horizontal" Margin="24"  Minimum="11" Maximum="100" Value="24" />

        <Button>
            Button Number 1
        </Button>

        <Button>
            Button Number 2
        </Button>

        <Button>
            Button Number 3
        </Button>
    </StackPanel>

     

    按鈕的Style元件包含了字形大小的setter,且他使用了data bindingscrollbar的大小。當你改變scrollbar時也會同時改變按鈕的大小。

     

    雖然我已經有介紹過簡單的控制項和textblock元件的style,但是style也會在繪圖中扮演重要的角色。下面是一個XAML檔案定義了一個橢圓型的Style且有TargetType 。這個style定義了橢圓型的尺寸,而每橢圓形只要專注在他的位置和顏色就好!

     

    GraphicsStyles.xaml

     

    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Canvas.Resources>
            <Style TargetType="{x:Type Ellipse}">
                <Setter Property="Stroke" Value="Black" />
                <Setter Property="StrokeThickness" Value="3" />
                <Setter Property="Width" Value="96" />
                <Setter Property="Height" Value="96" />
            </Style>
        </Canvas.Resources>

        <Ellipse Canvas.Left="100" Canvas.Top="50" Fill="Blue" />
        <Ellipse Canvas.Left="150" Canvas.Top="100" Fill="Red" />
        <Ellipse Canvas.Left="200" Canvas.Top="150" Fill="Green" />
        <Ellipse Canvas.Left="250" Canvas.Top="100" Fill="Cyan" />
        <Ellipse Canvas.Left="300" Canvas.Top="50" Fill="Magenta" />
    </Canvas>

     

    假如你需要繪出一系列的水平線,類似筆記本那種。Style就可以包含一個Setter來定義Stroke顏色,當然,因為每個水平線都用一樣的X座標,也可以共用這些位置。

     

    <Style TargetType="Line">
        <Setter Property="Stroke" Value="Blue" />
        <Setter Property="X1" Value="100" />
        <Setter Property="X2" Value="300" />
    </Style>

    然後每一個水平線的垂直位置才不同:

    <Line Y1="100" Y2="100" />
    <Line Y1="125" Y2="125" />
    <Line Y1="150" Y2="150" />
    ...

    這看起來還不錯,但是假如你需要把這些位置改變間格大小時,你就必須要每一個每一個去改Y1,Y2 要改兩個地方*N,所以這個解法就是使用RelativeSource去連結到一個參考的來源,這樣就不需要去修改。這是前面一張所討論到的。

     

    你可以設定Y2的屬性來指定和Y1一樣,用下面的語法:

    <Setter Property="Y2"
            Value="{Binding RelativeSource={RelativeSource self}, Path=Y1}" />

    接下來你只需要設定Y1,這樣就會套用到Y2!

     

    下面是一個範例:

     

     

    DrawGrid.xaml

     

    [View full width]

    <!-- ===========================================
          DrawGrid.xaml (c) 2006 by Charles Petzold
         =========================================== -->
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Canvas.Resources>
            <Style x:Key="base" TargetType="Line">
                <Setter Property="Stroke" Value="Blue" />
            </Style>

            <Style x:Key="horz" TargetType="Line"
                   BasedOn="{StaticResource base}">
                <Setter Property="X1" Value="100" />
                <Setter Property="X2" Value="300" />
                <Setter Property="Y2"  Value="{Binding RelativeSource={RelativeSource self},                                   Path=Y1}" />
            </Style>

            <Style x:Key="vert" TargetType="Line"
                   BasedOn="{StaticResource base}">
                <Setter Property="Y1" Value="100" />
                <Setter Property="Y2" Value="300" />
                <Setter Property="X2" Value="{Binding RelativeSource={RelativeSource self},
                                        Path=X1}" />
            </Style>
        </Canvas.Resources>

        <Line Style="{StaticResource horz}" Y1="100" />
        <Line Style="{StaticResource horz}" Y1="125" />
        <Line Style="{StaticResource horz}" Y1="150" />
        <Line Style="{StaticResource horz}" Y1="175" />
        <Line Style="{StaticResource horz}" Y1="200" />
        <Line Style="{StaticResource horz}" Y1="225" />
        <Line Style="{StaticResource horz}" Y1="250" />
        <Line Style="{StaticResource horz}" Y1="275" />
        <Line Style="{StaticResource horz}" Y1="300" />

        <Line Style="{StaticResource vert}" X1="100" />
        <Line Style="{StaticResource vert}" X1="125" />
        <Line Style="{StaticResource vert}" X1="150" />
        <Line Style="{StaticResource vert}" X1="175" />
        <Line Style="{StaticResource vert}" X1="200" />
        <Line Style="{StaticResource vert}" X1="225" />
        <Line Style="{StaticResource vert}" X1="250" />
        <Line Style="{StaticResource vert}" X1="275" />
        <Line Style="{StaticResource vert}" X1="300" />
    </Canvas>

     

    雖然你應該要用c#來處理這些重複出現的元件,但因為這個元件重複的很少,所以在此使用XAML是合理的。

     

    Style屬性不是只有針對FrameworkElement被定義,也會定義在FrameworkContentElement,也就是說你可以定義從TextElement繼承下來的style類別。這可以讓你可以使用FlowDocument來格式化一些項目當成是style! 下面是一小片段的文章,而可以用兩個Paragraph  style來定義他們。

     

     

    DocumentStyles.xaml

    <!-- =================================================

          DocumentStyles.xaml (c) 2006 by Charles Petzold

         ================================================= -->

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

          Title="I. Down the Rabbit-Hole">

        <Page.Resources>

     

            <Style TargetType="{x:Type Paragraph}" x:Key="Normal">

                <Setter Property="TextIndent" Value="0.25in" />

            </Style>

     

            <Style TargetType="{x:Type Paragraph}" x:Key="ChapterHead">

                <Setter Property="TextAlignment" Value="Center" />

                <Setter Property="FontSize" Value="16pt" />

            </Style>

     

        </Page.Resources>

        <FlowDocumentReader>

            <FlowDocument>

                <Paragraph Style="{StaticResource ChapterHead}">

                     Chapter I

                </Paragraph>

                <Paragraph Style="{StaticResource ChapterHead}">

                     Down the Rabbit-Hole

                </Paragraph>

                <Paragraph Style="{StaticResource Normal}">

                    Alice was beginning to get very tired of sitting by

                    her sister on the bank, and of having nothing to do:

                    once or twice she had peeped into the book her sister

                    was reading, but it had no pictures or conversations

                    in it, &#x201C;and what is the use of a book,&#x201D;

                    thought Alice, &#x201C;without pictures or

                    conversations?&#x201D;

                </Paragraph>

                <Paragraph Style="{StaticResource Normal}">

                    ...

                </Paragraph>

            </FlowDocument>

        </FlowDocumentReader>

    </Page>

     

     

     

    當然你可以會覺得這沒有啥,其他的style也可以做到。

    雖然Setter是在Style中相當常見,而你也可以使用EventSetter 元件來設定一個特定的routed event 的event handler。這是另外一種方式你可以在多個元件中共享事件的處理。下面是一個範例。

     

     

     

    EventSetterDemo.xaml

     

     

    <!-- ==================================================

          EventSetterDemo.xaml (c) 2006 by Charles Petzold

         ================================================== -->

    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

            x:Class="Petzold.EventSetterDemo.EventSetterDemo"

            Title="EventSetter Demo">

        <Window.Resources>

            <Style TargetType="{x:Type Button}">

                <Setter Property="FontSize" Value="24" />

                <Setter Property="HorizontalAlignment" Value="Center" />

                <Setter Property="Margin" Value="24" />

                <EventSetter Event="Click" Handler="ButtonOnClick" />

            </Style>

        </Window.Resources>

     

        <StackPanel>

            <Button>

                Button Number 1

            </Button>

     

            <Button>

                Button Number 2

            </Button>

     

            <Button>

                Button Number 3

            </Button>

        </StackPanel>

    </Window>

     

     

     

    The C# part of the Window class contains the event handler referred to in the EventSetter element.

     

    EventSetterDemo.cs

     

    [View full width]

    //------------------------------------------------
    // EventSetterDemo.cs (c) 2006 by Charles Petzold
    //------------------------------------------------
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;

    namespace Petzold.EventSetterDemo
    {
        public partial class EventSetterDemo : Window
        {
            [STAThread]
            public static void Main()
            {
                Application app = new Application();
                app.Run(new EventSetterDemo());
            }
            public EventSetterDemo()
            {
                InitializeComponent();
            }
            void ButtonOnClick(object sender,
     RoutedEventArgs args)
            {
                Button btn = args.Source as Button;
                MessageBox.Show("The button labeled "
     + btn.Content +
                                " has been clicked",
     Title);
            }
        }
    }

     

     

    我之前有提過Style有定義六個屬性,我現在已經提過了 Setters, Resources, TargetType, and BasedOn。有一個唯讀的IsSealed 屬性會當Seal 方法被呼叫的時候會變成true.也就是這個style被使用的時候會發生。最後一個屬性叫做Triggers,可以讓你控制一個元件或是控制項要如何回應元件屬性的改變,或是根據data binding的改變或是事件的處理。

     

    當第一個setters 和triggers 相遇的時候,通常會很容易搞混。這兩個一般都會在一起。當元件被第一次建立的時候會讓Setter發生效用。而  Trigger只會當一些事情發生的時候才會去設定屬性,也就是在這些屬性被觸發改變的時候。

     

    Style 的Triggers 屬性是TriggerCollection型態,也就是一個TriggerBase型態的集合物件。這個類別繼承自抽象的TriggerBase ,如下:

     

    Object

        DispatcherObject (abstract)

     

          DependencyObject

     

          TriggerBase (abstract)

     

                 DataTrigger

     

                 EventTrigger

     

                 MultiDataTrigger

     

                 MultiTrigger

     

                 Trigger

     

    本章中我不會提到的是EventTrigger,這會造成一個控制項或是元件在一個指定的事件發生時產生改變。一般 EventTrigger 會用在動畫處理,所以我會在第30章中解說。. (你也許已經注意到FrameworkElement 本身有定義一個屬性叫做 Triggers. 我也不會在本章中討論這個屬性,因為這個Triger物件也是 EventTrigger型態)

     

    最常見的TriggerBase 所繼承下來的是Trigger,會指出一個控制項或是元件如何對特定的屬性改變實做出反應。很經常的是,這些屬性包含了使用者輸入,例如針對IsMouseOver屬性。這個 Trigger 會透過Setter定義來對某些屬性做改變。下面是一般這樣的定義:

     

    <Style.Triggers>
        <Trigger Property="Control.IsMouseOver" Value="True">
            <Setter Property="Control.FontStyle" Value="Italic" />
            <Setter Property="Control.Foreground" Value="Blue" />
        </Trigger>
    </Style.Triggers>

     

    注意到 Style.Triggers屬性元件需要定義在Style 定義中,其Trigger 元件的Property 和Value 屬性會指出在何時會觸發,以及他的值應該要是啥值來做出反應。上面就是寫當IsMouseOver=true的時候會觸發! 雖然你沒有被限制要用Boolean屬性,但通常是這樣。而Trigger所指定的屬性必須要用 dependency property來回傳。

     

    就像Style一樣,Trigger 也幼一個屬性叫做Setters,並且和Style一樣,這個屬性是Trigger的內容,且Trigger可以包含很多個。這兩個Setter屬性指出當觸發的時候要把控制項設定其值。 (Trigger 也有一個屬性叫做 SourceName, 但是你不會把這個屬性用在這個地方,你會在template的地方使用,這會在下一張提到。)

    下面是個範例:

     

     

     

     

     

     

    StyleWithTriggers.xaml

     

    <!-- ====================================================

          StyleWithTriggers.xaml (c) 2006 by Charles Petzold

         ==================================================== -->

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style x:Key="normal">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

     

                <Style.Triggers>

                    <Trigger Property="Control.IsMouseOver" Value="true">

                        <Setter Property="Control.FontStyle" Value="Italic" />

                        <Setter Property="Control.Foreground" Value="Blue" />

                    </Trigger>

     

                    <Trigger Property="Button.IsPressed" Value="true">

                        <Setter Property="Control.Foreground" Value="Red" />

                    </Trigger>

                </Style.Triggers>

            </Style>

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 2

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 3

        </Button>

    </StackPanel>

     

     

    除了Trigger是基於IsMouseOver 屬性來做觸發之外,其他的Trigger會改變前景色。而這些屬性會當這些屬性改回去的時候回覆原始值。

     

    在某些特定的情形中,兩個Trigger的元件的順序會影響這些觸發如何運作。假如Trigger 元件設定一樣的屬性,後面的Trigger元件會覆寫前面的Trigger元件。所以假如你包了兩個Trigger屬性在同一個XAML的時候,就像上面的範例,若把兩個setter互換位子,就會發現點下去的時候就會沒有反應,因為IsMouseOver 會比較優先!

     

    假如你沒有辦法正確地判斷這些順序,你可以考慮使用MultiTrigger。MultiTrigger是類似Trigger,除了他可以同時支援觸發多個條件。

     

    MultiTriggerDemo.xaml

     

     

    <!-- ===================================================

          MultiTriggerDemo.xaml (c) 2006 by Charles Petzold

         =================================================== -->

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style x:Key="normal">

                <Setter Property="Control.FontSize" Value="24" />

                <Setter Property="Control.HorizontalAlignment" Value="Center" />

                <Setter Property="Control.Margin" Value="24" />

     

                <Style.Triggers>

                    <Trigger Property="Button.IsPressed" Value="True">

                        <Setter Property="Control.Foreground" Value="Red" />

                    </Trigger>

     

                    <MultiTrigger>

                        <MultiTrigger.Conditions>

                            <Condition Property="Control.IsMouseOver" Value="True" />

                            <Condition Property="Button.IsPressed" Value="False" />

                        </MultiTrigger.Conditions>

                        <Setter Property="Control.FontStyle" Value="Italic" />

                        <Setter Property="Control.Foreground" Value="Blue" />

                    </MultiTrigger>

     

                </Style.Triggers>

            </Style>

        </StackPanel.Resources>

     

        <Button Style="{StaticResource normal}">

            Button Number 1

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 2

        </Button>

     

        <Button Style="{StaticResource normal}">

            Button Number 3

        </Button>

    </StackPanel>

     

     

     

     

    MultiTrigger 的Conditions 屬性是一種ConditionCollection型態的物件,每個Condition 元件會標明一個屬性和一個值!當滑鼠在按鈕上面的時候按鈕文字會是藍色,且當點選的時喉會變成紅色。且也只有在滑鼠在按鈕上面的時候才會是斜體!

     

    而DataTrigger 類別是類似Trigger 除了他是用來替代屬性的binding之外!該Binding  一般而言會參考到其他元件。當這個Binding有一個特定的值的時候,DataTrigger 可以設定一個屬性,如下:

     

     

     

    StyleWithDataTrigger.xaml

     

    <!-- =======================================================

          StyleWithDataTrigger.xaml (c) 2006 by Charles Petzold

         ======================================================= -->

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style TargetType="{x:Type Button}">

                <Setter Property="FontSize" Value="24" />

                <Setter Property="HorizontalAlignment" Value="Center" />

                <Setter Property="Margin" Value="24" />

     

                <Style.Triggers>

                    <DataTrigger Binding="{Binding ElementName=txtbox,

                                                   Path=Text.Length}"

                                 Value="0">

                        <Setter Property="IsEnabled" Value="False" />

                    </DataTrigger>

                </Style.Triggers>

            </Style>

        </StackPanel.Resources>

     

        <TextBox Name="txtbox" HorizontalAlignment="Center"

                 Width="2in" Margin="24" />

     

        <Button>

            Button Number 1

        </Button>

     

        <Button>

            Button Number 2

        </Button>

     

        <Button>

            Button Number 3

        </Button>

     

    </StackPanel>

     

     

     

     

     

    這個DataTrigger 包含了一個連結到TextBox 元件,並且也指明了他的名稱。這個路徑是指到Text.Length。當他的值是0,則按鈕的IsEnabled 屬性就會是false。所以當文字方塊有輸入字的時候就會讓按鈕enable

     

    而MultiDataTrigger 可以設定多個元件來做組合條件。也就是所有的條件都符合才可以觸發。MultiDataTrigger 可以有多個條件,且這些條件類別可以定義s Property, Binding, and Value屬性,或許你也可以混合一般的 triggers 和data triggers,但其實不然。當定義一個 MultiTrigger, 你可在Condition中使用  Property 和 Value 屬性。當定義一個MultiDataTrigger, 你可以使用Binding 和 Value 屬性。下面就是一個範例,需要兩個checkbox都打勾才能讓按鈕ennable

     

     

     

     

     

     

    MultiDataTriggerDemo.xaml

     

    <!-- =======================================================

          MultiDataTriggerDemo.xaml (c) 2006 by Charles Petzold

         ======================================================= -->

    <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <StackPanel.Resources>

            <Style TargetType="{x:Type CheckBox}">

                <Setter Property="HorizontalAlignment" Value="Center" />

                <Setter Property="Margin" Value="12" />

            </Style>

     

     

            <Style TargetType="{x:Type Button}">

                <Setter Property="FontSize" Value="24" />

                <Setter Property="HorizontalAlignment" Value="Center" />

                <Setter Property="Margin" Value="12" />

                <Setter Property="IsEnabled" Value="False" />

     

                <Style.Triggers>

                    <MultiDataTrigger>

                        <MultiDataTrigger.Conditions>

                            <Condition Binding="{Binding ElementName=chkbox1,

                                                 Path=IsChecked}"

                                       Value="True" />

                            <Condition Binding="{Binding ElementName=chkbox2,

                                                 Path=IsChecked}"

                                       Value="True" />

                        </MultiDataTrigger.Conditions>

                        <Setter Property="IsEnabled" Value="True" />

                    </MultiDataTrigger>

                </Style.Triggers>

            </Style>

        </StackPanel.Resources>

     

        <CheckBox Name="chkbox1">

            Check 1

        </CheckBox>

     

        <CheckBox Name="chkbox2">

            Check 2

        </CheckBox>

     

        <Button>

            Button Number 1

        </Button>

     

        <Button>

            Button Number 2

        </Button>

     

        <Button>

            Button Number 3

        </Button>

     

    </StackPanel>

     

     

    就像你已經看過的,style可以讓你的程式更有系統地組成一些樣版。Styles可以是簡單的一行,也或許可以是一大篇讓你的應用程式的每個控制項看起來就是不一樣。當你希望為兩個元件給予相同的屬性值的時候可以使用style,且你會在改變這些值的時候瞭解到這種方便性。

    夏洛克 / Xuite日誌 / 回應(0) / 引用(0) / 好文轉寄
    回應