MS Ajax Beta가 출시된 이후 지난 몇 주 동안 저는 wwHoverPanel 컨트롤이 MS Ajax와 함께 실행될 때 일부 문제가 발생한다는 보고를 많이 받았습니다. 컨트롤 자체는 MS AJAX를 직접적으로 방해하지 않지만 AJAX UpdatePanel() 내부에 컨트롤을 고정하는 경우 컨트롤이 내뱉는 스크립트 코드가 콜백 생성 업데이트에 제대로 생성되지 않으므로 문제가 있습니다. . 스크립트 코드가 누락되어도 컨트롤은 계속 작동하지만 예상치 못한 동작이 나타납니다. 예를 들어 업데이트 패널에 배치된 호버 패널은 많은 경우 위치를 잃고 현재 마우스 커서 위치에서 팝업되는 대신 해당 패널이 있는 컨테이너 컨트롤의 테두리에 팝업됩니다.
문제는 Microosft가 MS AJAX Beta에서 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일 *** ]
///
MS Ajax가 없을 때 동작할 수 있는 ///
Page.ClientScript 및 MS Ajax ScriptManager 객체에 대한 프록시 객체입니다
.MS Ajax는
/// 사용하지 못할 수 있으므로 메서드에 직접 액세스하는 것은 /// 불가능하며
/// 이 클래스를 통해 /// 클라이언트 스크립트 메서드를 간접적으로 참조해야 합니다
.
///
/// 이 클래스는 컨트롤 시작 시 호출되어야 하며
/// 모든 호출 Page.ClientScript를 대체하는 데 사용됩니다.
Scriptmanager 호출은/// 반사를 통해
이루어집니다
. ///
public class ClientScriptProxy
{
private static Type scriptManagerType = null;
// *** ScriptManager의 프록시 메서드 등록
private static MethodInfo RegisterClientScriptBlockMethod;
개인 정적 MethodInfo RegisterStartupScriptMethod;
개인 정적 MethodInfo RegisterClientScriptIncludeMethod;
개인 정적 MethodInfo RegisterClientScriptResourceMethod;
//개인 정적 MethodInfo RegisterPostBackControlMethod;
//비공개 정적 MethodInfo GetWebResourceUrlMethod;
ClientScriptManager 클라이언트스크립트;
///
/// 이 웹 애플리케이션에서 MsAjax를 사용할 수 있는지 확인
///
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
_IsMsAjax를 반환합니다.
}
}
개인 정적 bool _IsMsAjax = false;
공개 bool IsMsAjaxOnPage
{
get
{
return _IsMsAjaxOnPage;
}
}
개인 bool _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 .클라이언트스크립트;
}
///
웹 애플리케이션
에 MS Ajax가 등록되어 있는지 확인합니다
.///
/// 참고: 메서드는 정적이므로/// 어디서나
직접 액세스할 수 있습니다
. ///
///
public static bool CheckForMsAjax()
{
scriptManagerType = Type. GetType("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, 버전=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false);
if (scriptManagerType != null)
{
_IsMsAjax = true;
사실을 반환;
}
_IsMsAjax = 거짓;
거짓을 반환;
}
///
/// 페이지에 클라이언트 스크립트 블록을 등록합니다.
///
///
///
/// param>
///
///
public void RegisterClientScriptBlock(컨트롤 제어, 유형 유형, 문자열 키, 문자열 스크립트, 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(컨트롤 제어, 유형 유형, 문자열 키, 문자열 스크립트, 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(컨트롤 제어, 유형 유형, 문자열 키, 문자열 url)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoke(null, new object[4] { 컨트롤, 유형, 키, URL });
}
else
this.clientScript.RegisterClientScriptInclude( 유형, 키, url);
}
///
/// WebResource 페이지에 스크립트 포함 태그를 추가합니다.
///
///
///
/// param>
public void RegisterClientScriptResource(컨트롤 제어, 유형 유형, 문자열 자원 이름)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoke(null, 새 개체[3] { 컨트롤, 유형, 리소스 이름 });
}
else
this.clientScript.RegisterClientScriptResource(type,resourceName);
}
public string GetWebResourceUrl(컨트롤 제어, 유형 유형, 문자열 자원 이름)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// GetWebResourceUrlMethod.Invoke(null, new object[2] {resourceName, control.GetType().Assembly })를 문자열로 반환합니다.
//}
//그렇지 않으면
this.clientScript.GetWebResourceUrl(type, resourcesName)을 반환합니다.
}
}
코드는 기본적으로 MS Ajax 어셈블리가 유형으로 액세스될 수 있는지 확인하고, 그렇다면 MS Ajax가 설치되어 있다고 가정합니다. 이는 최적이 아닙니다. ScriptManager가 현재 페이지에서 실제로 사용되고 있는지 여부를 아는 것이 더 좋지만 모든 컨트롤을 검색하지 않으면(느리게) 이를 쉽게 수행하는 방법을 알 수 없습니다.
컨트롤은 ScriptManager 메서드에 대한 Reflection 호출 시 발생하는 오버헤드 중 일부를 연기하기 위해 각 MethodInfo 구조를 캐시합니다. 나는 이러한 메서드에 대한 호출이 많이 있지 않는 한 여기의 Reflection이 오버헤드에 대해 많은 걱정을 야기할 것이라고 생각하지 않습니다(리소스가 많으면 가능하다고 생각합니다. 예를 들어 FreeTextBox와 같은 컨트롤을 생각해 보세요). 그럼에도 불구하고 Reflection 오버헤드는 걱정할 가치가 없을 것입니다.
이 클래스를 사용하려면 ClientScript에 대한 모든 호출이 대신 이 클래스 호출로 대체됩니다. 따라서 컨트롤을 초기화하는 동안 어딘가에 다음을 추가합니다.
protected 재정의 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 내부에 호스팅된 컨트롤을 사용해도 다시 제대로 렌더링되기 시작했습니다.