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

C# 建立 Windows Forms 應用程式範本:從零手刻完整架構 (選單、工具列、狀態列、事件)

     編輯:關於C語言

前言

每次建立新 WinForms 專案都要重複「新增表單 → 拉控制項 → 寫事件」的樣板程式碼。若不使用 Visual Studio 設計器 (或需跨平台編譯),掌握以程式碼完整建構表單架構的能力極為關鍵。本文手把手教你打造一個可重複使用的 MyWinApp 範本類別,涵蓋表單屬性、選單、工具列、狀態列、TreeView/Label/Button 控制項、完整事件處理與資源釋放。

完整範本原始碼 (單一檔案可直接編譯)

// 編譯指令: csc MyWinApp.cs /r:System.dll /r:System.Windows.Forms.dll /r:System.Drawing.dll
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.ComponentModel;

public class MyWinApp : Form
{
    // === 欄位:控制項與狀態 ===
    private Label _lblHint;
    private Button _btnDemo;
    private TreeView _treeView;
    private ImageList _imgList;
    private readonly string _imageFolder;

    // 選單
    private MainMenu _mainMenu;
    private MenuItem _mnuFile, _mnuFileNew, _mnuFileOpen, _mnuFileSave, _mnuFileSaveAs, _mnuFileExit;
    private MenuItem _mnuSub, _mnuSubItem;

    // 工具列
    private ToolBar _toolBar;
    private ToolBarButton _tbbSep1, _tbbNew, _tbbOpen, _tbbSave, _tbbSep2, _tbbPrint;

    // 狀態列
    private StatusBar _statusBar;
    private StatusBarPanel _sbpHelp, _sbpTime;

    public MyWinApp()
    {
        // 圖片資料夾相對路徑 (需專案建置後複製 Images 資料夾至輸出目錄)
        _imageFolder = Path.Combine(Application.StartupPath, "Images") + Path.DirectorySeparatorChar;

        InitializeComponent();
    }

