私の ASP.NET アプリケーションは、独自のバージョンのテーマをサポートしています (標準の ASP.NET テーマではありません)。アプリケーションのページで使用されるすべてのユーザー コントロールを含む 1 つのフォルダーがあります。適切な用語がないため、オプションで「オーバーライド」する方法を提供したいと思います。これらのユーザー コントロールは、テーマのコントロール フォルダーを介して制御します。
問題は、テーマ ファイルではなく、アプリケーション ページがユーザー コントロールを参照することです。私が望む動作は、同じ名前のコントロールがテーマ コントロール フォルダーに存在する場合、通常のコントロールの代わりに読み込まれることです。アプリケーション ページ (レジスタ ディレクティブなど) を変更せずにこれを実現したいと考えています。
これを達成するための最良の方法は何ですか?コントロールのテーマ固有のバージョンをチェックし、存在する場合は、それ自体のロードを停止し、代わりに他のコントロールをロードするユーザーコントロールの基本クラスを使用できるように思われます。
以下の関数は、私の目標を達成するための大雑把な試みです。ページは代わりに必要なユーザー コントロールをロードしますが、ASP.NET は依然として内部で両方のユーザー コントロール イベント ハンドラー (Init、PreRender、Load など) を完全に処理しています。サーバーの負荷を軽減するために、このコントロールの処理を中止したいと考えています。
protected void Page_Init(object sender, EventArgs e)
{
if (File.Exists("~/themes/mytheme/controls/mycontrol.ascx")
{
this.
UserControl ctrl = this.LoadControl("~/themes/mytheme/controls/mycontrol.ascx");
this.Controls.Clear();
this.Controls.Add(ctrl);
}
}
public void RemoveAllEventHandlers()
{
RemoveAllEventHandlers(this);
}
public static void RemoveAllEventHandlers(Control ctrl)
{
if (ctrl != null) {
Type ctrlType = ctrl.GetType();
PropertyInfo propInfo = ctrlType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
EventHandlerList handlerList = (EventHandlerList)propInfo.GetValue(ctrl, null);
FieldInfo headInfo = handlerList.GetType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
Dictionary<object, Delegate[]> handlerDict = new Dictionary<object, Delegate[]>();
object head = headInfo.GetValue(handlerList);
if (head != null) {
Type entry = head.GetType();
FieldInfo handlerFI = entry.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = entry.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = entry.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
HelpAddEntry(handlerDict, head, handlerFI, keyFI, nextFI);
foreach (KeyValuePair<object, Delegate[]> pair in handlerDict) {
for (int x = pair.Value.Length - 1; x >= 0; x += -1) {
handlerList.RemoveHandler(pair.Key, pair.Value[x]);
}
}
}
}
}
private static void HelpAddEntry(Dictionary<object, Delegate[]> dict, object entry, FieldInfo handlerFI, FieldInfo keyFI, FieldInfo nextFI)
{
Delegate del = (Delegate)handlerFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object nxt = nextFI.GetValue(entry);
Delegate[] listeners = del.GetInvocationList();
if (listeners != null && listeners.Length > 0) {
dict.Add(key, listeners);
}
if (nxt != null) {
HelpAddEntry(dict, nxt, handlerFI, keyFI, nextFI);
}
}
編集
上記の更新されたコードを使用して、元のコントロールのイベント ハンドラーが実行されないようにすることができました。ただし、このアプローチには根本的な欠陥があります。これら 2 つのコントロールの間には親子関係があり、代替ではありません。そのため、親コントロールに設定されているプロパティは子コントロールに渡されず (これは Reflection で実行できると確信しています)、子コントロールの HTML ID 値は異なります (つまり、事前に保留されています)。親コントロールの ID と一緒に)。