使用者在註冊表單輸入帳號時,離開欄位或點擊「檢查」按鈕,**不重新整理頁面**即時查詢資料庫並顯示「可用」/「已存在」。核心元件:UpdatePanel (局部更新) + TextBox AutoPostBack 或 Button 非同步觸發 + Label 顯示結果。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="Demo.Register" %>
<!DOCTYPE html>
<html><head><title>使用者註冊 - 帳號即時檢查</title>
<style>
.valid { color: green; font-weight: bold; }
.invalid { color: red; font-weight: bold; }
.checking { color: orange; }
.input-group { margin-bottom: 15px; }
label { display: inline-block; width: 100px; text-align: right; margin-right: 10px; }
</style></head><body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<h2>會員註冊</h2>
<div class="input-group">
<label>帳號:</label>
<asp:UpdatePanel ID="upUsername" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:TextBox ID="txtUsername" runat="server"
AutoPostBack="true"
OnTextChanged="txtUsername_TextChanged"
CssClass="form-control"
Style="width: 250px;" />
<asp:Label ID="lblUsernameStatus" runat="server" CssClass="valid" />
</ContentTemplate>
<!-- 也可改用 Button 觸發,見下方說明 -->
</asp:UpdatePanel>
</div>
<div class="input-group">
<label>密碼:</label>
<asp:TextBox ID="txtPassword" runat="server" TextMode="Password" CssClass="form-control" Style="width: 250px;" />
</div>
<div class="input-group">
<label>確認密碼:</label>
<asp:TextBox ID="txtConfirmPwd" runat="server" TextMode="Password" CssClass="form-control" Style="width: 250px;" />
</div>
<div class="input-group">
<label></label>
<asp:Button ID="btnRegister" runat="server" Text="送出註冊" OnClick="btnRegister_Click" CssClass="btn btn-primary" />
<asp:Label ID="lblResult" runat="server" ForeColor="Red" />
</div>
</form></body></html>
using System;
using System.Web.UI;
namespace Demo
{
public partial class Register : System.Web.UI.Page
{
// 模擬資料庫:既有帳號集合
private static readonly string[] ExistingUsers = { "admin", "root", "test", "user1", "demo" };
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblUsernameStatus.Text = "請輸入帳號";
lblUsernameStatus.CssClass = "";
}
}
// TextBox AutoPostBack 觸發 (失焦或 Enter)
protected void txtUsername_TextChanged(object sender, EventArgs e)
{
CheckUsernameAvailability(txtUsername.Text.Trim());
}
// 替代方案:Button 點擊觸發 (需在 Trigger 中指定)
protected void btnCheck_Click(object sender, EventArgs e)
{
CheckUsernameAvailability(txtUsername.Text.Trim());
}
private void CheckUsernameAvailability(string username)
{
if (string.IsNullOrWhiteSpace(username))
{
SetStatus("帳號不可為空", false);
return;
}
// 長度、字元限制驗證
if (username.Length < 4 || username.Length > 20)
{
SetStatus("長度需 4-20 字元", false);
return;
}
if (!System.Text.RegularExpressions.Regex.IsMatch(username, @"^[a-zA-Z0-9_]+$"))
{
SetStatus("僅限英數字與底線", false);
return;
}
// 模擬資料庫查詢 (實務用 Parameterized Query / EF / Dapper)
bool exists = Array.Exists(ExistingUsers, u => u.Equals(username, StringComparison.OrdinalIgnoreCase));
SetStatus(exists ? "帳號已存在" : "帳號可用", !exists);
}
private void SetStatus(string message, bool isValid)
{
lblUsernameStatus.Text = message;
lblUsernameStatus.CssClass = isValid ? "valid" : "invalid";
}
protected void btnRegister_Click(object sender, EventArgs e)
{
// 完整驗證
if (string.IsNullOrWhiteSpace(txtUsername.Text) ||
Array.Exists(ExistingUsers, u => u.Equals(txtUsername.Text.Trim(), StringComparison.OrdinalIgnoreCase)))
{
lblResult.Text = "帳號不可用或未檢查";
return;
}
if (txtPassword.Text != txtConfirmPwd.Text)
{
lblResult.Text = "兩次密碼不一致";
return;
}
if (txtPassword.Text.Length < 6)
{
lblResult.Text = "密碼至少 6 字元";
return;
}
// 註冊成功邏輯 (寫入資料庫、發送驗證信...)
lblResult.Text = "註冊成功!";
lblResult.ForeColor = System.Drawing.Color.Green;
}
}
}
| 設定 | 值 | 作用 |
|---|---|---|
UpdateMode="Conditional" |
條件式更新 | 只有觸發器觸發或 ChildrenAsTriggers="true" 時才更新面板 |
ChildrenAsTriggers="true" |
子控件觸發 | 面板內控件 (如 TextBox AutoPostBack) 回發時自動觸發面板更新 |
AutoPostBack="true" |
自動回發 | TextBox 失去焦點或按 Enter 時發起非同步回發 |
OnTextChanged |
伺服器事件 | 回發時執行的後端驗證方法 |
若需如 Google 註冊般「打字同時檢查」,可加入 Timer 控件:
<asp:UpdatePanel ID="upUsername" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:TextBox ID="txtUsername" runat="server"
onkeyup="resetCheckTimer();"
ClientIDMode="Static" />
<asp:Label ID="lblUsernameStatus" runat="server" />
<asp:Timer ID="tmrCheck" runat="server" Interval="800" Enabled="false"
OnTick="tmrCheck_Tick" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="tmrCheck" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>
<script>
var checkTimer;
function resetCheckTimer() {
clearTimeout(checkTimer);
checkTimer = setTimeout(function() {
// 觸發 Timer 的 Tick 事件 (需配合 __doPostBack)
__doPostBack('tmrCheck', '');
}, 500); // 500ms 停止輸入後觸發
}
</script>
protected void tmrCheck_Tick(object sender, EventArgs e)
{
tmrCheck.Enabled = false; // 單次觸發
CheckUsernameAvailability(txtUsername.Text.Trim());
}
SELECT 1 FROM Users WHERE Username = @u,防 SQL Injection。UpdatePanel 屬 Server-Side AJAX,傳輸 ViewState 與 HTML 片段。前後端分離寫法更輕量:
// 前端 (jQuery)
$('#txtUsername').on('blur', function() {
var val = $(this).val().trim();
if (!val) return;
$.post('/api/account/check-username', { username: val }, function(res) {
$('#lblStatus').text(res.message).toggleClass('valid', res.available).toggleClass('invalid', !res.available);
});
});
// 後端 Web API (ASP.NET Core)
[HttpPost("check-username")]
public async Task<ActionResult<CheckResult>> CheckUsername([FromBody] CheckUsernameDto dto)
{
bool exists = await _userRepo.ExistsAsync(dto.Username);
return new CheckResult { Available = !exists, Message = exists ? "已存在" : "可用" };
}
UpdatePanel + AutoPostBack 是 Web Forms 時代實作即時驗證最快、代碼最少的方式,**關鍵在於將驗證區域隔離在極小的 UpdatePanel 中**,避免不必要的 HTML 傳輸。理解 ChildrenAsTriggers、UpdateMode、AsyncPostBackTrigger 三者關係,即可靈活控制局部更新行為。新專案強烈建議採用前端 AJAX + Web API 架構,獲得更好體驗與可維護性。