    private void InitializeComponent()
    {
        // 1. 表單基本屬性
        this.Text = "My Windows Application Template";
        this.Width = 600;
        this.Height = 450;
        this.StartPosition = FormStartPosition.CenterScreen;
        this.Icon = LoadIcon("app.ico");

        // 2. 圖片清單 (供工具列按鈕使用)
        _imgList = new ImageList { ImageSize = new Size(16, 16), ColorDepth = ColorDepth.Depth32Bit };
        _imgList.Images.Add(LoadImage("new.bmp"));   // Index 0
        _imgList.Images.Add(LoadImage("open.gif"));  // Index 1
        _imgList.Images.Add(LoadImage("save.bmp"));  // Index 2
        _imgList.Images.Add(LoadImage("print.gif")); // Index 3

        // 3. 主選單
        _mainMenu = new MainMenu();
        _mnuFile = new MenuItem("&File");
        _mnuFileNew = NewMenuItem("&New", Shortcut.CtrlN, MnuFileNew_Click);
        _mnuFileOpen = NewMenuItem("&Open", Shortcut.CtrlO, MnuFileOpen_Click);
        _mnuFileSave = NewMenuItem("&Save", Shortcut.CtrlS, MnuFileSave_Click);
        _mnuFileSaveAs = NewMenuItem("Save &As", null, MnuFileSaveAs_Click);
        _mnuSub = new MenuItem("&With Submenu");
        _mnuSubItem = NewMenuItem("Su&bmenu", MnuSubItem_Click);
        _mnuFileExit = NewMenuItem("E&xit", null, MnuFileExit_Click);

        _mnuSub.MenuItems.Add(_mnuSubItem);
        _mnuFile.MenuItems.AddRange(new MenuItem[] {
            _mnuFileNew, _mnuFileOpen, _mnuFileSave, _mnuFileSaveAs,
            _mnuSub, new MenuItem("-"), _mnuFileExit });
        _mainMenu.MenuItems.Add(_mnuFile);
        this.Menu = _mainMenu;

        // 4. 工具列
        _toolBar = new ToolBar
        {
            Appearance = ToolBarAppearance.Flat,
            ImageList = _imgList,
            ButtonSize = new Size(24, 24),
            ShowToolTips = true
        };
        _tbbSep1 = NewToolBarButton(ToolBarButtonStyle.Separator);
        _tbbNew = NewToolBarButton("New Document", 0);
        _tbbOpen = NewToolBarButton("Open Document", 1);
        _tbbSave = NewToolBarButton("Save", 2);
        _tbbSep2 = NewToolBarButton(ToolBarButtonStyle.Separator);
        _tbbPrint = NewToolBarButton("Print", 3);
        _toolBar.Buttons.AddRange(new ToolBarButton[] {
            _tbbSep1, _tbbNew, _tbbOpen, _tbbSave, _tbbSep2, _tbbPrint });
        _toolBar.ButtonClick += ToolBar_ButtonClick;
        this.Controls.Add(_toolBar);

        // 5. 一般控制項
        _treeView = new TreeView
        {
            Left = 20, Top = 60, Width = 200, Height = 300,
            Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left
        };
        _treeView.Nodes.Add("Root").Nodes.Add("Child 1");
        _treeView.Nodes[0].Nodes.Add("Child 2");
        _treeView.ExpandAll();

        _lblHint = new Label
        {
            Left = 240, Top = 60, Width = 300, Height = 40,
            Text = "Click the button below to test event handling.",
            Font = new Font("Segoe UI", 10)
        };

        _btnDemo = new Button
        {
            Left = 240, Top = 110, Width = 120, Height = 35,
            Text = "Click Me",
            FlatStyle = FlatStyle.System
        };
        _btnDemo.Click += BtnDemo_Click;

        this.Controls.AddRange(new Control[] { _treeView, _lblHint, _btnDemo });

        // 6. 狀態列
        _statusBar = new StatusBar { ShowPanels = true };
        _sbpHelp = new StatusBarPanel
        {
            BorderStyle = StatusBarPanelBorderStyle.Sunken,
            Text = "Press F1 for Help",
            AutoSize = StatusBarPanelAutoSize.Spring
        };
        _sbpTime = new StatusBarPanel
        {
            BorderStyle = StatusBarPanelBorderStyle.Raised,
            Text = DateTime.Today.ToLongDateString(),
            ToolTipText = DateTime.Now.ToLongTimeString(),
            AutoSize = StatusBarPanelAutoSize.Contents
        };
        _statusBar.Panels.AddRange(new StatusBarPanel[] { _sbpHelp, _sbpTime });
        this.Controls.Add(_statusBar);

        // 7. 表單關閉事件
        this.Closing += MyWinApp_Closing;
    }

    // === Helper Methods ===
    private Icon LoadIcon(string name)
    {
        string path = Path.Combine(_imageFolder, name);
        return File.Exists(path) ? new Icon(path) : SystemIcons.Application;
    }

    private Image LoadImage(string name)
    {
        string path = Path.Combine(_imageFolder, name);
        return File.Exists(path) ? Image.FromFile(path) : new Bitmap(16, 16);
    }

    private MenuItem NewMenuItem(string text, Shortcut shortcut, EventHandler handler)
    {
        var mi = new MenuItem(text, handler);
        if (shortcut != 0) mi.Shortcut = shortcut;
        return mi;
    }

    private ToolBarButton NewToolBarButton(string tooltip, int imageIndex)
    {
        return new ToolBarButton
        {
            ToolTipText = tooltip,
            ImageIndex = imageIndex
        };
    }

    private ToolBarButton NewToolBarButton(ToolBarButtonStyle style)
    {
        return new ToolBarButton { Style = style };
    }

    // === 事件處理器 ===
    private void MnuFileNew_Click(object sender, EventArgs e) => ShowMsg("File → New");
    private void MnuFileOpen_Click(object sender, EventArgs e) => ShowMsg("File → Open");
    private void MnuFileSave_Click(object sender, EventArgs e) => ShowMsg("File → Save");
    private void MnuFileSaveAs_Click(object sender, EventArgs e) => ShowMsg("File → Save As");
    private void MnuSubItem_Click(object sender, EventArgs e) => ShowMsg("Submenu → Submenu Item");
    private void MnuFileExit_Click(object sender, EventArgs e) => this.Close();

