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

如同 styletemplate 也可以利用 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...