MS Ajax ベータ版が公開されてからここ数週間、wwHoverPanel コントロールを MS Ajax と組み合わせて実行すると問題が発生するという報告を多数受け取りました。コントロール自体は MS AJAX に直接干渉しませんが、コントロールを AJAX UpdatePanel() 内に固定している場合、コントロールが吐き出すスクリプト コードがコールバックで生成された更新に適切に生成されないため、問題が発生します。 。スクリプト コードが欠落している場合でも、コントロールは引き続き機能しますが、予期しない動作がいくつか発生します。たとえば、更新パネルに配置されたホバー パネルは多くの場合その位置を失い、現在のマウス カーソル位置にポップアップ表示されるのではなく、それが存在するコンテナ コントロールの境界にポップアップ表示されます。
問題は、Microosft が MS AJAX ベータ版で、ScriptManager コントロールを通じて駆動される完全に別個のスクリプト生成エンジンを使用することを決定したことです。 MS Ajax ScriptManager は、ClientScript オブジェクトのメソッドの多くを模倣していますが、それらを静的メソッドとして提供します (ありがたいことに! それがなければ、私たちは本当に混乱するでしょう)。
したがって、RegisterClientScriptBlock、ResgisterClientScriptResources などのメソッド (ページへのスクリプト コードの取得を処理するもの) には、ScriptManager に関連する静的メソッドがあります。 ScriptManager メソッドは追加の最初のパラメーターとして Control を渡しますが、それ以外の点では既存の ClientScriptManager を模倣します。
ただし、この新しい動作により、既存のコントロールがバインドされます。コードで ClientScriptManager が使用されている場合、UpdatePanels はスクリプト コードを確認できません (コールバックで更新が必要な場合)。しかし同時に、コントロール開発者は MS Ajax ScriptManager が実際に存在すると仮定することはできません。
これらすべての最終結果として、この不一致に対処するのは必ずしも簡単ではなく、どのコントロールを使用するかを決定できるラッパー オブジェクトを作成する必要があります。ラッパーは、MS Ajax がアプリケーションで利用可能かどうかを判断し、利用できる場合は Reflection を使用して ScriptManager にアクセスし、スクリプト コードを書き出す必要があります。
ただし、これを私の功績とすることはできません。Eilon Lipton がこの問題について少し前に投稿しました。彼のコードは、私がこれを軌道に乗せるのに本当に必要なものでした。私はそれを、いくつかのアプリケーションで使用した ClientScriptProxy オブジェクトにラップしただけです。コントロールの。基本的に、アプリケーションで使用するいくつかの ClientScript メソッドを追加しました。クラスは次のとおりです。
[*** コード更新: コメントから 2006 年 12 月 12 日 *** ]
///
/// これは、Page.ClientScript および MS Ajax ScriptManager のプロキシ オブジェクトであり
、 /// MS Ajax が存在しない場合でも動作できます。 MS Ajax は
/// 利用できない可能性があるため、メソッドに直接アクセスすることは /// 不可能であり
、 /// このクラスを通じてクライアント スクリプト メソッドを間接的に参照する必要があります
。
///
/// このクラスはコントロールの起動時に呼び出され、
/// すべての呼び出し Page.ClientScript を置き換えるために使用されます。
Scriptmanager の呼び出しは/// Reflection を通じて
行われます
///
public class ClientScriptProxy
{
private static Type scriptManagerType = null;
// *** ScriptManager のプロキシ メソッドを登録します
private static MethodInfo RegisterClientScriptBlockMethod;
private static MethodInfo RegisterStartupScriptMethod;
private static MethodInfo RegisterClientScriptIncludeMethod;
private static MethodInfo RegisterClientScriptResourceMethod;
//プライベート静的 MethodInfo RegisterPostBackControlMethod;
//プライベート静的 MethodInfo GetWebResourceUrlMethod;
ClientScriptManager クライアントスクリプト;
///
/// この Web アプリケーションで MsAjax が利用可能かどうかを判断します
///
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
_IsMsAjax を返します。
プライベート
静的ブール値 _IsMsAjax = false
;
public bool IsMsAjaxOnPage
{
get
{
return _IsMsAjaxOnPage;
プライベート
ブール_IsMsAjaxOnPage = false
;
///
このオブジェクトにアクセスする
ために常に使用される、 /// このクラスの現在のインスタンス
。/// 参照がシングルトンとして使用されることを保証する
ためのパブリック コンストラクターはありません
。///
public static ClientScriptProxy Current
{
get
{
return
( HttpContext.Current.Items["__ClientScriptProxy"] ??
(HttpContext.Current.Items["__ClientScriptProxy"] =
new ClientScriptProxy(HttpContext.Current.Handler as Page) )))
ClientScriptProxy として;
}
}
///
/// 基本コンストラクター。
/// 在庫
を取得できるようにページ名を渡します。
///
///
protected ClientScriptProxy(Page CurrentPage)
{
this.clientScript = CurrentPage .ClientScript;
}
///
/// MS Ajax が現在の Web アプリケーションに /// 登録されているかどうかを確認します
。
///
/// 注: メソッドは静的であるため、/// どこ
からでも直接アクセスできます
///
///
public static bool CheckForMsAjax()
{
scriptManagerType = Type. GetType("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false);
if (scriptManagerType != null)
{
_IsMsAjax = true;
true を返します。
}
_IsMsAjax = false;
false を返します。
}
///
/// クライアント スクリプト ブロックをページに登録します。
///
///
///
/// param>
///
///
public void RegisterClientScriptBlock(Control control, Type type, string key, string script, bool addScriptTags )
{
if (this.IsMsAjax)
{
if (RegisterClientScriptBlockMethod == null)
RegisterClientScriptBlockMethod = scriptManagerType.GetMethod("RegisterClientScriptBlock");
RegisterClientScriptBlockMethod.Invoke(null, new object[5] { コントロール、タイプ、キー、スクリプト、addScriptTags });
else
this.clientScript.RegisterClientScriptBlock(type, key, script, addScriptTags)
;
}
///
/// ページの下部に配置されるスタートアップ コード スニペットを登録します
///
///
///
///
///
///
public void RegisterStartupScript(Control control, Type type, string key, string script, bool addStartupTags)
{
if (this.IsMsAjax)
{
if (RegisterStartupScriptMethod == null)
RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript");
RegisterStartupScriptMethod.Invoke(null, new object[5] { コントロール、タイプ、キー、スクリプト、addStartupTags });
else
this.clientScript.RegisterStartupScript(type, key, script, addStartupTags)
;
}
///
/// 外部スクリプト URL のページにスクリプト インクルード タグを登録します
///
///
///
///
///
public void RegisterClientScriptInclude(Control control, Type type, string key, string url)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoke(null, new object[4] { コントロール、タイプ、キー、URL });
それ
以外の場合は
this.clientScript.RegisterClientScriptInclude( type, key, url);
}
///
/// WebResource のページに script include タグを追加します。
///
///
///
/// param>
public void RegisterClientScriptResource(Control control, Type type, string resourceName)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoke(null, new object[3] { control, type, resourceName });
それ
以外の場合は
this.clientScript.RegisterClientScriptResource(type,resourceName);
}
public string GetWebResourceUrl(Control control, Type type, string resourceName)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// GetWebResourceUrlMethod.Invoke(null, new object[2] { resourceName, control.GetType().Assembly }) を文字列として返します。
//}
//そうでない場合は
this.clientScript.GetWebResourceUrl(type, resourceName) を返します。
}
}
このコードは基本的に、MS Ajax アセンブリに型としてアクセスできるかどうかをチェックし、アクセスできる場合は MS Ajax がインストールされていると想定します。これは完全に最適とは言えません。ScriptManager が現在のページで実際に使用されているかどうかを知ることができればよいのですが、すべてのコントロールをスキャンしない限り (遅い)、それを簡単に行う方法は見つかりません。
コントロールは、ScriptManager メソッドへの Reflection 呼び出しを行う際のオーバーヘッドの一部を延期するために、各 MethodInfo 構造体をキャッシュします。これらのメソッドを大量に呼び出さない限り、ここでの Reflection がオーバーヘッドについてあまり心配することはないと思います (リソースがたくさんある場合は可能だと思います。たとえば、FreeTextBox のようなコントロールを考えてください)。それでも、反射のオーバーヘッドはおそらく心配する必要はありません。
このクラスを使用するには、ClientScript へのすべての呼び出しが代わりにこのクラスの呼び出しに置き換えられます。そこで、コントロールの初期化中のどこかに次の内容を追加します。
protected override void OnInit(EventArgs e)
{
this.ClientScriptProxy = ClientScriptProxy.Current;
Base.OnInit(e);
}
そしてそれを使用するには:
this.ClientScriptProxy.RegisterClientScriptInclude(this,this.GetType(),
ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE,
this.ResolveUrl(this.ScriptLocation));
最初のパラメータは ScriptManager 呼び出しと同様にコントロール インスタンス (通常はこれ) であるため、ClientScript コードから切り替えるときにパラメータがわずかに変更されることに注意してください。
このコードをコントロールに追加すると、UpdatePanel の問題は解消され、UpdatePanel 内でホストされているコントロールでも再び正しくレンダリングされるようになりました。