    private void ToolBar_ButtonClick(object sender, ToolBarButtonClickEventArgs e)
    {
        int idx = _toolBar.Buttons.IndexOf(e.Button);
        // Index: 0=Sep, 1=New, 2=Open, 3=Save, 4=Sep, 5=Print
        string[] names = { "", "New", "Open", "Save", "", "Print" };
        if (idx >= 0 && idx < names.Length && names[idx] != "")
            ShowMsg($"Toolbar → {names[idx]}");
    }

    private void BtnDemo_Click(object sender, EventArgs e)
    {
        ShowMsg("Button clicked! Thank you.");
    }

    private void MyWinApp_Closing(object sender, CancelEventArgs e)
    {
        if (MessageBox.Show("確定要離開嗎?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
            e.Cancel = true;
    }

    private void ShowMsg(string msg) => MessageBox.Show(msg, "Event Triggered", MessageBoxButtons.OK, MessageBoxIcon.Information);

    // === 進入點 ===
    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MyWinApp());
    }
}

架構設計重點解析

1. 表單繼承與啟動

  • 類別繼承 Form,建構子呼叫 InitializeComponent()。
  • Main() 標記 [STAThread],呼叫 Application.Run(new MyWinApp()) 進入訊息迴圈。

2. 圖片資源管理

  • 使用 Application.StartupPath 取得執行檔目錄,搭配 Path.DirectorySeparatorChar 跨平台相容。
  • ImageList 集中管理工具列圖示,ToolBarButton.ImageIndex 參考索引。
  • 缺檔容錯:LoadIcon/LoadImage 檔案不存在時回傳系統預設圖示/空白 Bitmap,避免啟動崩潰。

3. 選單系統

  • MainMenu → MenuItem 階層結構。
  • 建構子多載 new MenuItem(text, handler, shortcut) 一次設定文字、事件、快捷鍵。
  • 分隔線:new MenuItem("-")。
  • 子選單:父 MenuItem.MenuItems.Add(child)。

4. 工具列

  • ToolBar + ToolBarButton,ButtonClick 事件統一處理所有按鈕。
  • ToolBarButtonStyle.Separator 產生分隔間距。
  • ShowToolTips = true 顯示 ToolTipText。

5. 狀態列

  • StatusBar + StatusBarPanel 多面板。
  • AutoSize.Spring 自動填滿剩餘寬度 (通常放提示文字)。
  • AutoSize.Contents 依內容寬度 (通常放時間)。

6. 版面配置與錨點

  • 手動設定 Left/Top/Width/Height 或 Location/Size。
  • Anchor 屬性控制控制項隨表單縮放行為 (範例 TreeView 固定左上、隨高度延伸)。
  • 複雜版面建議改用 TableLayoutPanel / FlowLayoutPanel 容器。

7. 事件處理模式

  • 選單項、按鈕:Click += Handler。
  • 工具列:統一 ButtonClick,透過 Buttons.IndexOf(e.Button) 判斷哪個按鈕。
  • 表單關閉:Closing 事件可取消關閉 (e.Cancel = true)。

8. 資源釋放

  • 表單關閉時自動呼叫 Dispose(),釋放 ImageList 圖片、Icon、Menu 等非受控資源。
  • 若有大量動態控制項,需在 Dispose(bool disposing) 覆寫中手動 Controls.Clear() 並逐一 Dispose()。

擴充建議:打造專屬專案範本

  1. 將上述程式碼存為 MyWinApp.cs。
  2. 在 Visual Studio:專案 → 匯出範本 → 項目範本,勾選「自動匯入至 VS」。
  3. 或手動建立 .vstemplate XML 放入 %USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates。
  4. 新增專案時即可選擇「My WinForms Template」,秒產標準架構。

結語

手刻 WinForms 範本不僅能擺脫設計器依賴,更能深入理解控制項樹、事件路由、資源管理等核心機制。將常用架構 (選單、工具列、狀態列、錨點配置、基底表單類別) 封裝為可重複使用的程式碼範本,是提升團隊開發效率、確保 UI 風格一致性的關鍵實務。

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