| 特性 | 說明 |
| 命名空間 | System.Windows.Forms |
| 執行緒模型 | **UI 執行緒** (訊息迴圈派送 WM_TIMER 訊息) |
| 精確度 | 約 15-55 毫秒 (受系統時鐘解析度限制),不適合高精度動畫 |
| 安全性 | Tick 事件在 UI 執行緒,**可直接操作 UI 控制項** |
| 可見性 | 設計時可見於元件托盤,**執行時不可見** |
| 核心屬性 | Interval (毫秒)、Enabled (啟用/停止) |
| 核心事件 | Tick (間隔到達觸發) |
| 核心方法 | Start() / Stop() (等同設定 Enabled) |
視窗飄動邏輯設計
- **初始位置**:
Form_Load 設定 DesktopLocation = new Point(0, 240) (螢幕座標)。
- **Timer1 (向右)**:
Interval=10ms,每 Tick DesktopLocation.X += 1,到達 X=550 時停止 Timer1、啟動 Timer2。
- **Timer2 (向左)**:
Interval=10ms,每 Tick DesktopLocation.X -= 1,到達 X=-150 時停止 Timer2、啟動 Timer1。
- **無限循環**:兩 Timer 交替啟用,形成來回飄動。
- **停止按鈕**:同時
Stop() 兩個 Timer。
完整程式碼:FloatingForm 專案
using System;
using System.Drawing;
using System.Windows.Forms;
namespace FloatingFormDemo
{
public partial class MainForm : Form
{
private readonly Timer _timerRight = new Timer(); // 向右飄動
private readonly Timer _timerLeft = new Timer(); // 向左飄動
private readonly Label _lblMessage = new Label();
private readonly Button _btnStop = new Button();
// 邊界常數
private const int RIGHT_BOUND = 550;
private const int LEFT_BOUND = -150;
private const int TIMER_INTERVAL = 10; // 毫秒
private const int STEP = 1; // 每次移動像素
public MainForm()
{
InitializeComponent();
InitializeTimers();
InitializeUI();
}
private void InitializeComponent()
{
this.SuspendLayout();
this.AutoScaleDimensions = new SizeF(6F, 13F);
this.AutoScaleMode = AutoScaleMode.Font;
this.ClientSize = new Size(352, 70);
this.Name = "MainForm";
this.Text = "C# Timer 飄動視窗示範";
this.Load += MainForm_Load;
this.ResumeLayout(false);
}
private void InitializeTimers()
{
// Timer1:向右
_timerRight.Interval = TIMER_INTERVAL;
_timerRight.Tick += TimerRight_Tick;
// Timer2:向左
_timerLeft.Interval = TIMER_INTERVAL;
_timerLeft.Tick += TimerLeft_Tick;
}
private void InitializeUI()
{
// 標籤
_lblMessage.Font = new Font("微軟正黑體", 14F, FontStyle.Bold, GraphicsUnit.Point);
_lblMessage.Location = new Point(8, 38);
_lblMessage.Size = new Size(344, 40);
_lblMessage.Text = "C# Timer 實作視窗飄動特效!";
_lblMessage.AutoSize = false;
_lblMessage.TextAlign = ContentAlignment.MiddleCenter;
// 停止按鈕
_btnStop.Font = new Font("微軟正黑體", 10F);
_btnStop.Location = new Point(1, 8);
_btnStop.Size = new Size(80, 25);
_btnStop.Text = "停止飄動";
_btnStop.Click += BtnStop_Click;
this.Controls.Add(_lblMessage);
this.Controls.Add(_btnStop);
}
private void MainForm_Load(object sender, EventArgs e)
{
// 設定初始位置 (螢幕座標)
this.DesktopLocation = new Point(0, 240);
// 啟動向右飄動
_timerRight.Start();
}
///
/// 向右飄動:X += 1,到達右邊界切換向左
///
private void TimerRight_Tick(object sender, EventArgs e)
{
Point newLoc = new Point(this.DesktopLocation.X + STEP, this.DesktopLocation.Y);
this.DesktopLocation = newLoc;
if (newLoc.X >= RIGHT_BOUND)
{
_timerRight.Stop();
_timerLeft.Start();
}
}
///
/// 向左飄動:X -= 1,到達左邊界切換向右
///
private void TimerLeft_Tick(object sender, EventArgs e)
{
Point newLoc = new Point(this.DesktopLocation.X - STEP, this.DesktopLocation.Y);
this.DesktopLocation = newLoc;
if (newLoc.X <= LEFT_BOUND)
{
_timerLeft.Stop();
_timerRight.Start();
}
}
private void BtnStop_Click(object sender, EventArgs e)
{
_timerRight.Stop();
_timerLeft.Stop();
_btnStop.Text = "已停止";
_btnStop.Enabled = false;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_timerRight?.Dispose();
_timerLeft?.Dispose();
}
base.Dispose(disposing);
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
關鍵技術細節解析
1. DesktopLocation vs Location
| 屬性 | 座標系 | 用途 |
Location | 父容器客戶區 (MDI 子視窗相對父視窗) | 一般子控制項定位 |
DesktopLocation | **螢幕絕對座標** (虛擬螢幕左上角為 0,0) | **最上層視窗 (Form) 定位**、跨螢幕移動 |
飄動視窗為獨立 Form,**必須使用 DesktopLocation**。
2. Point 結構運算
// 建立新位置 (不可直接修改 DesktopLocation.X,因為 Point 為實值型別)
Point newLoc = new Point(this.DesktopLocation.X + 1, this.DesktopLocation.Y);
this.DesktopLocation = newLoc; // 整體指派觸發視窗移動
3. Timer 精確度與平滑度
Interval=10 理論上 100 FPS,但實際受限於系統時鐘 (~15.6ms 解析度),實際約 60-65 FPS。
- 若需更平滑動畫,建議改用:
| 方案 | 優點 | 缺點 |
System.Windows.Forms.Timer | UI 安全、簡單 | 精度低、佔用 UI 執行緒 |
WPF DispatcherTimer + 動畫 | 硬體加速、插值平滑 | 需 WPF 專案 |
WinForms + System.Threading.Timer + BeginInvoke | 背景執行緒計算、UI 執行緒只更新 | 需手動同步、複雜 |
| Direct2D / SharpDX / SkiaSharp | 最高效能、GPU 加速 | 學習曲線陡峭 |
4. 多螢幕與虛擬螢幕座標
DesktopLocation 使用**虛擬螢幕座標系**:主螢幕左上為 (0,0),次螢幕在左側則 X 為負、在上方則 Y 為負。
- 範例中
LEFT_BOUND = -150 允許視窗部分移出主螢幕左側。
- 取得螢幕範圍:
Screen.PrimaryScreen.Bounds、Screen.AllScreens。
5. 視窗樣式優化 (可選)
若要隱藏標題列、邊框、最小化/最大化按鈕,使飄動效果更像「浮動標籤」:
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
this.TopMost = true; // 可選:永遠置頂
擴充創意:更多飄動模式
// 1. 垂直飄動 (上下)
newLoc = new Point(loc.X, loc.Y + (goingDown ? STEP : -STEP));
// 2. 圓形軌跡
double angle = _tickCount * 0.05;
newLoc = new Point(
centerX + (int)(radius * Math.Cos(angle)),
centerY + (int)(radius * Math.Sin(angle))
);
// 3. 隨機漫步 (布朗運動)
newLoc = new Point(loc.X + _rd.Next(-STEP, STEP+1), loc.Y + _rd.Next(-STEP, STEP+1));
// 配合邊界檢查 Math.Clamp
// 4. 跟隨滑鼠 (延遲跟隨)
Point mouse = Cursor.Position;
newLoc = new Point(
loc.X + (int)((mouse.X - loc.X) * 0.1),
loc.Y + (int)((mouse.Y - loc.Y) * 0.1)
);
常見問題與除錯
| 問題 | 原因 | 解決 |
| 視窗不移動 | Timer 未 Start、Interval=0、Tick 未訂閱 | 檢查 InitializeTimers、MainForm_Load 中 Start() |
| 視窗閃爍/抖動 | Interval 太小、UI 執行緒忙碌、雙重繪製 | 調大 Interval (50-100ms)、啟用 DoubleBuffered、減少控制項 |
| 視窗飛出螢幕找不回 | 邊界常數設錯、多螢幕座標誤解 | 加入螢幕邊界檢查、提供「重置位置」按鈕 |
| 關閉視窗後程式未結束 | Timer 未 Dispose、背景執行緒殘留 | Override Dispose 呼叫 timer.Dispose() |
總結
System.Windows.Forms.Timer 是 WinForms 中最簡單、最安全的 UI 定時器。本文透過「視窗水平飄動」範例,完整展示了:Interval/Enabled/Tick 核心用法、DesktopLocation + Point 視窗定位技巧、兩 Timer 交替啟用實現來回動畫、Dispose 釋放資源。掌握這些基礎後,可輕鬆擴充至垂直飄動、圓形軌跡、隨機漫步、滑鼠跟隨等豐富動畫效果,為應用程式增添生動互動體驗。進階需求請考慮 WPF 動畫系統或 GPU 加速繪圖庫。
- 上一篇文章: C# 使用 Windows Hook 監控滑鼠移動:SetWindowsHookEx 完整實作教學
- 下一篇文章: WF 框架程式設計:設計器架構與活動設計器詳解
Copyright ©
程式碼之家 All Rights Reserved