在 C# WinForm 應用中,實現「登錄成功關閉登錄窗、打開主窗口」看似簡單,實則涉及程序生命周期、線程模型、窗口所有權鏈等核心機制。本文分析常見錯誤模式,提供三種標準實現方案。
// ❌ 錯誤代碼:Program.cs
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormLogin()); // 以登錄窗為主窗口啟動
}
}
// FormLogin.cs 登錄成功處理
private void btnLogin_Click(object sender, EventArgs e) {
if (ValidateUser(txtUser.Text, txtPwd.Text)) {
FormMain main = new FormMain();
main.Show(); // 顯示主窗口
this.Close(); // 關閉登錄窗口 → 程序退出!
}
}
原因:Application.Run(form) 以參數窗口為「主窗口」,當主窗口關閉時,消息循環結束,程序退出。this.Close() 關閉主窗口導致應用終止,新創建的 FormMain 隨之銷毀。
// Program.cs
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 1. 創建並顯示登錄對話框
using (FormLogin loginForm = new FormLogin()) {
// ShowDialog 為模態對話框,阻塞直到關閉
DialogResult result = loginForm.ShowDialog();
// 2. 根據返回值決定是否啟動主程序
if (result == DialogResult.OK) {
Application.Run(new FormMain()); // 以主窗口啟動消息循環
}
// Else: 用戶取消/關閉,程序自然結束
}
}
}
// FormLogin.cs
public partial class FormLogin : Form {
public FormLogin() {
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e) {
string user = txtUser.Text.Trim();
string pwd = txtPwd.Text;
if (Authenticate(user, pwd)) {
// 關鍵:設置 DialogResult 為 OK
this.DialogResult = DialogResult.OK;
// 這裡不需要手動 Close(),設置 DialogResult 會自動關閉模態對話框
} else {
MessageBox.Show("用戶名或密碼錯誤", "登錄失敗",
MessageBoxButtons.OK, MessageBoxIcon.Error);
txtPwd.Clear();
txtPwd.Focus();
}
}
private bool Authenticate(string user, string pwd) {
// 實際項目調用服務層驗證
return "admin".Equals(user) && "123456".Equals(pwd);
}
// 可選:取消按鈕
private void btnCancel_Click(object sender, EventArgs e) {
this.DialogResult = DialogResult.Cancel;
}
}
using 確保登錄窗資源釋放Application.Run 僅調用一次,對應唯一主窗口適用於「啟動即顯示主窗口,未登錄時遮罩/禁用」的場景:
// Program.cs
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
FormMain mainForm = new FormMain();
// 主窗口構造器中處理登錄邏輯
Application.Run(mainForm);
}
}
// FormMain.cs
public partial class FormMain : Form {
public FormMain() {
InitializeComponent();
// 1. 初始化時隱藏主界面
this.Hide(); // 或設置 Opacity=0、禁用控件
// 2. 顯示登錄對話框
using (FormLogin login = new FormLogin()) {
if (login.ShowDialog(this) != DialogResult.OK) {
// 登錄失敗/取消 → 退出程序
Environment.Exit(0);
return;
}
}
// 3. 登錄成功 → 顯示主界面
this.Show();
this.WindowState = FormWindowState.Maximized;
}
}
完全控制應用生命周期,支持多窗口切換、托盤運行等複雜場景:
// AppContext.cs
public class AppContext : ApplicationContext {
private FormLogin loginForm;
private FormMain mainForm;
public AppContext() {
ShowLogin();
}
private void ShowLogin() {
loginForm = new FormLogin();
loginForm.LoginSuccess += OnLoginSuccess;
loginForm.FormClosed += (s, e) => {
if (mainForm == null) ExitThread(); // 登錄窗關閉且無主窗口 → 退出
};
loginForm.Show();
}
private void OnLoginSuccess(object sender, EventArgs e) {
loginForm.LoginSuccess -= OnLoginSuccess;
loginForm.Hide(); // 隱藏而非關閉,保持實例
mainForm = new FormMain();
mainForm.FormClosed += (s, e) => {
// 主窗口關閉 → 可選:重新登錄 或 退出
// ShowLogin(); // 重新登錄
ExitThread(); // 退出程序
};
mainForm.Show();
}
}
// Program.cs
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
}
// FormLogin.cs 擴展
private void FormLogin_Load(object sender, EventArgs e) {
// 讀取記住的用戶名
string savedUser = Properties.Settings.Default.RememberedUser;
if (!string.IsNullOrEmpty(savedUser)) {
txtUser.Text = savedUser;
chkRemember.Checked = true;
}
// 自動登錄邏輯
if (Properties.Settings.Default.AutoLogin && !string.IsNullOrEmpty(savedUser)) {
string savedPwd = Properties.Settings.Default.SavedPassword; // 實際應加密存儲
if (Authenticate(savedUser, savedPwd)) {
// 延遲執行避免界面閃爍
this.BeginInvoke(new Action(() => {
this.DialogResult = DialogResult.OK;
}));
}
}
}
private void btnLogin_Click(object sender, EventArgs e) {
if (Authenticate(txtUser.Text, txtPwd.Text)) {
// 保存設置
if (chkRemember.Checked) {
Properties.Settings.Default.RememberedUser = txtUser.Text;
// 實際項目:加密存儲密碼或 Token
} else {
Properties.Settings.Default.RememberedUser = "";
}
Properties.Settings.Default.AutoLogin = chkAutoLogin.Checked;
Properties.Settings.Default.Save();
this.DialogResult = DialogResult.OK;
}
}
| 概念 | 說明 |
|---|---|
| Application.Run(Form) | 啟動消息循環,參數 Form 為主窗口,關閉則退出 |
| Form.Show() | 非模態顯示,不阻塞調用線程 |
| Form.ShowDialog() | 模態顯示,阻塞直到關閉,返回 DialogResult |
| DialogResult 屬性 | 設置後自動關閉模態對話框,作為 ShowDialog 返回值 |
| 主窗口關閉行為 | 可通過 FormClosing 事件攔截(如最小化到托盤) |
掌握 WinForm 應用程序模型與窗口生命周期,是構建穩健桌面應用的基石。推薦方案一作為默認選擇,方案三應對複雜架構需求。