首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 Store Apps 開發實戰(15):控件 UI 進階 — 字體繼承、樣式、控件模板、系統資源與視覺狀態

     編輯:關於.NET

前言

在上一篇掌握控件核心基石後,本文將聚焦於 控件的視覺表現層:字體屬性如何在視覺樹中自動繼承、Style 如何集中管理外觀、ControlTemplate 如何徹底重塑控件結構、系統內建資源 如何讓 App 符合 Windows 設計語言、以及 VisualState / VisualStateManager 如何實現豐富的互動回饋。

一、字體繼承 — 讓排版輕鬆統一

Control 基類定義了 FontFamily、FontSize、FontWeight、FontStyle、Foreground 等字體相關屬性,這些屬性在視覺樹中具有 屬性值繼承 特性:子元素若未顯式設定,會自動使用最近祖先元素的值。

範例:頁面 → UserControl → TextBlock 的字體級聯

<Page
    x:Class="XamlDemo.Controls.UI.FontInherit"
    FontSize="100">  <!-- 頁面層級設定 -->
    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">
            <!-- 繼承 Page 的 FontSize=100 -->
            <TextBlock Text="FontSize = 100" />

            <UserControl FontSize="50">  <!-- 區域覆寫 -->
                <!-- 繼承 UserControl 的 FontSize=50 -->
                <TextBlock Text="FontSize = 50" />
            </UserControl>
        </StackPanel>
    </Grid>
</Page>

💡 實務建議:在 Page 或 App.xaml 設定基礎字體大小,子控件只需在必要時覆寫,即可全站風格一致且易維護。

二、Style — 集中化外觀管理

Style 針對特定 TargetType 定義一組 Setter,可套用至任意符合型別的 FrameworkElement。

核心屬性

  • x:Key:顯式樣式鍵值,需透過 {StaticResource Key} 套用
  • TargetType:目標型別(必填)
  • BasedOn:繼承另一個樣式,實現樣式層級複用
  • Setter.Property / Value:設定屬性值

範例:顯式、隱式、動態切換樣式

<Page.Resources>
    <!-- 顯式樣式:需指定 Key -->
    <Style x:Key="MyTextStyle" TargetType="TextBox">
        <Setter Property="FontSize" Value="24"/>
        <Setter Property="Foreground" Value="#0000FF"/>
    </Style>

    <Style x:Key="MyTextStyle2" TargetType="TextBox">
        <Setter Property="FontSize" Value="24"/>
        <Setter Property="Foreground" Value="#FF0000"/>
    </Style>

    <!-- 隱式樣式:無 Key,自動套用於所有目標型別 -->
    <Style TargetType="TextBox" BasedOn="{StaticResource MyTextStyle}">
        <Setter Property="TextAlignment" Value="Center"/>
    </Style>
</Page.Resources>

<StackPanel>
    <!-- 套用顯式樣式 -->
    <TextBox Text="我是 TextBox" Style="{StaticResource MyTextStyle}" />

    <!-- 自動套用隱式樣式 -->
    <TextBox Text="我是 TextBox" />

    <Button Content="動態切換樣式" Click="btnChangeStyle_Click" />
</StackPanel>

後台動態切換

private void btnChangeStyle_Click(object sender, RoutedEventArgs e)
{
    // 從頁面資源取得樣式並套用
    txtStyleDemo.Style = (Style)root.Resources["MyTextStyle2"];
}

三、ControlTemplate — 徹底重塑控件視覺樹

ControlTemplate 定義控件的完整視覺結構,透過 ContentPresenter 呈現 Content,透過 TemplateBinding 綁定控件屬性(如 Background、Foreground)。

三種套用方式

  1. 資源引用:Template="{StaticResource MyControlTemplate}"
  2. 內聯定義:直接在 Button.Template 內撰寫
  3. 包裹在 Style 中:Style.Setter.Property="Template"

範例:自訂 Button 模板

<Grid.Resources>
    <ControlTemplate x:Key="MyControlTemplate" TargetType="Button">
        <Border BorderBrush="Red" BorderThickness="1">
            <Grid Background="{TemplateBinding Background}">
                <ContentPresenter HorizontalAlignment="Right" Foreground="Red" />
            </Grid>
        </Border>
    </ControlTemplate>

    <Style x:Key="MyStyle" TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border BorderBrush="Red" BorderThickness="1">
                        <Grid Background="{TemplateBinding Background}">
                            <ContentPresenter HorizontalAlignment="Right" Foreground="Red" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Grid.Resources>

<StackPanel>
    <Button Content="資源引用" Template="{StaticResource MyControlTemplate}" Background="Yellow" />
    <Button Content="內聯模板" Background="Yellow">
        <Button.Template>
            <ControlTemplate>
                <Border BorderBrush="Red" BorderThickness="1">
                    <Grid Background="Yellow">
                        <ContentPresenter HorizontalAlignment="Right" Foreground="Red" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Button.Template>
    </Button>
    <Button Content="Style 包裹模板" Style="{StaticResource MyStyle}" Background="Yellow" />
    <Button Content="動態切換模板" Click="btnChangeControlTemplate_Click" />
