Nas últimas semanas, desde que o MS Ajax Beta foi lançado, tenho recebido vários relatórios sobre o controle wwHoverPanel apresentando alguns problemas ao ser executado em combinação com o MS Ajax. Os controles em si não interferem diretamente no MS AJAX, mas se você estiver inserindo os controles dentro de um AJAX UpdatePanel(), há um problema, pois o código do script que os controles exibem não é gerado corretamente nas atualizações geradas pelo retorno de chamada . Com a falta do código do script, os controles ainda funcionam, mas exibem alguns comportamentos inesperados. Por exemplo, um painel suspenso colocado em um painel de atualização perderá seu posicionamento em muitos casos e, em vez de aparecer na posição atual do cursor do mouse, aparecerá na borda do controle do contêiner em que ele reside.
O problema é que a Microosft decidiu no MS AJAX Beta usar um mecanismo de geração de script completamente separado, controlado pelo controle ScriptManager. O MS Ajax ScriptManager imita muitos dos métodos do objeto ClientScript, mas os fornece como métodos estáticos (felizmente! Sem isso estaríamos realmente ferrados).
Portanto, métodos como RegisterClientScriptBlock, ResgisterClientScriptResources – qualquer coisa que lide com a inserção de código de script na página tem métodos estáticos relacionados no ScriptManager. Os métodos ScriptManager passam o Control como um primeiro parâmetro adicional, mas imitam o ClientScriptManager existente.
Porém, esse novo comportamento coloca os controles existentes em uma ligação - se o código usar ClientScriptManager, então UpdatePanels não será capaz de ver o código do script (se precisar de atualização em um retorno de chamada). Mas, ao mesmo tempo, o desenvolvedor do controle não pode presumir que o MS Ajax ScriptManager realmente existe.
O resultado final de tudo isso é que não é exatamente simples lidar com essa incompatibilidade e o que precisa acontecer é que um objeto wrapper precisa ser criado para decidir qual controle usar. O wrapper precisa decidir se o MS Ajax está disponível no aplicativo e, se estiver, usar o Reflection para acessar o ScriptManager para escrever qualquer código de script.
Porém, não posso levar o crédito por isso: Eilon Lipton postou sobre esse problema há algum tempo e seu código realmente era o que eu precisava para fazer isso decolar. Acabei de agrupar tudo em um objeto ClientScriptProxy que usei em alguns de controles. Basicamente, adicionei alguns métodos ClientScript que uso em meus aplicativos. Aqui está a aula:
[*** código atualizado: 12/12/2006 a partir de comentários *** ]
///
/// Este é um objeto proxy para o
objeto Page.ClientScript e MS Ajax ScriptManager /// que pode operar quando o MS Ajax não está presente. Como o MS Ajax
/// pode não estar disponível, o acesso direto aos métodos não é possível
/// e somos obrigados a referenciar indiretamente os métodos de script do cliente por meio
/// desta classe.
///
/// Esta classe deve ser invocada na inicialização do Controle e ser utilizada
/// para substituir todas as chamadas Page.ClientScript. As chamadas do Scriptmanager são feitas
/// através do Reflection
///
public class ClientScriptProxy
{
private static Type scriptManagerType = null;
// *** Registra métodos proxy do ScriptManager
private static MethodInfo RegisterClientScriptBlockMethod;
private static MethodInfo RegisterStartupScriptMethod;
private static MethodInfo RegisterClientScriptIncludeMethod;
private static MethodInfo RegisterClientScriptResourceMethod;
//método estático privado RegisterPostBackControlMethod;
//método estático privado GetWebResourceUrlMethod;
ClientScriptManagerclientScript;
///
/// Determina se MsAjax está disponível nesta aplicação Web
///
public bool IsMsAjax
{
get
{
if (scriptManagerType == null)
CheckForMsAjax();
retornar _IsMsAjax;
}
}
private static bool _IsMsAjax = false;
public bool IsMsAjaxOnPage
{
get
{
return _IsMsAjaxOnPage;
}
}
private bool _IsMsAjaxOnPage = false;
///
/// Instância atual desta classe que sempre deve ser usada para
/// acessar este objeto. Não há construtores públicos para
/// garantir que a referência seja usada como um Singleton.
///
public static ClientScriptProxy Current
{
get
{
return
( HttpContext.Current.Items["__ClientScriptProxy"] ??
(HttpContext.Current.Items["__ClientScriptProxy"] =
new ClientScriptProxy(HttpContext.Current.Handler as Page )))
como ClientScriptProxy;
}
}
///
/// Construtor base. Passe o nome da página para que possamos pegar
/// o estoque
///
///
protected ClientScriptProxy(Page CurrentPage)
{
this.clientScript = CurrentPage .ClienteScript;
}
///
/// Verifica se o MS Ajax está registrado no
/// aplicativo Web atual.
///
/// Nota: O método é estático, portanto pode ser acessado diretamente de
/// qualquer lugar
///
///
public static bool CheckForMsAjax()
{
scriptManagerType = Type. GetType("Microsoft.Web.UI.ScriptManager, Microsoft.Web.Extensions, Versão = 1.0.61025.0, Cultura = neutro, PublicKeyToken = 31bf3856ad364e35", falso);
if (scriptManagerType! = null)
{
_IsMsAjax = true;
retornar verdadeiro;
}
_IsMsAjax = falso;
retornar falso;
}
///
/// Registra um bloco de script do cliente na página.
///
///
///
/// param>
///
///
public void RegisterClientScriptBlock(controle de controle, tipo de tipo, chave de string, script de string, bool addScriptTags )
{
if (this.IsMsAjax)
{
if (RegisterClientScriptBlockMethod == null)
RegisterClientScriptBlockMethod = scriptManagerType.GetMethod("RegisterClientScriptBlock");
RegisterClientScriptBlockMethod.Invoke(null, new object[5] { controle, tipo, chave, script, addScriptTags });
}
else
this.clientScript.RegisterClientScriptBlock(tipo, chave, script, addScriptTags);
}
///
/// Registra um trecho de código de inicialização que é colocado na parte inferior da página
///
///
///
///
///
///
public void RegisterStartupScript(controle de controle, tipo de tipo, chave de string, script de string, bool addStartupTags)
{
if (this.IsMsAjax)
{
if (RegisterStartupScriptMethod == null)
RegisterStartupScriptMethod = scriptManagerType.GetMethod("RegisterStartupScript");
RegisterStartupScriptMethod.Invoke(null, new object[5] { controle, tipo, chave, script, addStartupTags });
}
else
this.clientScript.RegisterStartupScript(tipo, chave, script, addStartupTags);
}
///
/// Registra uma tag de inclusão de script na página para um URL de script externo
///
///
///
///
///
public void RegisterClientScriptInclude(Control control, Type type, string chave, string url)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptIncludeMethod == null)
RegisterClientScriptIncludeMethod = scriptManagerType.GetMethod("RegisterClientScriptInclude");
RegisterClientScriptIncludeMethod.Invoke(null, new object[4] { controle, tipo, chave, url });
}
else
this.clientScript.RegisterClientScriptInclude( tipo, chave, url);
}
///
/// Adiciona uma tag de inclusão de script na página para WebResource.
///
///
///
/// parâmetro>
public void RegisterClientScriptResource (controle de controle, tipo de tipo, string resourceName)
{
if (this.IsMsAjax)
{
if (RegisterClientScriptResourceMethod == null)
RegisterClientScriptResourceMethod = scriptManagerType.GetMethod ("RegisterClientScriptResource");
RegisterClientScriptResourceMethod.Invoke(null, novo objeto[3] { controle, tipo, nomederecurso });
}
else
this.clientScript.RegisterClientScriptResource(tipo,resourceName);
}
public string GetWebResourceUrl(controle de controle, tipo de tipo, string resourceName)
{
//if (this.IsMsAjax)
//{
// if (GetWebResourceUrlMethod == null)
// GetWebResourceUrlMethod = scriptManagerType.GetMethod("GetScriptResourceUrl");
// retorna GetWebResourceUrlMethod.Invoke(null, new object[2] { resourceName, control.GetType().Assembly }) como string;
//}
//else
return this.clientScript.GetWebResourceUrl(type, resourceName);
}
}
O código basicamente verifica se o assembly MS Ajax pode ser acessado como um tipo e, em caso afirmativo, assume que o MS Ajax está instalado. Isso não é o ideal – seria melhor saber se um ScriptManager está realmente sendo usado na página atual, mas sem examinar todos os controles (lento), não consigo ver uma maneira de fazer isso facilmente.
O controle armazena em cache cada uma das estruturas MethodInfo para adiar parte da sobrecarga ao fazer as chamadas de Reflexão para os métodos ScriptManager. Não acho que o Reflection aqui vá causar muita preocupação com sobrecarga, a menos que você tenha MUITAS chamadas para esses métodos (suponho que seja possível se você tiver muitos recursos - pense em um controle como FreeTextBox, por exemplo). Mesmo assim, provavelmente não vale a pena se preocupar com a sobrecarga do Reflection.
Para usar esta classe, todas as chamadas para ClientScript são substituídas por chamar esta classe. Então, em algum lugar durante a inicialização do controle, adiciono:
substituição protegida void OnInit(EventArgs e)
{
this.ClientScriptProxy = ClientScriptProxy.Current;
base.OnInit(e);
}
E então para usá-lo:
this.ClientScriptProxy.RegisterClientScriptInclude(this,this.GetType(),
ControlResources.SCRIPTLIBRARY_SCRIPT_RESOURCE,
this.ResolveUrl(this.ScriptLocation));
Observe que o primeiro parâmetro é a instância de controle (normalmente esta), assim como a chamada ScriptManager, portanto, haverá uma ligeira alteração nos parâmetros ao mudar do código ClientScript.
Depois que adicionei esse código aos meus controles, os problemas com o UpdatePanel desapareceram e ele começou a renderizar corretamente novamente, mesmo com os controles hospedados dentro dos UpdatePanels.