За последние несколько недель с момента выхода бета-версии MS Ajax я получил ряд сообщений о том, что элемент управления wwHoverPanel сталкивается с некоторыми проблемами при работе в сочетании с MS Ajax. Сами элементы управления не мешают работе MS AJAX напрямую, но если вы вставляете элементы управления внутрь AJAX UpdatePanel(), возникает проблема, поскольку код сценария, который выдают элементы управления, не генерируется должным образом в обновлениях, генерируемых обратным вызовом. . При отсутствии кода сценария элементы управления по-прежнему работают, но демонстрируют непредвиденное поведение. Например, панель при наведении, помещенная в панель обновления, во многих случаях потеряет свое положение и вместо того, чтобы появиться в текущей позиции курсора мыши, появится на границе элемента управления контейнера, в котором она находится.
Проблема в том, что Microosft решила в бета-версии MS AJAX использовать совершенно отдельный механизм генерации сценариев, который управляется через элемент управления ScriptManager. MS Ajax ScriptManager имитирует многие методы объекта ClientScript, но предоставляет их как статические методы (к счастью! без этого мы бы по-настоящему облажались).
Таким образом, такие методы, как RegisterClientScriptBlock, ResgisterClientScriptResources — все, что связано с размещением кода сценария на странице, имеет связанные статические методы в ScriptManager. Методы ScriptManager передают элемент управления в качестве дополнительного первого параметра, но в остальном имитируют существующий ClientScriptManager.
Однако это новое поведение связывает существующие элементы управления: если код использует ClientScriptManager, то UpdatePanels не сможет видеть код сценария (если он требует обновления в обратном вызове). Но в то же время разработчик элемента управления не может предположить, что MS Ajax ScriptManager действительно существует.
Конечным результатом всего этого является то, что справиться с этим несоответствием не совсем просто, и необходимо создать объект-оболочку, которая сможет решить, какой элемент управления использовать. Оболочке необходимо решить, доступен ли MS Ajax в приложении, и если да, то использовать Reflection для доступа к ScriptManager для записи любого кода сценария.
Однако я не могу приписать это себе: Эйлон Липтон некоторое время назад писал об этой проблеме, и его код действительно был тем, что мне нужно, чтобы сдвинуть дело с мертвой точки, я просто завернул эту вещь в объект ClientScriptProxy, который я использовал в нескольких случаях. элементов управления. По сути, я добавил несколько методов ClientScript, которые использую в своих приложениях. Вот класс:
[*** код обновлен: 12.12.2006 из комментариев *** ]
///
/// Это прокси-объект для объекта Page.ClientScript и MS Ajax ScriptManager
///, который может работать, когда MS Ajax отсутствует. Поскольку MS Ajax
/// может быть недоступен, прямой доступ к методам невозможен
/// и нам необходимо косвенно ссылаться на методы клиентского сценария через
/// этот класс.
///
/// Этот класс следует вызывать при запуске Control и использовать
/// для замены всех вызовов 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 clientScript;
///
/// Определяет, доступен ли MsAjax в этом веб-приложении
///
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
вернуть _IsMsAjax;
}
}
Private static bool _IsMsAjax = false;
общественный bool IsMsAjaxOnPage
{
получить
{
вернуться _IsMsAjaxOnPage;
}
}
Private 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, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", false);
если (scriptManagerType! = null)
{
_IsMsAjax = true;
вернуть истину;
}
_IsMsAjax = ложь;
вернуть ложь;
}
///
/// Регистрирует блок клиентского скрипта на странице.
///
///
///
/// 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] {control, type, key, script, addScriptTags});
}
Еще
this.clientScript.RegisterClientScriptBlock (тип, ключ, скрипт, 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] {control, type, key, script, addStartupTags});
}
Еще
this.clientScript.RegisterStartupScript (тип, ключ, сценарий, addStartupTags);
}
///
/// Регистрирует тег включения скрипта на странице для внешнего URL-адреса скрипта
///
///
///
///
///
public void RegisterClientScriptInclude(Control control, Type type, string ключ, URL-адрес строки)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoke(null, new object[4] {control, type, key, url});
}
Еще
this.clientScript.RegisterClientScriptInclude (тип, ключ, URL);
}
///
/// Добавляет тег включения скрипта на страницу для WebResource.
///
///
///
/// param>
public void RegisterClientScriptResource (элемент управления, тип типа, строковое имя ресурса)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoke(null, new object[3] {control, type, resourcesName});
}
Еще
this.clientScript.RegisterClientScriptResource(type,resourceName);
}
public string GetWebResourceUrl(Control control, Type type, string resourcesName)
{
//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 используется на текущей странице, но без сканирования всех элементов управления (медленно) я не вижу способа легко это сделать.
Элемент управления кэширует каждую из структур MethodInfo, чтобы отложить некоторые накладные расходы при выполнении вызовов Reflection к методам ScriptManager. Я не думаю, что Reflection здесь вызовет сильное беспокойство по поводу накладных расходов, если только у вас не МНОГО вызовов этих методов (я полагаю, это возможно, если у вас много ресурсов — подумайте, например, об элементе управления, таком как FreeTextBox). Даже в этом случае о накладных расходах на отражение, вероятно, не стоит беспокоиться.
Чтобы использовать этот класс, все вызовы ClientScript заменяются вызовом этого класса. Поэтому где-то во время инициализации элемента управления я добавляю:
защищенное переопределение void OnInit (EventArgs e)
{
this.ClientScriptProxy = ClientScriptProxy.Current;
base.OnInit(е);
}
И затем использовать его:
this.ClientScriptProxy.RegisterClientScriptInclude(this,this.GetType(),
ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE,
this.ResolveUrl(this.ScriptLocation));
Обратите внимание, что первый параметр — это экземпляр элемента управления (обычно это), как и вызов ScriptManager, поэтому при переходе от кода ClientScript произойдет небольшое изменение параметров.
Как только я добавил этот код в свои элементы управления, проблемы с UpdatePanel исчезли, и он снова начал правильно отображаться, даже если элементы управления размещены внутри UpdatePanels.