</StackPanel>

後台動態切換模板

private void btnChangeControlTemplate_Click(object sender, RoutedEventArgs e)
{
    btnControlTemplateDemo.Template = (ControlTemplate)root.Resources["MyControlTemplate2"];
}

四、系統內建資源 — 遵循 Windows 設計語言的捷徑

WinRT 提供大量 ThemeResource / StaticResource 涵蓋色彩、筆刷、字體、尺寸、動畫緩動等,直接引用即可確保 App 與系統風格一致、支援高對比度、自動適配深/淺色主題。

常用系統資源範例

<Border Padding="12,4,12,4"
        BorderThickness="{StaticResource ButtonBorderThemeThickness}"
        BorderBrush="{StaticResource ButtonBorderThemeBrush}"
        Background="{StaticResource ButtonBackgroundThemeBrush}">
    <TextBlock Text="我是一個長得像 Button 的 TextBlock"
               FontWeight="SemiBold"
               FontFamily="{StaticResource ContentControlThemeFontFamily}"
               FontSize="{StaticResource ControlContentThemeFontSize}"
               Foreground="{StaticResource ButtonForegroundThemeBrush}" />
</Border>
<Button Content="真正的 Button" />

💡 提示:在 Visual Studio XAML 編輯器輸入 {StaticResource 會自動列出所有可用系統資源。

五、VisualState 與 VisualStateManager — 互動狀態的視覺化

控件在不同互動狀態(Normal、PointerOver、Pressed、Disabled、Focused...)下呈現不同外觀,透過 VisualStateGroup 分組、VisualState 定義狀態動畫、VisualTransition 定義狀態轉場,最終由 VisualStateManager.GoToState 驅動。

控件模板中的 VisualState 定義

<ControlTemplate x:Key="myControlTemplate" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal" />
                <VisualState x:Name="Disabled" />

                <VisualState x:Name="PointerOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="borderBrush"
                                        Storyboard.TargetProperty="Color"
                                        To="Green" />
                    </Storyboard>
                </VisualState>

                <VisualState x:Name="Pressed">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames
                            Storyboard.TargetName="grid"
                            Storyboard.TargetProperty="Background">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0.3"
                                Value="{StaticResource ButtonPressedBackgroundThemeBrush}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames
                            Storyboard.TargetName="contentPresenter"
                            Storyboard.TargetProperty="Foreground">
                            <DiscreteObjectKeyFrame KeyTime="0"
                                Value="{StaticResource ButtonPressedForegroundThemeBrush}"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>

                <!-- 狀態轉場:Normal → PointerOver 1 秒彈性緩動 -->
                <VisualStateGroup.Transitions>
                    <VisualTransition To="PointerOver" GeneratedDuration="0:0:1">
                        <VisualTransition.GeneratedEasingFunction>
                            <ElasticEase EasingMode="EaseInOut" />
                        </VisualTransition.GeneratedEasingFunction>
                    </VisualTransition>
                </VisualStateGroup.Transitions>
            </VisualStateGroup>

            <VisualStateGroup x:Name="FocusStates">
                <VisualState x:Name="Focused" />
                <VisualState x:Name="Unfocused" />
                <VisualState x:Name="PointerFocused" />
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Border x:Name="border" BorderThickness="10">
            <Border.BorderBrush>
                <SolidColorBrush x:Name="borderBrush" Color="Red" />
            </Border.BorderBrush>
            <Grid x:Name="grid" Background="{TemplateBinding Background}" Width="500" Height="200">
                <ContentPresenter x:Name="contentPresenter"
                                  VerticalAlignment="Center" HorizontalAlignment="Center"
                                  FontSize="24.667"
                                  Foreground="{TemplateBinding Foreground}" />
            </Grid>
        </Border>
    </Grid>
</ControlTemplate>

程式碼觸發狀態切換

private void btnVisualStateManager_Click(object sender, RoutedEventArgs e)
{
    // 強制將 btnDemo 切換到 PointerOver 狀態,並啟用轉場動畫
    VisualStateManager.GoToState(btnDemo, "PointerOver", true);
}

自定義控件開發者注意事項

  • 在控件邏輯中呼叫 VisualStateManager.GoToState(this, "StateName", true) 切換狀態
  • 狀態名稱需與模板中 VisualState.x:Name 完全一致
  • 不同控件類型擁有不同的標準狀態群組(如 Button 有 CommonStates、FocusStates)

六、小結

本文完整串聯了控件視覺表現的完整鏈路:

  1. 字體繼承:利用屬性值繼承實現全站排版一致性
  2. Style:集中管理屬性設定,支援繼承、隱式/顯式套用、動態切換
  3. ControlTemplate:徹底替換控件視覺樹,三種套用方式任君選擇
  4. 系統資源:零成本遵循 Windows 設計語言、支援主題切換與無障礙
  5. VisualState / VisualStateManager:聲明式定義狀態動畫與轉場,程式碼一行觸發

善用這些機制,能讓你的 App 擁有專業、一致、可維護的視覺體驗,同時大幅降低重複樣式碼。


原始碼下載:https://files.cnblogs.com/webabcd/Windows8.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved