Durante las últimas semanas desde que se lanzó MS Ajax Beta, he recibido varios informes de que el control wwHoverPanel tiene algunos problemas cuando se ejecuta en combinación con MS Ajax. Los controles en sí no interfieren con MS AJAX directamente, pero si coloca los controles dentro de un AJAX UpdatePanel(), hay un problema ya que el código de script que escupen los controles no se genera correctamente en las actualizaciones generadas por la devolución de llamada. . Al faltar el código del script, los controles aún funcionan, pero muestran algunos comportamientos inesperados. Por ejemplo, un panel flotante colocado en un panel de actualización perderá su posición en muchos casos y, en lugar de aparecer en la posición actual del cursor del mouse, aparecerá en el borde del control contenedor en el que se encuentra.
El problema es que Microosft decidió en MS AJAX Beta utilizar un motor de generación de scripts completamente independiente que se controla a través del control ScriptManager. MS Ajax ScriptManager imita muchos de los métodos del objeto ClientScript, pero los proporciona como métodos estáticos (¡afortunadamente! sin eso estaríamos realmente jodidos).
Entonces, métodos como RegisterClientScriptBlock, ResgisterClientScriptResources, cualquier cosa que tenga que ver con obtener código de script en la página, tiene métodos estáticos relacionados en ScriptManager. Los métodos ScriptManager pasan el Control como un primer parámetro adicional pero, por lo demás, imitan el ClientScriptManager existente.
Sin embargo, este nuevo comportamiento pone los controles existentes en un aprieto: si el código usa ClientScriptManager, UpdatePanels no podrá ver el código del script (si necesita actualizarse en una devolución de llamada). Pero al mismo tiempo el desarrollador del control no puede asumir que el MS Ajax ScriptManager realmente existe.
El resultado final de todo esto es que no es exactamente sencillo lidiar con esta discrepancia y lo que debe suceder es que se debe crear un objeto contenedor que pueda decidir qué control usar. El contenedor debe decidir si MS Ajax está disponible en la aplicación y, si lo está, usar Reflection para acceder al ScriptManager y escribir cualquier código de script.
Sin embargo, no puedo atribuirme el mérito de esto: Eilon Lipton publicó sobre este problema hace un tiempo y su código realmente era lo que necesitaba para que esto despegara, simplemente lo envolví en un objeto ClientScriptProxy que usé en un puñado de controles. Básicamente agregué algunos de los métodos ClientScript que uso en mis aplicaciones. Aquí está la clase:
[*** código actualizado: 12/12/2006 a partir de comentarios *** ]
/// <summary>
/// Este es un objeto proxy para Page.ClientScript y MS Ajax ScriptManager
/// objeto que puede funcionar cuando MS Ajax no está presente. Debido a que MS Ajax
/// puede no estar disponible, no es posible acceder a los métodos directamente
/// y debemos hacer referencia indirectamente a los métodos de script del cliente a través de
/// esta clase.
///
/// Esta clase debe invocarse al iniciar el Control y usarse
/// para reemplazar todas las llamadas a Page.ClientScript. Las llamadas a Scriptmanager se realizan
/// a través de Reflection
/// </summary>
public class ClientScriptProxy
{
private static Type scriptManagerType = null;
// *** Registrar métodos proxy de ScriptManager
private static MethodInfo RegisterClientScriptBlockMethod;
MethodInfo estático privado RegisterStartupScriptMethod;
MethodInfo estático privado RegisterClientScriptIncludeMethod;
MethodInfo estático privado RegisterClientScriptResourceMethod;
//MethodInfo estático privado RegisterPostBackControlMethod;
// Información de método estática privada GetWebResourceUrlMethod;
ClientScriptManager clientScript;
/// <summary>
/// Determina si MsAjax está disponible en esta aplicación web
/// </summary>
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
devolver _IsMsAjax;
}
}
bool estático privado _IsMsAjax = false;
public bool IsMsAjaxOnPage
{
obtener
{
return _IsMsAjaxOnPage;
}
}
bool privado _IsMsAjaxOnPage = false;
/// <summary>
/// Instancia actual de esta clase que siempre debe usarse para
/// acceder a este objeto. No hay constructores públicos para
/// garantizar que la referencia se utilice como Singleton.
/// </summary>
public static ClientScriptProxy Current
{
get
{
return
( HttpContext.Current.Items["__ClientScriptProxy"] ??
(HttpContext.Current.Items["__ClientScriptProxy"] =
new ClientScriptProxy(HttpContext.Current.Handler as Page )))
como ClientScriptProxy;
}
}
/// <resumen>
/// Constructor base. Pase el nombre de la página para que podamos recoger
/// las acciones
/// </summary>
/// <param name="CurrentPage"></param>
protected ClientScriptProxy(Page CurrentPage)
{
this.clientScript = CurrentPage .ClienteScript;
}
/// <summary>
/// Comprueba si MS Ajax está registrado con la
/// aplicación web actual.
///
/// Nota: El método es estático, por lo que se puede acceder directamente desde
/// cualquier lugar
/// </summary>
/// <returns></returns>
public static bool CheckForMsAjax()
{
scriptManagerType = Type. GetType("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, Versión=1.0.61025.0, Cultura=neutral, PublicKeyToken=31bf3856ad364e35", falso);
if (scriptManagerType! = nulo)
{
_IsMsAjax = verdadero;
devolver verdadero;
}
_IsMsAjax = falso;
devolver falso;
}
/// <summary>
/// Registra un bloque de script de cliente en la página.
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="key"></ param>
/// <param name="script"></param>
/// <param name="addScriptTags"></param>
public void RegisterClientScriptBlock(Control de control, tipo de tipo, clave de cadena, script de cadena, bool addScriptTags )
{
if (this.IsMsAjax)
{
if (RegisterClientScriptBlockMethod == null)
RegisterClientScriptBlockMethod = scriptManagerType.GetMethod("RegisterClientScriptBlock");
RegisterClientScriptBlockMethod.Invoke(nulo, nuevo objeto[5] {control, tipo, clave, secuencia de comandos, addScriptTags});
}
else
this.clientScript.RegisterClientScriptBlock(tipo, clave, script, addScriptTags);
}
/// <summary>
/// Registra un fragmento de código de inicio que se coloca en la parte inferior de la página
/// </summary>
/// <param name="control"></param>
/// <param nombre="tipo"></param>
/// <param nombre="clave"></param>
/// <param nombre="script"></param>
/// <param nombre="addStartupTags" ></param>
public void RegisterStartupScript(Control de control, Tipo de tipo, clave de cadena, script de cadena, bool addStartupTags)
{
if (this.IsMsAjax)
{
if (RegisterStartupScriptMethod == null)
RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript");
RegisterStartupScriptMethod.Invoke(nulo, nuevo objeto[5] {control, tipo, clave, secuencia de comandos, addStartupTags});
}
else
this.clientScript.RegisterStartupScript(tipo, clave, script, addStartupTags);
}
/// <summary>
/// Registra una etiqueta de inclusión de secuencia de comandos en la página para una URL de secuencia de comandos externa
/// </summary>
/// <param name="control"></param>
/// <param name ="type"></param>
/// <param name="key"></param>
/// <param name="url"></param>
public void RegisterClientScriptInclude(Control de control, Tipo de tipo, cadena clave, URL de cadena)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoke(nulo, nuevo objeto[4] {control, tipo, clave, URL});
}
else
this.clientScript.RegisterClientScriptInclude(tipo, clave, url);
}
/// <summary>
/// Agrega una etiqueta de inclusión de secuencia de comandos a la página de WebResource.
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="resourceName"></ parámetro>
public void RegisterClientScriptResource(Control de control, Tipo de tipo, cadena nombreRecurso)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoke(nulo, nuevo objeto[3] {control, tipo, nombreRecurso});
}
else
this.clientScript.RegisterClientScriptResource(tipo,resourceName);
}
cadena pública GetWebResourceUrl(control de control, tipo de tipo, nombre de recurso de cadena)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// devuelve GetWebResourceUrlMethod.Invoke(null, new object[2] { ResourceName, control.GetType().Assembly }) como cadena;
//}
//de lo contrario,
devuelve this.clientScript.GetWebResourceUrl(tipo, nombreRecurso);
}
}
Básicamente, el código verifica si se puede acceder al ensamblaje de MS Ajax como un tipo y, de ser así, asume que MS Ajax está instalado. Esto no es del todo óptimo: sería mejor saber si realmente se está utilizando un ScriptManager en la página actual, pero sin escanear todos los controles (lento) no veo una manera de hacerlo fácilmente.
El control almacena en caché cada una de las estructuras MethodInfo para diferir parte de la sobrecarga al realizar las llamadas de Reflection a los métodos de ScriptManager. No creo que Reflection aquí vaya a causar mucha preocupación por los gastos generales a menos que tenga MUCHAS llamadas a estos métodos (supongo que es posible si tiene muchos recursos; piense en un control como FreeTextBox, por ejemplo). Incluso entonces, probablemente no valga la pena preocuparse por la sobrecarga de Reflection.
Para utilizar esta clase, todas las llamadas a ClientScript se reemplazan por llamar a esta clase. Entonces, en algún lugar durante la inicialización del control agrego:
anulación protegida void OnInit(EventArgs e)
{
this.ClientScriptProxy = ClientScriptProxy.Current;
base.OnInit(e);
}
Y luego para usarlo:
this.ClientScriptProxy.RegisterClientScriptInclude(this,this.GetType(),
ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE,
this.ResolveUrl(this.ScriptLocation));
Observe que el primer parámetro es la instancia de control (normalmente esta), al igual que la llamada a ScriptManager, por lo que habrá un ligero cambio de parámetros al cambiar del código ClientScript.
Una vez que agregué este código a mis controles, los problemas con UpdatePanel desaparecieron y comenzó a renderizarse correctamente nuevamente incluso con los controles alojados dentro de UpdatePanels.