ASP.NETコントロールプロパティにDataBinding式が含まれているかどうかを検出する方法
-
06-07-2019 - |
質問
System.Web.UI.Control
を継承するカスタムコントロールがあり、その一部のプロパティはデータバインディング式を使用して宣言的に設定できます。例:
<foo:Foo runat="server" MyFoo="<%# this.GetFoo() %>" />
今、その場合、コントロール(またはその親の1つ)で .DataBind()
を呼び出して、これらの式を評価する必要があります。
できるようにしたいのは、この方法でプロパティが設定された場合に を検出し、< code> OnPreRender またはその付近。
それでは質問:データバインディング式の実行待ちを検出するにはどうすればよいですか
一部の ControlBuilder
または DataBindContext
クラスには、これを判断するために必要な情報が含まれていると確信しています。 Reflectorを探し回っていますが、見つけられないようです。
追加する必要があるのは、この方法で直接プロパティが割り当てられていない場合、 DataBind()
を実行するオーバーヘッドを支払いたくないということです。これが私が事前に検出したい理由です。このクラスは非常に軽量ですが、コードビハインドを必要とせずに宣言的にプロパティを設定できる機能が欲しいです。
解決
ControlBuilder
をさらに詳しく調べると、データバインディング式が存在する場合、各コントロールインスタンスのコンパイルされたファクトリが DataBinding
イベントハンドラをアタッチすることに気付きました。これを確認することは、データバインディングを行う必要があるかどうかを判断するための非常に信頼性の高い方法であることがわかった。ここに私の問題の解決の基礎があります:
using System;
using System.Reflection;
using System.Web.UI;
public class AutoDataBindControl : Control
{
private static readonly object EventDataBinding;
private bool needsDataBinding = false;
static AutoDataBindControl()
{
try
{
FieldInfo field = typeof(Control).GetField(
"EventDataBinding",
BindingFlags.NonPublic|BindingFlags.Static);
if (field != null)
{
AutoDataBindControl.EventDataBinding = field.GetValue(null);
}
}
catch { }
if (AutoDataBindControl.EventDataBinding == null)
{
// effectively disables the auto-binding feature
AutoDataBindControl.EventDataBinding = new object();
}
}
protected override void DataBind(bool raiseOnDataBinding)
{
base.DataBind(raiseOnDataBinding);
// flag that databinding has taken place
this.needsDataBinding = false;
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// check for the presence of DataBinding event handler
if (this.HasEvents())
{
EventHandler handler = this.Events[AutoDataBindControl.EventDataBinding] as EventHandler;
if (handler != null)
{
// flag that databinding is needed
this.needsDataBinding = true;
this.Page.PreRenderComplete += new EventHandler(this.OnPreRenderComplete);
}
}
}
void OnPreRenderComplete(object sender, EventArgs e)
{
// DataBind only if needed
if (this.needsDataBinding)
{
this.DataBind();
}
}
}
このソリューションは、 DataBinding
イベントハンドラーがアタッチされていない場合、またはコントロールが(直接または親を介して)手動でデータバインドされている場合、それ自体を無効にします。
このコードのほとんどは、イベントの存在をテストできるようにするためにフープをジャンプしているだけです。唯一必要なリフレクションは、 EventDataBinding
のキーとして使用される object
を取得するための1回限りのルックアップです。
他のヒント
ControlBuilder
クラスには、 SubBuilders
という internal ArrayList
があります。各データバインディング式 TemplateParser
enocuntersに対して、 ProcessCodeBlock()
は BlockType
プロパティ CodeBlockTypeを持つ
から CodeBlockBuilder
オブジェクトを追加します.DataBinding SubBuilders
へ。
したがって、必要な ControlBuilder
へのハンドルを取得できる場合、 SubBuilders
をリフレクションで反復処理し、 CodeBlockBuilder
BlockType == CodeBlockType.DataBinding
の場合。
もちろんこれはあらゆる種類の厄介なものであり、これがコアの問題を解決する最善の方法であると本当に疑っています。 2ステップ前に戻って元の問題を確認する場合は、代わりにStackoverflowに投稿することをお勧めします-優れた解決策を考え出すのに役立つ非常に賢い人がたくさんいます。