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

Silverlight / WPF 自訂按鈕控制項:依賴屬性綁定圖片、VisualStateManager 狀態切換 (Normal/MouseOver/Pressed/Disabled)

     編輯:ASP.NET 基礎

適用平台

  • Silverlight 4/5 (已終止支援,僅維護舊專案參考)
  • WPF (.NET Framework / .NET Core / .NET 6+) — 程式碼幾乎相同,命名空間微調
  • WinUI 3 / MAUI — 概念相同,建議改用 PointerEntered/Exited 事件或 VisualStateGroup 標準狀態

核心架構:ControlTemplate + VisualStateManager

Silverlight/WPF 採用「樣板分離」模式:邏輯類別 (C#) 定義依賴屬性 + XAML ControlTemplate 定義視覺樹與狀態動畫。狀態切換由 VisualStateManager 自動處理。

1. 邏輯類別:MyImageButton.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ImageButtonDemo
{
    /// <summary>
    /// 支援 Normal / MouseOver 兩種圖片切換的按鈕
    /// </summary>
    public class MyImageButton : Button
    {
        // 依賴屬性:正常狀態圖片
        public static readonly DependencyProperty ImageNormalProperty =
            DependencyProperty.Register(
                nameof(ImageNormal),
                typeof(ImageSource),
                typeof(MyImageButton),
                new PropertyMetadata(null));

        // 依賴屬性:滑鼠懸停圖片
        public static readonly DependencyProperty ImageHoverProperty =
            DependencyProperty.Register(
                nameof(ImageHover),
                typeof(ImageSource),
                typeof(MyImageButton),
                new PropertyMetadata(null));

        public ImageSource ImageNormal
        {
            get => (ImageSource)GetValue(ImageNormalProperty);
            set => SetValue(ImageNormalProperty, value);
        }

        public ImageSource ImageHover
        {
            get => (ImageSource)GetValue(ImageHoverProperty);
            set => SetValue(ImageHoverProperty, value);
        }

        static MyImageButton()
        {
            // 設定預設樣式鍵 (Silverlight 必須)
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(MyImageButton),
                new FrameworkPropertyMetadata(typeof(MyImageButton)));
        }

        public MyImageButton()
        {
            // Silverlight 需在建構式設定
            DefaultStyleKey = typeof(MyImageButton);
        }
    }
}

2. 樣式範本:Themes/Generic.xaml (WPF) 或 Styles.xaml (Silverlight)

<ResourceDictionary
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ImageButtonDemo">

    <Style TargetType="local:MyImageButton">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyImageButton">
                    <Grid Background="{TemplateBinding Background}">
                        <!-- 視覺狀態群組 -->
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <!-- Normal: 預設狀態,顯示 ImageNormal -->
                                <VisualState x:Name="Normal" />
                                
                                <!-- MouseOver: 切換顯示 ImageHover -->
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames 
                                            Storyboard.TargetName="PART_HoverImage"
                                            Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames 
                                            Storyboard.TargetName="PART_NormalImage"
                                            Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                
                                <!-- Pressed: 可選擇加入按下效果 (縮放/陰影) -->
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimation 
                                            Storyboard.TargetName="PART_RootGrid"
                                            Storyboard.TargetProperty="RenderTransform.ScaleX"
                                            To="0.97" Duration="0:0:0.05" />
                                        <DoubleAnimation 
                                            Storyboard.TargetName="PART_RootGrid"
                                            Storyboard.TargetProperty="RenderTransform.ScaleY"
                                            To="0.97" Duration="0:0:0.05" />
                                    </Storyboard>
                                </VisualState>
                                
                                <!-- Disabled: 灰階/半透明 -->
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimation 
                                            Storyboard.TargetName="PART_NormalImage"
                                            Storyboard.TargetProperty="Opacity"
                                            To="0.4" Duration="0" />
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

                        <Grid x:Name="PART_RootGrid" RenderTransformOrigin="0.5,0.5">
                            <Grid.RenderTransform>
                                <ScaleTransform ScaleX="1" ScaleY="1" />
                            </Grid.RenderTransform>
                            
                            <!-- 正常圖片 (預設顯示) -->
                            <Image x:Name="PART_NormalImage"
                                   Source="{TemplateBinding ImageNormal}"
                                   Stretch="Uniform"
                                   Visibility="Visible" />
                            
                            <!-- 懸停圖片 (預設隱藏) -->
                            <Image x:Name="PART_HoverImage"
                                   Source="{TemplateBinding ImageHover}"
                                   Stretch="Uniform"
                                   Visibility="Collapsed" />
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3. 使用範例 (MainPage.xaml / MainWindow.xaml)

<UserControl x:Class="ImageButtonDemo.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ImageButtonDemo"
    Width="400" Height="300">

    <Grid Background="White">
        <local:MyImageButton 
            Width="120" Height="120"
            HorizontalAlignment="Center" VerticalAlignment="Center"
            ImageNormal="/Assets/btn_normal.png"
            ImageHover="/Assets/btn_hover.png"
            ToolTipService.ToolTip="點擊執行動作"
            Click="MyImageButton_Click" />
    </Grid>
</UserControl>

4. 事件處理

private void MyImageButton_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("自訂圖片按鈕被點擊了!");
    // 執行業務邏輯...
}

WPF 專屬優化

WPF 可使用 Trigger 簡化無需 VisualStateManager (但 VSM 更標準、支援 Blend 設計):

<Style TargetType="local:MyImageButton">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyImageButton">
                <Grid>
                    <Image x:Name="NormalImg" Source="{TemplateBinding ImageNormal}" Stretch="Uniform" />
                    <Image x:Name="HoverImg" Source="{TemplateBinding ImageHover}" Stretch="Uniform" Visibility="Collapsed" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="NormalImg" Property="Visibility" Value="Collapsed" />
                        <Setter TargetName="HoverImg" Property="Visibility" Value="Visible" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="RenderTransform">
                            <Setter.Value><ScaleTransform ScaleX="0.95" ScaleY="0.95" /></Setter.Value>
                        </Setter>
                        <Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

現代化遷移建議 (.NET 6+ / WinUI 3 / MAUI)

平台推薦做法
WPF (.NET 6+)繼續使用 ControlTemplate + VSM,或改用 Button + ContentTemplate + DataTrigger 純 XAML 實現
WinUI 3 / Windows App SDK使用 Button + VisualStateGroup (CommonStates: Normal, PointerOver, Pressed, Disabled),PointerOver 取代 MouseOver
.NET MAUI使用 ImageButton (內建 Source/HoverSource/PressedSource/DisabledSource) 或自訂 ControlTemplate
Blazor / WebCSS :hover / :active 切換背景圖片,或元件庫 (MudBlazor, Radzen) 內建按鈕變體

Silverlight 專案遷移關鍵點

  • 命名空間:System.Windows.Controls → 同,但組件不同
  • 依賴屬性註冊:WPF 用 OverrideMetadata,Silverlight 用建構式 DefaultStyleKey
  • 資源字典:WPF 放 Themes/Generic.xaml,Silverlight 合併在 App.xaml 或頁面 Resources
  • 圖片路徑:Silverlight 用相對 URI (/Assets/img.png),WPF 用 Pack URI (pack://application:,,,/Assets/img.png)
  • 事件模型:Silverlight 無 PreviewMouseDown 等通道事件
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved