على مدار الأسابيع القليلة الماضية منذ طرح الإصدار التجريبي من MS Ajax، تلقيت عددًا من التقارير حول مواجهة التحكم في wwHoverPanel لبعض المشكلات عند التشغيل مع MS Ajax. لا تتداخل عناصر التحكم نفسها مع MS AJAX بشكل مباشر، ولكن إذا كنت تقوم بلصق عناصر التحكم داخل AJAX UpdatePanel()، فهناك مشكلة لأن رمز البرنامج النصي الذي تبثه عناصر التحكم لا يتم إنشاؤه بشكل صحيح في التحديثات التي تم إنشاؤها بواسطة رد الاتصال . مع فقدان رمز البرنامج النصي، لا تزال عناصر التحكم تعمل ولكنها تظهر بعض السلوكيات غير المتوقعة. على سبيل المثال، ستفقد لوحة التمرير الموضوعة في لوحة التحديث موضعها في كثير من الحالات، وبدلاً من الظهور عند موضع مؤشر الماوس الحالي، ستظهر على حدود عنصر التحكم في الحاوية الذي تعيش فيه.
تكمن المشكلة في أن شركة Microosft قررت في MS AJAX Beta استخدام محرك منفصل تمامًا لإنشاء البرامج النصية والذي يتم تشغيله من خلال عنصر التحكم ScriptManager. يحاكي MS Ajax ScriptManager العديد من أساليب كائن ClientScript، ولكنه يوفرها كطرق ثابتة (لحسن الحظ! بدون ذلك سنفشل حقًا).
لذا فإن أساليب مثل RegisterClientScriptBlock وResgisterClientScriptResources - أي شيء يتعامل مع إدخال كود البرنامج النصي إلى الصفحة له طرق ثابتة ذات صلة في ScriptManager. تقوم أساليب ScriptManager بتمرير عنصر التحكم كمعلمة أولى إضافية ولكنها بخلاف ذلك تحاكي ClientScriptManager الموجود.
ومع ذلك، فإن هذا السلوك الجديد يضع عناصر التحكم الموجودة في ربط - إذا كان الكود يستخدم ClientScriptManager، فلن يتمكن UpdatePanels من رؤية كود البرنامج النصي (إذا كان يحتاج إلى التحديث في رد اتصال). ولكن في نفس الوقت لا يستطيع مطور التحكم افتراض وجود MS Ajax ScriptManager بالفعل.
والنتيجة النهائية لكل هذا هي أنه ليس من السهل تمامًا التعامل مع عدم التطابق هذا وما يجب أن يحدث هو أنه يجب إنشاء كائن مجمّع يمكنه تحديد عنصر التحكم الذي سيتم استخدامه. يحتاج المجمع إلى التعامل مع تحديد ما إذا كان MS Ajax متاحًا في التطبيق أم لا، وإذا كان كذلك، فاستخدم الانعكاس للوصول إلى ScriptManager لكتابة أي رمز نصي.
على الرغم من ذلك، لا يمكنني الحصول على الفضل في ذلك: نشر إيلون ليبتون حول هذه المشكلة منذ فترة وكان الكود الخاص به هو ما أحتاجه حقًا لبدء هذا الأمر، لقد قمت للتو بتغليف الشيء في كائن ClientScriptProxy الذي استخدمته في عدد قليل من الضوابط. لقد قمت بشكل أساسي بإضافة عدد قليل من أساليب ClientScript التي أستخدمها في تطبيقاتي. وهنا الطبقة:
[*** تم تحديث الكود: 12/12/2006 من التعليقات *** ]
/// <summary>
/// هذا كائن وكيل لكائن Page.ClientScript وMS Ajax ScriptManager
/// الذي يمكن أن يعمل في حالة عدم وجود MS Ajax. نظرًا لأن MS Ajax
/// قد لا يكون متاحًا، فإن الوصول إلى الأساليب مباشرة غير ممكن
/// ونحن مطالبون بالإشارة بشكل غير مباشر إلى أساليب البرنامج النصي للعميل من خلال
/// هذه الفئة.
///
/// يجب استدعاء هذه الفئة عند بدء تشغيل عنصر التحكم واستخدامها
/// لاستبدال كافة الاستدعاءات Page.ClientScript. يتم إجراء استدعاءات Scriptmanager
/// من خلال الانعكاس
/// </summary>
public class ClientScriptProxy
{
Private static Type scriptManagerType = null;
// *** تسجيل طرق الوكيل الخاصة بـ ScriptManager
Private static MethodInfo RegisterClientScriptBlockMethod;
طريقة معلومات ثابتة خاصة RegisterStartupScriptMethod؛
خاص ثابت MethodInfo RegisterClientScriptIncludeMethod؛
طريقة معلومات ثابتة خاصة RegisterClientScriptResourceMethod؛
// معلومات ثابتة خاصة RegisterPostBackControlMethod;
// خاص ثابت MethodInfo GetWebResourceUrlMethod؛
ClientScriptManager clientScript;
/// <summary>
/// تحديد ما إذا كان MsAjax متاحًا في تطبيق الويب هذا
/// </summary>
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
return _IsMsAjax;
}
}
منطق ثابت خاص _IsMsAjax = false;
public bool IsMsAjaxOnPage
{
get
{
return _IsMsAjaxOnPage;
}
}
public bool _IsMsAjaxOnPage = false;
/// <summary>
/// المثيل الحالي لهذه الفئة والذي يجب استخدامه دائمًا
للوصول إلى هذا الكائن. لا توجد مُنشئات عامة
/// للتأكد من استخدام المرجع باعتباره Singleton.
/// </summary>
public static ClientScriptProxy Current
{
get
{
return
( HttpContext.Current.Items["__ClientScriptProxy"] ??
(HttpContext.Current.Items["__ClientScriptProxy"] =
new ClientScriptProxy(HttpContext.Current.Handler كصفحة )))
كـ ClientScriptProxy؛
}
}
/// <summary>
/// مُنشئ القاعدة. قم بتمرير اسم الصفحة حتى نتمكن من التقاط
/// المخزون
/// </summary>
/// <param name="CurrentPage"></param>
protected ClientScriptProxy(Page CurrentPage)
{
this.clientScript = CurrentPage .ClientScript;
}
/// <summary>
/// يتحقق لمعرفة ما إذا كان MS Ajax مسجلاً في
تطبيق الويب الحالي ///.
///
/// ملاحظة: الطريقة ثابتة لذا يمكن الوصول إليها مباشرة من
/// في أي مكان
/// </summary>
/// <returns></returns>
public static bool CheckForMsAjax()
{
scriptManagerType = Type. GetType("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, الإصدار=1.0.61025.0, Culture=محايد, PublicKeyToken=31bf3856ad364e35", false);
if (scriptManagerType != null)
{
_IsMsAjax = true;
عودة صحيحة؛
}
_IsMsAjax = false;
عودة كاذبة.
}
/// <summary>
/// تسجيل كتلة البرنامج النصي للعميل في الصفحة.
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="key"></ param>
/// <param name="script"></param>
/// <param name="addScriptTags"></param>
public void RegisterClientScriptBlock(التحكم في التحكم، نوع النوع، مفتاح السلسلة، البرنامج النصي للسلسلة، bool addScriptTags )
{
if (this.IsMsAjax)
{
if (RegisterClientScriptBlockMethod == null)
RegisterClientScriptBlockMethod = scriptManagerType.GetMethod("RegisterClientScriptBlock");
RegisterClientScriptBlockMethod.Invoc(null, new object[5] { control, type, key, script, addScriptTags });
}
else
this.clientScript.RegisterClientScriptBlock(type, key, script, addScriptTags);
}
/// <summary>
/// تسجيل مقتطف رمز بدء التشغيل الذي يتم وضعه في أسفل الصفحة
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="key"></param>
/// <param name="script"></param>
/// <param name="addStartupTags" </param>
public void RegisterStartupScript(Control control, Type type, string key, string script, bool addStartupTags)
{
if (this.IsMsAjax)
{
if (RegisterStartupScriptMethod == null)
RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript");
RegisterStartupScriptMethod.Invoc(null, new object[5] { control, type, key, script, addStartupTags });
}
else
this.clientScript.RegisterStartupScript(type, key, script, addStartupTags);
}
/// <summary>
/// تسجيل علامة تضمين البرنامج النصي في الصفحة لعنوان url للبرنامج النصي الخارجي
/// </summary>
/// <param name="control"></param>
/// <param name ="type"></param>
/// <param name="key"></param>
/// <param name="url"></param>
public void RegisterClientScriptInclude(تحكم التحكم، نوع النوع، السلسلة key, string url)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoc(null, new object[4] { control, type, key, url });
}
else
this.clientScript.RegisterClientScriptInclude( type, key, url);
}
/// <summary>
/// يضيف علامة تضمين البرنامج النصي إلى صفحة WebResource.
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="resourceName"></ param>
public void RegisterClientScriptResource(Control control, Type type, string ResourcesName)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoc(null, new object[3] { control, type, ResourcesName });
}
else
this.clientScript.RegisterClientScriptResource(type,resourceName);
}
public string GetWebResourceUrl(Control control, Type type, string ResourcesName)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// return GetWebResourceUrlMethod.Invoc(null, new object[2] { ResourceName, control.GetType().Assembly }) كسلسلة؛
//
//else
return this.clientScript.GetWebResourceUrl(type, ResourceName);
}
}
يتحقق الرمز بشكل أساسي لمعرفة ما إذا كان يمكن الوصول إلى مجموعة MS Ajax كنوع، وإذا كان الأمر كذلك، فيفترض أنه تم تثبيت MS Ajax. هذا ليس الحل الأمثل - سيكون من الأفضل معرفة ما إذا كان يتم استخدام ScriptManager بالفعل في الصفحة الحالية، ولكن بدون فحص جميع عناصر التحكم (بطيء) لا أستطيع رؤية طريقة للقيام بذلك بسهولة.
يقوم عنصر التحكم بتخزين كل بنية من بنيات MethodInfo مؤقتًا لتأجيل بعض الحمل أثناء إجراء استدعاءات الانعكاس لأساليب ScriptManager. لا أعتقد أن الانعكاس هنا سيسبب الكثير من القلق بشأن النفقات العامة إلا إذا كان لديك الكثير من الاستدعاءات لهذه الأساليب (أفترض أن هذا ممكن إذا كان لديك الكثير من الموارد - فكر في عنصر تحكم مثل FreeTextBox على سبيل المثال). وحتى في هذه الحالة، ربما لا يستحق الانعكاس العلوي القلق بشأنه.
لاستخدام هذه الفئة، يتم استبدال كافة الاستدعاءات إلى ClientScript باستدعاء هذه الفئة بدلاً من ذلك. لذلك في مكان ما أثناء تهيئة عنصر التحكم أقوم بإضافة:
تجاوز محمي 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 وبدأت في العرض بشكل صحيح مرة أخرى حتى مع عناصر التحكم المستضافة داخل UpdatePanels.