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

WPF 自訂控件 Marquee 實作:跑馬燈效果的完整封裝與使用指南

     編輯:關於.NET

HTML `` 標籤提供簡單的文字捲動效果,WPF 雖可用 Animation/Storyboard 實現,但每次都需撰寫大量 XAML 與程式碼。本文示範繼承 ContentControl 開發可復用的 Marquee 自訂控件,支援任意內容 (文字、圖形、控件)、四大行為模式、完整依賴屬性綁定,使用如同原生控件般簡單。

控件繼承與核心屬性

public class Marquee : ContentControl {
    // 依賴屬性
    public static readonly DependencyProperty DirectionProperty = ...; // Left/Right
    public static readonly DependencyProperty BehaviorProperty = ...;  // Scroll/Slide/Alternate
    public static readonly DependencyProperty ScrollAmountProperty = ...; // 像素/步
    public static readonly DependencyProperty ScrollDelayProperty = ...;  // 毫秒/步
    public static readonly DependencyProperty LoopProperty = ...;       // 循環次數
}

繼承 ContentControl 使 Content 可放入任何 UIElement (TextBlock、Rectangle、Button 等)。

三種動畫實作策略比較

選擇 DispatcherTimer,Tick 事件中計算位移並更新 TranslateTransform.X。

核心動畫邏輯:OnUpdateContentPosition

protected virtual void OnUpdateContentPosition() {
    if (_contentHost == null) return;
    double contentWidth = _contentHost.DesiredSize.Width;
    double leftBound, rightBound;
    
    // 邊界計算
    if (Behavior == MarqueeBehavior.Alternate || Behavior == MarqueeBehavior.Slide) {
        leftBound = 0;
        rightBound = ActualWidth - contentWidth;
    } else { // Scroll
        leftBound = -contentWidth; // 完全捲出視野
        rightBound = ActualWidth;
    }
    
    if (_currentLoop < Loop) {
        if (_currentDirection == Direction.Left) {
            _contentTranslate.X -= ScrollAmount;
            if (_contentTranslate.X <= leftBound) {
                if (Behavior == MarqueeBehavior.Scroll || Behavior == MarqueeBehavior.Slide)
                    _contentTranslate.X = rightBound; // 瞬間跳回另一側
                else
                    _currentDirection = Direction.Right; // Alternate 反向
                _currentLoop += _loopCounter;
            }
        } else { // Direction.Right
            _contentTranslate.X += ScrollAmount;
            if (_contentTranslate.X >= rightBound) {
                if (Behavior == MarqueeBehavior.Scroll || Behavior == MarqueeBehavior.Slide)
                    _contentTranslate.X = leftBound;
                else
                    _currentDirection = Direction.Left;
                _currentLoop += _loopCounter;
            }
        }
    } else {
        // 循環結束:確保內容可見
        _contentTranslate.X = Math.Max(0, Math.Min(_contentTranslate.X, ActualWidth - contentWidth));
    }
}

關鍵點:

  • 使用 TranslateTransform 位移 ContentPresenter,不改變佈局
  • DesiredSize.Width 取得內容實際寬度
  • 三種行為的邊界與反轉邏輯分離清晰
  • 循環計數器 _loopCounter 處理往返一次算一循環 (Alternate) 或單向一循環 (Scroll/Slide)

使用範例

<!-- 命名空間對應 xmlns:l="clr-namespace:YourNamespace" -->
<l:Marquee Content="123" Direction="Right" Behavior="Scroll" ScrollAmount="20" ScrollDelay="500"/>

<l:Marquee Direction="Right" Behavior="Slide">
    <Rectangle Width="20" Height="20" Fill="Blue"/>
</l:Marquee>

<l:Marquee>
    <Button Content="Button" Width="50"/>
</l:Marquee>

注意事項與擴充建議

  • Loop 結束時未停止 Timer,視窗 Resize 會重新播放;建議在 ResetAnimation 中重啟、Stop 方法中停止
  • 可擴充 Pause/Resume、MouseEnter 暫停、垂直捲動、無縫循環 (雙份內容)
  • 高頻率 Timer (ScrollDelay < 16ms) 可能影響 UI 回應,考慮 CompositionTarget.Rendering 替代
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
策略優點缺點選擇
Animation + Storyboard聲明式、GPU 加速動態參數綁定複雜、ScrollDelay 難控制✗
CompositionTarget.Rendering幀級控制高頻率事件、ScrollDelay 需自行計時✗
DispatcherTimer精確延遲控制、邏輯直觀UI 執行緒定時器、極高頻率可能影響回應✓