頁面 Default.aspx 包含兩個使用者控制項 UcA.ascx、UcB.ascx。UcA 內的按鈕點擊時,需取得 UcB 內 TextBox 的值。由於控制項 ID 在命名容器中會改變 (如 ctl00$UcB1$TextBox1),直接 FindControl 易斷、耦合度高。
IGetValue:UcB 實作,提供取值方法。IGetUserControl:頁面實作,提供取得 UcB 實例的方法。this.Page 轉型呼叫,完全解耦。// IGetValue.cs
namespace MyProject.Contracts
{
public interface IGetValue
{
string GetValue();
}
}
// IGetUserControl.cs
using System.Web.UI;
namespace MyProject.Contracts
{
public interface IGetUserControl
{
UserControl GetUcB(); // 回傳 UcB 實例
}
}
<%@ Control Language="C#" ClassName="UcB" %>
<asp:TextBox ID="TextBox1" runat="server" />
using System.Web.UI;
using MyProject.Contracts;
public partial class UcB : UserControl, IGetValue
{
public string GetValue() => TextBox1.Text.Trim();
}
<%@ Page Language="C#" %>
<%@ Register Src="UcA.ascx" TagPrefix="uc" TagName="UcA" %>
<%@ Register Src="UcB.ascx" TagPrefix="uc" TagName="UcB" %>
<html>
<body>
<form runat="server">
<fieldset><legend>UcA</legend><uc:UcA ID="UcA1" runat="server" /></fieldset>
<fieldset><legend>UcB</legend><uc:UcB ID="UcB1" runat="server" /></fieldset>
</form>
</body>
</html>
using System.Web.UI;
using MyProject.Contracts;
public partial class Default : Page, IGetUserControl
{
public UserControl GetUcB() => UcB1; // 直接回傳強型別實例
}
<%@ Control Language="C#" ClassName="UcA" %>
<asp:Button ID="btnGet" runat="server" Text="取得 UcB 值" OnClick="btnGet_Click" />
using System.Web.UI;
using MyProject.Contracts;
public partial class UcA : UserControl
{
protected void btnGet_Click(object sender, EventArgs e)
{
// 1. 取得頁面介面
if (Page is IGetUserControl page)
{
// 2. 取得 UcB 實例
var ucB = page.GetUcB();
// 3. 轉型為值介面並取值
if (ucB is IGetValue valueProvider)
{
string val = valueProvider.GetValue();
// 顯示或處理
ScriptManager.RegisterStartupScript(this, GetType(), "alert", $"alert('UcB 值:{val}');", true);
}
}
}
}
| 傳統 FindControl | 介面契約模式 |
|---|---|
| 依賴具體 ID 字串 ("UcB1$TextBox1") | 依賴介面契約,重構安全 |
| 命名容器變更即失效 | 不受命名容器影響 |
| 緊耦合:UcA 需知 UcB 內部結構 | 鬆耦合:UcA 僅知介面 |
| 難以單元測試 | 易 Mock 介面測試 |
| 多層巢狀控制項極難維護 | 層級任意深度皆適用 |
// 定義事件介面
public interface IValueChangedNotifier
{
event EventHandler ValueChanged;
}
// UcB 實作
public partial class UcB : UserControl, IGetValue, IValueChangedNotifier
{
public event EventHandler ValueChanged;
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
ValueChanged?.Invoke(this, new ValueChangedEventArgs(TextBox1.Text));
}
}
// 頁面轉發事件給 UcA
public partial class Default : Page, IGetUserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (UcB1 is IValueChangedNotifier notifier)
notifier.ValueChanged += (s, args) => UcA1.OnValueChanged(args.NewValue);
}
}