2012年1月2日

WPF Templates


Style 只能就 element 已有的 property 設定,template 可以完全取代 element 的視覺外觀 (visual tree),而不會改變原有的功能與程式碼。Control templates 都是由 ControlTemplate class 所衍生的。

Control Templates

利用 ControlTemplate 定義 control 的外觀,並以 x:Key 設定 template 的名稱。
<Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="buttonTemplate">
            <Grid Width="100" Height="30">
                <Rectangle Fill="Bisque" Stroke="Brown" RadiusX="5" RadiusY="5"/>
                <Ellipse Width="24" Height="24" Fill="Brown" HorizontalAlignment="Left" Margin="3"/>
            </Grid>
        </ControlTemplate>
    </Grid.Resources>
    <Button Template="{StaticResource buttonTemplate}"/>
</Grid>

Control Template Trigger

如同 style,template 也可以利用 trigger 來動態調整 control 的外觀。
<Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="buttonTemplate">
            <Grid Width="100" Height="30">
                <Rectangle Fill="Bisque" Stroke="Brown" RadiusX="5" RadiusY="5"/>
                <Ellipse x:Name="Icon" Width="24" Height="24" Fill="Brown" HorizontalAlignment="Left" Margin="3"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsMouseOver" Value="True">
                    <Setter TargetName="Icon" Property="Fill" Value="Red"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Grid.Resources>
    <Button Template="{StaticResource buttonTemplate}">OK</Button>
</Grid>

Restricting the Target Type

與 style 相同,template 也可以指定要套用的類別,但在  dictionary 中,不允許移除 x:Key 的設定。
<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
    <Grid Width="100" Height="30">
        …
    </Grid>
</ControlTemplate>

Content Property

在前面的範例中,我們希望能顯示出原有 Button.Content 中的文字, 則可以透過 TemplateBinding 來達成。
<TextBlock Text="{TemplateBinding Content}" Margin="36,0,0,0" VerticalAlignment="Center"/>

不過要特別注意,Button 的 Content 不一定是文字型態,因此使用 ContentControl 或 ContentPresenter 是比較正確的寫法。ContentPresenter 比 ContentControl 輕些,因為 ControlControl 在 ContentPresenter 外面又包了一層物件。
<ContentPresenter Content="{TemplateBinding Content}"/>

Other Properties

除了 content property,也可以沿用原來物件的其他 property,例如 Background, FontSize,..等…。下列的範例,要特別注意,矩形的背景,直接使用 TemplateBinding 來達成,而圓形使用漸層上色,必須將 brush 轉換成 color,因此語法不同:
<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
    <Grid Width="100" Height="30">
        <Rectangle Fill="{TemplateBinding Background}" Stroke="Brown" RadiusX="5" RadiusY="5"/>
        <Ellipse x:Name="Icon" Width="24" Height="24" HorizontalAlignment="Left" Margin="3">
            <Ellipse.Fill>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0, 1">
                    <GradientStop Offset="0"
                                    Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground.Color}"/>
                    <GradientStop Offset="1" Color="Red"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <ContentPresenter Content="{TemplateBinding Content}" Margin="36,0,0,0" VerticalAlignment="Center"/>
    </Grid>
</ControlTemplate>
在上面的範例中,使用 Binding 取代 TemplateBinding,可以獲得更細密的內容。另外,在使用 trigger 時,因為是在 visual tree 外面動態處理,只能使用 Binding 而無法使用 TemplateBinding。

Control Parts

許多較複雜的 control 是由不同的 part 所組成,例如 ComboBox 由 PART_EditableTextBox 與 PART_Popup 組成。WPF 內建的 control 皆以 PART_xxx 命名。當設計 template 時,可能需要注意到這些細節。

Control States

在 WPF 4.0 之後,增加了 State 的設計,可以透過 VisualStateManager.GoToState 切換狀態。一個 control 可以有多個 state group,每個 group 又有多個 state。例如 Button 的 CommonStates group 包含了 Normal, MouseOver, Pressed, Disabled 四種狀態,而 FocusStates group 包含了 Unfocused, Focused 兩種狀態。在任何時候,Button 一定會在每一組 group 中的某個 state 中,例如 MouseOver & Focused。
設計 template 時,可以使用 <VisualStateManager.VirtualStateGroup> 來設計各種狀態的轉換方式。

Mixing Templates With Styles

將 template 設置在 style 中,可以用來取代 WPF 預設的 style,使用方式如下。注意在下列的範例中,ControlTemplate 可以不用 x:Key 來指定 template 的名稱。
<Grid>
    <Grid.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        …
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <Button>OK</Button>
</Grid>

沒有留言:

Deploying Vue & .NET with Google OAuth on GCP Cloud Run

Deploying Vue & .NET with Google OAuth on GCP Cloud Run Deploying Vue & .NET with Google OAuth on GCP Cloud Run...