ในช่วงสองสามสัปดาห์ที่ผ่านมานับตั้งแต่เปิดตัว MS Ajax Beta ฉันได้รับรายงานจำนวนหนึ่งเกี่ยวกับการควบคุม 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 มีอยู่จริง
ผลลัพธ์สุดท้ายของทั้งหมดนี้ก็คือ มันไม่ตรงไปตรงมาเลยในการจัดการกับความไม่ตรงกันนี้ และสิ่งที่ต้องเกิดขึ้นก็คือ จำเป็นต้องสร้างออบเจ็กต์ wrapper ที่สามารถตัดสินใจได้ว่าจะใช้การควบคุมใด Wrapper จำเป็นต้องจัดการกับการตัดสินใจว่า MS Ajax พร้อมใช้งานในแอปพลิเคชันหรือไม่ และหากมี ให้ใช้ Reflection เพื่อเข้าถึง ScriptManager เพื่อเขียนโค้ดสคริปต์ใดๆ
ฉันไม่สามารถให้เครดิตสำหรับสิ่งนี้ได้: Eilon Lipton โพสต์เกี่ยวกับปัญหานี้มาระยะหนึ่งแล้วและรหัสของเขาคือสิ่งที่ฉันต้องการจริงๆ เพื่อเริ่มต้นสิ่งนี้ ฉันเพิ่งรวมสิ่งนี้ไว้ในวัตถุ ClientScriptProxy ที่ฉันใช้กับกำมือหนึ่ง ของการควบคุม โดยพื้นฐานแล้วฉันได้เพิ่มวิธี ClientScript จำนวนหนึ่งที่ฉันใช้ในแอปพลิเคชันของฉัน นี่คือชั้นเรียน:
[*** รหัสอัปเดต: 12/12/2549 จากความคิดเห็น *** ]
///
/// นี่คือวัตถุพร็อกซีสำหรับวัตถุ Page.ClientScript และ MS Ajax ScriptManager
/// ที่สามารถดำเนินการได้เมื่อไม่มี MS Ajax เนื่องจาก MS Ajax
/// อาจไม่สามารถเข้าถึงได้ การเข้าถึงวิธีการโดยตรงจึงไม่สามารถทำได้
/// และเราจำเป็นต้องอ้างอิงวิธีสคริปต์ไคลเอนต์ทางอ้อมผ่าน
/// คลาสนี้
///
/// คลาสนี้ควรถูกเรียกใช้เมื่อเริ่มต้นการควบคุมและใช้
/// เพื่อแทนที่การโทรทั้งหมด Page.ClientScript โทร Scriptmanager ถูกสร้างขึ้น
/// ผ่าน Reflection
///
ClientScriptProxy ระดับสาธารณะ
{
private static Type scriptManagerType = null;
// *** ลงทะเบียนวิธีการพร็อกซีของ ScriptManager
ส่วนตัว Static MethodInfo RegisterClientScriptBlockMethod;
MethodInfo แบบคงที่ส่วนตัว RegisterStartupScriptMethod;
MethodInfo แบบคงที่ส่วนตัว RegisterClientScriptIncludeMethod;
MethodInfo แบบคงที่ส่วนตัว RegisterClientScriptResourceMethod;
// MethodInfo แบบคงที่ส่วนตัว RegisterPostBackControlMethod;
// MethodInfo GetWebResourceUrlMethod แบบคงที่ส่วนตัว;
ClientScriptManager ไคลเอนต์สคริปต์;
///
/// พิจารณาว่า MsAjax มีอยู่ในเว็บแอปพลิเคชันนี้หรือไม่
///
บูลสาธารณะ IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
กลับ _IsMsAjax;
}
}
บูลคงที่ส่วนตัว _IsMsAjax = false;
บูลสาธารณะ IsMsAjaxOnPage
{
รับ
{
กลับ _IsMsAjaxOnPage;
}
}
บูลส่วนตัว _IsMsAjaxOnPage = false;
///
/// ตัวอย่างปัจจุบันของคลาสนี้ซึ่งควรใช้เสมอเพื่อ
/// เข้าถึงวัตถุนี้ ไม่มีตัวสร้างสาธารณะที่จะ
/// ตรวจสอบให้แน่ใจว่าการอ้างอิงนั้นถูกใช้เป็นซิงเกิลตัน
///
ClientScriptProxy Current แบบคงที่สาธารณะ
{
รับ
{
return
( HttpContext.Current.Items["__ClientScriptProxy"] ??
(HttpContext.Current.Items["__ClientScriptProxy"] =
new ClientScriptProxy(HttpContext.Current.Handler as Page )))
เป็น ClientScriptProxy;
}
}
/// <สรุป>
/// ตัวสร้างฐาน ส่งผ่านชื่อเพจเพื่อให้เราสามารถรับ
/// หุ้นของ
///
///
protected ClientScriptProxy(Page CurrentPage)
{
this.clientScript = CurrentPage .ไคลเอนต์สคริปต์;
-
///
/// ตรวจสอบเพื่อดูว่า MS Ajax ได้รับการลงทะเบียนกับ
/// เว็บแอปพลิเคชัน
ปัจจุบันหรือไม่///
/// หมายเหตุ: วิธีการเป็นแบบคงที่เพื่อให้สามารถเข้าถึงได้โดยตรงจาก
/// ทุกที่
///
///
บูลคงที่สาธารณะ CheckForMsAjax()
{
scriptManagerType = Type. GetType ("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, Version=1.0.61025.0, วัฒนธรรม=เป็นกลาง, PublicKeyToken=31bf3856ad364e35", false);
ถ้า (scriptManagerType != null)
{
_IsMsAjax = จริง;
กลับเป็นจริง;
-
_IsMsAjax = เท็จ;
กลับเท็จ;
-
///
/// ลงทะเบียนบล็อกสคริปต์ไคลเอนต์ในหน้า
///
///
///
/// param>
///
///
โมฆะสาธารณะ RegisterClientScriptBlock(การควบคุมการควบคุม, ประเภทประเภท, คีย์สตริง, สคริปต์สตริง, บูล addScriptTags )
{
if (this.IsMsAjax)
{
ถ้า (RegisterClientScriptBlockMethod == null)
RegisterClientScriptBlockMethod = scriptManagerType.GetMethod("RegisterClientScriptBlock");
RegisterClientScriptBlockMethod.Invoid (null, วัตถุใหม่ [5] { ควบคุม, พิมพ์, คีย์, สคริปต์, addScriptTags });
}
อื่น ๆ
this.clientScript.RegisterClientScriptBlock (ประเภท, คีย์, สคริปต์, addScriptTags);
-
///
/// ลงทะเบียนข้อมูลโค้ดเริ่มต้นที่ถูกวางไว้ที่ด้านล่างของหน้า
///
///
///
///
///
///
โมฆะสาธารณะ RegisterStartupScript (การควบคุมการควบคุม ประเภทประเภท คีย์สตริง สคริปต์สตริง บูล addStartupTags)
{
if (this.IsMsAjax)
{
ถ้า (RegisterStartupScriptMethod == null)
RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript");
RegisterStartupScriptMethod.Invoid (null, วัตถุใหม่ [5] { ควบคุม, พิมพ์, คีย์, สคริปต์, addStartupTags });
}
อื่น ๆ
this.clientScript.RegisterStartupScript (ประเภท, คีย์, สคริปต์, addStartupTags);
-
///
/// ลงทะเบียนสคริปต์รวมแท็กลงในหน้าสำหรับ URL สคริปต์ภายนอก
///
///
/// <ชื่อพารามิเตอร์ ="type">
///
///
โมฆะสาธารณะ RegisterClientScriptInclude(การควบคุมการควบคุม, ประเภทประเภท, สตริง คีย์, URL สตริง)
{
if (this.IsMsAjax)
{
ถ้า (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod ("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Inrigg (null, วัตถุใหม่ [4] { ควบคุม, พิมพ์, คีย์, url });
}
อื่น ๆ
this.clientScript.RegisterClientScriptInclude (ประเภท, คีย์, url);
-
///
/// เพิ่มสคริปต์รวมแท็กลงในเพจสำหรับ WebResource
///
///
///
/// พารามิเตอร์>
โมฆะสาธารณะ RegisterClientScriptResource (การควบคุมการควบคุม ประเภทประเภท สตริงชื่อทรัพยากร)
{
ถ้า (this.IsMsAjax)
{
ถ้า (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod ("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoid (null วัตถุใหม่ [3] { ควบคุม พิมพ์ ชื่อทรัพยากร });
}
อื่น ๆ
this.clientScript.RegisterClientScriptResource (ประเภท, ชื่อทรัพยากร);
-
สตริงสาธารณะ GetWebResourceUrl (การควบคุมการควบคุม, ประเภทประเภท, สตริงชื่อทรัพยากร)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// return GetWebResourceUrlMethod.Invoid(null, new object[2] { resourcesName, control.GetType().Assembly }) เป็นสตริง;
//
//else
ส่งคืน this.clientScript.GetWebResourceUrl (ประเภท, ชื่อทรัพยากร);
-
-
โดยทั่วไปโค้ดจะตรวจสอบเพื่อดูว่าแอสเซมบลี MS Ajax สามารถเข้าถึงได้เป็นประเภทหรือไม่ และหากเป็นเช่นนั้นถือว่าติดตั้ง MS Ajax แล้ว สิ่งนี้ยังไม่ค่อยเหมาะสมนัก ควรทราบว่า ScriptManager ถูกใช้จริงบนหน้าปัจจุบันหรือไม่ แต่ถ้าไม่มีการสแกนผ่านส่วนควบคุมทั้งหมด (ช้า) ฉันก็ไม่เห็นวิธีการทำเช่นนั้นได้ง่ายๆ
ตัวควบคุมจะแคชแต่ละโครงสร้าง MethodInfo เพื่อเลื่อนโอเวอร์เฮดบางส่วนในการเรียก Reflection ไปยังเมธอด ScriptManager ฉันไม่คิดว่า Reflection ที่นี่จะทำให้เกิดความกังวลอย่างมากเกี่ยวกับค่าใช้จ่ายเว้นแต่คุณจะมีการเรียกใช้วิธีการเหล่านี้เป็นจำนวนมาก (ฉันคิดว่าเป็นไปได้ถ้าคุณมีทรัพยากรจำนวนมาก ลองนึกถึงการควบคุมเช่น FreeTextBox เป็นต้น) ถึงอย่างนั้นค่าโสหุ้ยของการสะท้อนกลับก็อาจไม่คุ้มที่จะกังวล
หากต้องการใช้คลาสนี้การเรียก ClientScript ทั้งหมดจะถูกแทนที่ด้วยการเรียกคลาสนี้แทน ดังนั้นในระหว่างการเริ่มต้นการควบคุมฉันจึงเพิ่ม:
การป้องกันการแทนที่เป็นโมฆะ OnInit (EventArgs e)
{
this.ClientScriptProxy = ClientScriptProxy.Current;
ฐาน OnInit(e);
-
จากนั้นจึงใช้มัน:
this.ClientScriptProxy.RegisterClientScriptInclude (นี่, this.GetType (),
ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE,
this.ResolveUrl (this.ScriptLocation));
โปรดสังเกตว่าพารามิเตอร์แรกคืออินสแตนซ์การควบคุม (โดยทั่วไปจะเป็นสิ่งนี้) เช่นเดียวกับการเรียก ScriptManager ดังนั้นจะมีการเปลี่ยนแปลงพารามิเตอร์เล็กน้อยเมื่อเปลี่ยนจากโค้ด ClientScript
เมื่อฉันเพิ่มโค้ดนี้ลงในการควบคุมแล้ว ปัญหาเกี่ยวกับ UpdatePanel ก็หายไป และเริ่มแสดงผลได้อย่างถูกต้องอีกครั้ง แม้ว่าจะมีการควบคุมที่โฮสต์อยู่ภายใน UpdatePanels ก็ตาม