0

私の 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 と一緒に)。

4

2 に答える 2

0

ページ ディレクティブでコントロールの Src 属性を「書き換える」ことで、この問題を解決するはるかに洗練された方法を見つけました。これの利点は、ASP.NET がページを解析する前に実行されることです。これを行うには、PageParserFilter から継承するクラスを作成します。次に、Web.config ファイルのノードで、新しく作成されたクラスに pageParserFilterType 属性を設定すると、サイトのすべてのページが新しいクラスを介して実行されるようになります。

于 2013-10-28T19:12:58.757 に答える
0

ここで欠けているものはほとんどありません。代わりにこれを試してください:

protected void Page_Init(object sender, EventArgs e)
{
    string path = "~/themes/mytheme/controls/mycontrol.ascx";
    if (File.Exists(Server.MapPath(path)))
    {
        UserControl ctrl = (UserControl)this.LoadControl("~/themes/mytheme/controls/mycontrol.ascx");
        this.Controls.Clear();
        this.Controls.Add(ctrl);
    }
    else 
    {

        //Load Other controls

    }

}
于 2013-10-21T03:15:16.163 に答える