1.6. Windows Forms

WinForms 是 .NET 最经典的桌面 UI 框架,快速开发 Windows 桌面应用。

1.6.1. WinForms 概述

特性

说明

架构

基于 GDI+ 绘图,封装 Windows 控件

设计器

拖拽控件,所见即所得

事件模型

基于委托的事件驱动

适用场景

快速工具、内部管理系统、工业控制

.NET 支持

.NET Framework 全系列 + .NET 6+

1.6.2. 创建 WinForms 项目

dotnet new winforms -n MyWinFormsApp
cd MyWinFormsApp
dotnet run

1.6.2.1. 基本结构

// Form1.cs
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();  // 设计器生成的代码
    }
}

// Form1.Designer.cs(设计器自动维护)
partial class Form1
{
    private System.ComponentModel.IContainer components = null;
    private Button buttonOK;
    private TextBox textBoxName;
    private Label labelTitle;

    private void InitializeComponent()
    {
        this.buttonOK = new Button();
        this.textBoxName = new TextBox();
        this.labelTitle = new Label();
        // ... 属性设置
    }
}

1.6.3. 常用控件

// 标签
var label = new Label
{
    Text = "用户名:",
    Location = new Point(20, 20),
    Size = new Size(80, 25),
    Font = new Font("微软雅黑", 10)
};

// 文本框
var textBox = new TextBox
{
    Location = new Point(100, 20),
    Size = new Size(200, 25),
    PasswordChar = '*'        // 密码模式
};

// 按钮
var button = new Button
{
    Text = "登录",
    Location = new Point(100, 60),
    Size = new Size(100, 35),
    BackColor = Color.LightBlue,
    FlatStyle = FlatStyle.Flat
};
button.Click += ButtonLogin_Click;  // 事件绑定

// 下拉框
var comboBox = new ComboBox
{
    Location = new Point(100, 60),
    DropDownStyle = ComboBoxStyle.DropDownList
};
comboBox.Items.AddRange(["选项1", "选项2", "选项3"]);

// 数据表格
var dataGrid = new DataGridView
{
    Location = new Point(20, 100),
    Size = new Size(400, 200),
    AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
    AllowUserToAddRows = false,
    ReadOnly = true
};
dataGrid.DataSource = userList;

1.6.4. 事件处理

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
        Load += MainForm_Load;           // 窗体加载
        FormClosing += MainForm_FormClosing;  // 关闭前
    }

    private void MainForm_Load(object? sender, EventArgs e)
    {
        // 初始化数据
        LoadUserData();
    }

    private void MainForm_FormClosing(object? sender,
        FormClosingEventArgs e)
    {
        if (HasUnsavedChanges)
        {
            var result = MessageBox.Show(
                "有未保存的修改,确定退出?",
                "确认",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);
            e.Cancel = (result == DialogResult.No);
        }
    }

    private void ButtonLogin_Click(object? sender, EventArgs e)
    {
        string user = textBoxUser.Text;
        string pass = textBoxPassword.Text;

        if (Authenticate(user, pass))
        {
            MessageBox.Show("登录成功!", "提示",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        else
        {
            MessageBox.Show("用户名或密码错误!", "错误",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

1.6.5. 对话框

// 消息框
MessageBox.Show("消息内容", "标题");
MessageBox.Show("确认删除?", "确认",
    MessageBoxButtons.YesNoCancel,
    MessageBoxIcon.Warning);

// 打开文件
using var openDlg = new OpenFileDialog
{
    Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*",
    Multiselect = false,
    InitialDirectory = Environment.GetFolderPath(
        Environment.SpecialFolder.Desktop)
};
if (openDlg.ShowDialog() == DialogResult.OK)
{
    string path = openDlg.FileName;
    // 处理文件...
}

// 保存文件
using var saveDlg = new SaveFileDialog
{
    Filter = "图片文件|*.png;*.jpg",
    DefaultExt = "png"
};
if (saveDlg.ShowDialog() == DialogResult.OK)
{
    image.Save(saveDlg.FileName);
}

// 文件夹选择
using var folderDlg = new FolderBrowserDialog();
if (folderDlg.ShowDialog() == DialogResult.OK)
{
    string path = folderDlg.SelectedPath;
}

1.6.6. MDI 多文档界面

// 主窗体设置为 MDI 容器
public partial class MainForm : Form
{
    public MainForm()
    {
        IsMdiContainer = true;  // 设置为 MDI 容器

        var menu = new MenuStrip();
        var fileMenu = menu.Items.Add("文件") as ToolStripMenuItem;
        fileMenu.DropDownItems.Add("打开", null, OpenChildForm);

        MainMenuStrip = menu;
        Controls.Add(menu);
    }

    private void OpenChildForm(object? sender, EventArgs e)
    {
        var child = new ChildForm();
        child.MdiParent = this;   // 设置在容器内显示
        child.Show();
    }
}

1.6.7. 自定义控件

// 继承现有控件
public class NumericTextBox : TextBox
{
    public NumericTextBox()
    {
        this.Font = new Font("Consolas", 12);
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        // 只允许数字输入
        if (!char.IsDigit(e.KeyChar) &&
            !char.IsControl(e.KeyChar))
            e.Handled = true;

        base.OnKeyPress(e);
    }
}

// UserControl(组合多个控件)
public partial class AddressControl : UserControl
{
    public AddressControl()
    {
        InitializeComponent();  // 设计器支持
    }

    public string Street => textStreet.Text;
    public string City => textCity.Text;
    public string ZipCode => textZip.Text;
}

小技巧

WinForms 开发建议:

  • 使用 using 管理 GDI 资源(Brush、Pen、Font)

  • 耗时操作使用 async/await 避免 UI 阻塞

  • 跨线程访问 UI 用 Invoke/BeginInvoke

  • 考虑 MVP (Model-View-Presenter) 模式分离逻辑