Windows Forms (WinForms) 是 .NET Framework 早期提供的事件驅動桌面 UI 框架,雖然不如 WPF/MAUI 現代,但以「所見即所得」設計師、豐富控制項、成熟生態、低學習曲線,仍是企業內部工具、維護專案、快速原型的主流選擇。本章完整演示從專案建立、介面設計、事件處理到部署的標準流程。
FileBrowserDemo<!-- .NET 8.0 (SDK 風格) -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
<!-- .NET Framework (舊格式) -->
<Project>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
</Project>
| 控制項 | 名稱 | 關鍵屬性 | 用途 |
|---|---|---|---|
| Button | btnBrowse | Text="瀏覽..." | 觸發開啟檔案對話方塊 |
| TextBox | txtFilePath | ReadOnly=True, Anchor=Top,Left,Right | 顯示選取檔案完整路徑 |
| OpenFileDialog | ofdFile | Filter="文字檔|*.txt|所有檔案|*.*", Title="選擇檔案" | 檔案選取對話方塊(非視覺元件) |
在設計師雙擊 Button → 自動產生 btnBrowse_Click 並切換至程式碼視圖:
private void btnBrowse_Click(object sender, EventArgs e)
{
if (ofdFile.ShowDialog() == DialogResult.OK)
{
txtFilePath.Text = ofdFile.FileName;
}
}
// 標準事件簽章
public delegate void EventHandler(object sender, EventArgs e);
// 產生器語法 (C# 2.0+)
btnBrowse.Click += new EventHandler(btnBrowse_Click);
// Lambda 簡寫 (C# 3.0+)
btnBrowse.Click += (s, e) => { /* ... */ };
// 方法群組轉換
btnBrowse.Click += btnBrowse_Click;
| 類別 | 事件 | 觸發時機 |
|---|---|---|
| 滑鼠 | Click, DoubleClick, MouseDown/Up/Move/Enter/Leave | 滑鼠互動 |
| 鍵盤 | KeyDown/Up/Press, PreviewKeyDown | 鍵盤輸入 |
| 焦點 | Enter, Leave, GotFocus, LostFocus | 焦點移動 |
| 生命週期 | Load, Shown, FormClosing, FormClosed | 表單狀態變化 |
| 資料 | TextChanged, Validating, Validated | 內容變更/驗證 |
| 繪圖 | Paint | 需重繪時 |
namespace FileBrowserDemo
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent(); // 設計師產生,勿手動修改
}
// 事件處理器 - 只負責 UI 互動
private void btnBrowse_Click(object sender, EventArgs e)
{
if (ofdFile.ShowDialog() == DialogResult.OK)
{
txtFilePath.Text = ofdFile.FileName;
// 業務邏輯委派給服務類別
FileService.ProcessFile(ofdFile.FileName);
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// 初始化邏輯
}
}
}
public static class FileService
{
public static void ProcessFile(string path)
{
// 實際業務邏輯:讀取、解析、驗證...
// 易於單元測試、復用、維護
}
}
// 簡單繫結
txtName.DataBindings.Add("Text", customer, "Name", true, DataSourceUpdateMode.OnPropertyChanged);
// 複雜繫結 (DataGridView)
var bindingSource = new BindingSource();
bindingSource.DataSource = typeof(Customer);
dgvCustomers.DataSource = bindingSource;
// 物件清單繫結
bindingSource.DataSource = customerList; // List
public class Customer : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set { _name = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string prop = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
private void btnBad_Click(object sender, EventArgs e)
{
// UI 凍結 5 秒,使用者以為當機
Thread.Sleep(5000);
MessageBox.Show("完成");
}
private async void btnGood_Click(object sender, EventArgs e)
{
btnGood.Enabled = false;
try
{
await Task.Run(() => HeavyWork());
MessageBox.Show("完成");
}
finally
{
btnGood.Enabled = true;
}
}
private void HeavyWork()
{
Thread.Sleep(5000); // 背景執行緒睡眠不影響 UI
// 跨執行緒存取 UI 需 Invoke
// this.Invoke(() => lblStatus.Text = "處理中...");
}
private async void btnProgress_Click(object sender, EventArgs e)
{
var progress = new Progress(p => progressBar.Value = p);
await Task.Run(() =>
{
for (int i = 0; i <= 100; i += 10)
{
Thread.Sleep(200);
progress.Report(i); // 自動切回 UI 執行緒
}
});
}
| 模式 | 特點 | 適用場景 |
|---|---|---|
| Framework-dependent | 需目標機安裝 .NET Runtime,檔案小 | 企業環境、統一管理 Runtime |
| Self-contained | 包含 Runtime,無需安裝,檔案大 (≈60MB+) | 無法安裝 Runtime 的環境 |
| Single-file | 單一 .exe,解壓至暫存目錄執行 | 簡單分發、工具類程式 |
| Trimmed / AOT | 移除未使用組件 / 提前編譯原生碼 | 極致啟動速度、體積優化 |
# Framework-dependent
dotnet publish -c Release -r win-x64 --self-contained false
# Self-contained single-file
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true
# 安裝程式建立 (第三方工具)
# WiX Toolset, Inno Setup, Advanced Installer, InstallShield
Control.Invoke/BeginInvoke 或 Progress<T>、SynchronizationContextIDisposable 物件 (Graphics, Pen, Brush, FileStream) 必須 using 或 Dispose()Application.ThreadException + AppDomain.CurrentDomain.UnhandledExceptionInitializeComponent 區域勿手動修改,複雜邏輯移至 OnLoad、建構函式或獨立方法WinForms 以直觀的設計師、豐富的控制項、成熟的事件模型,支撐了無數企業桌面應用。掌握「介面設計 → 事件處理 → 業務分離 → 非同步 UI → 正確部署」完整流程,配合現代 C# 語法 (async/await, Pattern Matching, Record) 與 .NET 8+ 執行階段優化,能開發出高品質、易維護的桌面應用。下一章將探討類別庫開發與 NuGet 套件發佈,實現程式碼跨專案復用。