sábado, 24 de outubro de 2009

Configurando o CultureInfo do MicrosoftAjax.js no ASP NET MVC

Alguns componentes do AjaxControlToolkit dependem da cultura do usuário comportam de maneira diferente. Por exemplo o controle CalendarExtender, um usuário do Brasil verá os nomes dos meses em português já um usuário do Japão verá os mesmos nomes em Japonês.

Para usarmos estes componentes componentes precisamos fazer algumas configurações para que tudo funcione da forma que desejamos.

Assim criei uma classe chamada ScriptExtensions, esta classe estende a classe AjaxHelper do ASP NET MVC, veja o código abaixo:
/// 
/// Extensões de scripts para facilitar o uso no MVC.
///

public static class ScriptExtensions
{
///
/// Inclui referências a arquivos de script na página.
///

/// Classe extendida.
/// Caminho do script.
/// String com as tags de referência a scripts.
public static string ScriptInclude(this AjaxHelper helper, params string[] url)
{
ResourceTracker tracker = new ResourceTracker(helper.ViewContext.HttpContext);

StringBuilder sb = new StringBuilder();
foreach (string item in url)
{
if (!tracker.Contains(item))
{
tracker.Add(item);
sb.AppendFormat("", item);
sb.AppendLine();
}
}
return sb.ToString();
}

///
/// Inclui um código de script na página.
///

/// Classe extendida.
/// Chave para controlar se o script já foi registrado na página.
/// Bloco de script que será inserido na página.
/// True para incluir as tags de script.
/// Script formatado
public static string ScriptInclude(this AjaxHelper helper, string key, string script, bool includeScriptTags)
{
ResourceTracker tracker = new ResourceTracker(helper.ViewContext.HttpContext);

string scriptToWrite = string.Empty;

if (!tracker.Contains(key))
{
tracker.Add(key);
scriptToWrite = includeScriptTags ? string.Format(CultureInfo.CurrentCulture, Constants.ScriptFormat, script) : script;
}

return scriptToWrite;
}

}

A classe acima usa a classe ResourceTracker. Esta classe server para rastrearmos os scripts e css que já colocamos na tela para não colocamos a mesma referência duas vezes, Veja o código da classe ResourceTracker:

///
/// Rastreador de recursos utilizados.
///

public class ResourceTracker
{
///
/// Chave da classe que será utilizada no contexto da aplicação.
///

const string resourceKey = "__resources";

///
/// Lista com os recursos utilizados
///

private List resources;

///
/// Construtor padrão.
///

/// Contexto HTTP para salvar a lista.
public ResourceTracker(HttpContextBase context)
{
resources = (List)context.Items[resourceKey];
if (resources == null)
{
resources = new List();
context.Items[resourceKey] = resources;
}
}

///
/// Adiciona uma referência na lista.
///

/// Chave a ser adicionada.
public void Add(string key)
{
key = key.ToLower(CultureInfo.CurrentCulture);
resources.Add(key);
}


///
/// Verifica se uma chave existe na lista atual.
///

/// Chave que será verificada.
/// Retorna true se a chave foi encontrada e false para caso não.
public bool Contains(string key)
{
key = key.ToLower(CultureInfo.CurrentCulture);
return resources.Contains(key);
}

}

Bom agora temos mais uma classe. A AjaxExtensions que também estende a classe AjaxHelper. Esta classe possui vários métodos mas o método que irei explicar é o método SetScriptCulture. Na sua View quando você chamar este método a variável "__cultureinfo" necessária para a localização do MicrosoftAjax.js é configurada, veja o código da classe:


///
/// Extensões para auxiliar o uso de bibliotecas Ajax no MVC.
///

public static class AjaxExtensions
{
///
/// Caminho da biblioteca Ajax da Microsoft.
///

private static string microsoftAjaxLibraryUrl = "/Scripts/MicrosoftAjax.js";

///
/// Caminho da biblioteca Ajax da Microsoft para MVC.
///

private static string microsoftAjaxMvcLibraryUrl = "/Scripts/MicrosoftMvcAjax.js";

///
/// Caminho da biblioteca do AjaxControlToolkit.
///

private static string ajaxControlToolkitFolderUrl = "/Scripts/AjaxControlToolkit/";

///
/// Caminho da biblioteca do jQuery.
///

private static string jQueryLibraryUrl = "/Scripts/jquery-1.3.2.min.js";

///
/// Este método pode ser utilizado para mudar o caminho padrão da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Novo caminho.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static void SetMicrosoftAjaxLibraryUrl(this AjaxHelper helper, string url)
{
microsoftAjaxLibraryUrl = url;
}

///
/// Retorna o caminho da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Caminho do script.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static string GetMicrosoftAjaxLibraryUrl(this AjaxHelper helper)
{
return microsoftAjaxLibraryUrl;
}

///
/// Este método pode ser utilizado para mudar o caminho padrão da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Novo caminho.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static void SetMicrosoftAjaxMvcLibraryUrl(this AjaxHelper helper, string url)
{
microsoftAjaxMvcLibraryUrl = url;
}

///
/// Retorna o caminho da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Caminho do script.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static string GetMicrosoftAjaxMvcLibraryUrl(this AjaxHelper helper)
{
return microsoftAjaxMvcLibraryUrl;
}

///
/// Este método pode ser utilizado para mudar o caminho padrão da biblioteca do jQuery.
///

/// Classe extendida.
/// Novo caminho.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static void SetJQueryLibraryUrl(this AjaxHelper helper, string url)
{
jQueryLibraryUrl = url;
}

///
/// Retorna o caminho da biblioteca jQuery.
///

/// Classe extendida.
/// Caminho do script.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static string GetJQueryLibraryUrl(this AjaxHelper helper)
{
return jQueryLibraryUrl;
}

///
/// Este método pode ser utilizado para mudar o caminho padrão da biblioteca AjaxControlToolkit.
///

/// Classe extendida.
/// Novo caminho.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static void SetAjaxControlToolkitFolderUrl(this AjaxHelper helper, string url)
{
ajaxControlToolkitFolderUrl = url;
}

///
/// Retorna o caminho da biblioteca AjaxControlToolkit.
///

/// Classe extendida.
/// Caminho do script.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Extension Method")]
public static string GetAjaxControlToolkitFolderUrl(this AjaxHelper helper)
{
return ajaxControlToolkitFolderUrl;
}

///
/// Inclui uma referência da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Referência a biblioteca.
public static string MicrosoftAjaxLibraryInclude(this AjaxHelper helper)
{
return SetScriptCulture(helper) + ScriptExtensions.ScriptInclude(helper, microsoftAjaxLibraryUrl);
}

///
/// Inclui uma referência da biblioteca Ajax da Microsoft.
///

/// Classe extendida.
/// Referência a biblioteca.
public static string MicrosoftAjaxMvcLibraryInclude(this AjaxHelper helper)
{
return SetScriptCulture(helper) + ScriptExtensions.ScriptInclude(helper, microsoftAjaxMvcLibraryUrl);
}

///
/// Inclui uma referência da biblioteca jQuery.
///

/// Classe extendida.
/// Referência a biblioteca.
public static string JQueryLibraryInclude(this AjaxHelper helper)
{
return ScriptExtensions.ScriptInclude(helper, jQueryLibraryUrl);
}

///
///
///

/// Classe extendida.
///
///
public static string ToolkitInclude(this AjaxHelper helper, params string[] fileName)
{
StringBuilder sb = new StringBuilder();
foreach (string item in fileName)
{
string fullUrl = ajaxControlToolkitFolderUrl + item;
sb.AppendLine(ScriptExtensions.ScriptInclude(helper, fullUrl));
}
return sb.ToString();
}

///
///
///

/// Classe extendida.
///
///
public static string DynamicToolkitCssInclude(this AjaxHelper helper, string fileName)
{
string fullUrl = ajaxControlToolkitFolderUrl + fileName;
return helper.DynamicCssInclude(fullUrl);
}

///
///
///

/// Classe extendida.
///
///
public static string DynamicCssInclude(this AjaxHelper helper, string url)
{
ResourceTracker tracker = new ResourceTracker(helper.ViewContext.HttpContext);
if (tracker.Contains(url))
return String.Empty;

StringBuilder sb = new StringBuilder();
sb.AppendLine("var link=document.createElement('link')");
sb.AppendLine("link.setAttribute('rel', 'stylesheet');");
sb.AppendLine("link.setAttribute('type', 'text/css');");
sb.AppendFormat("link.setAttribute('href', '{0}');", url);
sb.AppendLine();
sb.AppendLine("var head = document.getElementsByTagName('head')[0];");
sb.AppendLine("head.appendChild(link);");
return string.Format(CultureInfo.CurrentCulture, Constants.ScriptFormat, sb.ToString());
}

///
///
///

/// Classe extendida.
///
///
///
public static string Create(this AjaxHelper helper, string clientType, string elementId)
{
//TODO: Corrigir erro de parametro na linha abaixo.
return Create(helper, clientType, new { }, elementId);
}

///
///
///

/// Classe extendida.
///
///
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper" , Justification = "Extension Method")]
public static string Create(this AjaxHelper helper, string clientType, object props, string elementId)
{
string strProps = ObjectToString(props);
StringBuilder sb = new StringBuilder();
sb.AppendLine("Sys.Application.add_init(function(){");
sb.AppendFormat("$create({0},{1},null,null,$get('{2}'))", clientType, strProps, elementId);
sb.AppendLine("});");
return string.Format(CultureInfo.CurrentCulture, Constants.ScriptFormat, sb.ToString());
}

///
///
///

///
///
private static string ObjectToString(object thing)
{
List colProps = new List();
PropertyInfo[] props = thing.GetType().GetProperties();
foreach (var prop in props)
{
var val = prop.GetValue(thing, null);
colProps.Add(String.Format(CultureInfo.CurrentCulture, "{0}:{1}", prop.Name, prop.GetValue(thing, null)));
}
return "{" + String.Join(",", colProps.ToArray()) + "}";
}

///
/// Seta as informacoes de cultura para o script MicrosoftAjax.js.
///

/// Classe extendida.
/// Block string javascript.
public static string SetScriptCulture(this AjaxHelper helper)
{
string cultureInfoScriptFormat = string.Format(CultureInfo.CurrentCulture, Constants.ScriptFormat, "var __cultureInfo = '{0}';");

JavaScriptSerializer jss = new JavaScriptSerializer();
string cultureInfoJSON = jss.Serialize(new ClientCultureInfo(Thread.CurrentThread.CurrentUICulture));

return ScriptExtensions.ScriptInclude(helper, "__cultureInfo", string.Format(CultureInfo.CurrentCulture, cultureInfoScriptFormat, cultureInfoJSON), false);
}

///
///
///

///
internal static string GetLocalizedExtenderBaseScript()
{
return "AjaxControlToolkit.ExtenderBase.BaseScripts." + Thread.CurrentThread.CurrentUICulture.Name + ".js";
}
}


Veja que o método SetScriptCulture realiza um serealize da classe ClientCultureInfo. Veja a classe ClientCultureInfo abaixo:

///
/// Representação da cultura no cliente (Browser).
/// Utilizado pelo Microsoft Ajax Framework para manter a internacionalização configurada na aplicação.
///

[Serializable]
public class ClientCultureInfo
{
///
/// Nome da cultura.
///

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Variavel com este casing no cliente (JavaScript)")]
public string name;

///
/// Formato da data.
///

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Variavel com este casing no cliente (JavaScript)")]
public DateTimeFormatInfo dateTimeFormat;

///
/// Formato do númerico.
///

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Variavel com este casing no cliente (JavaScript)")]
public NumberFormatInfo numberFormat;

///
/// Construdor da ClientCultureInfo
///

/// Informações da cultura.
public ClientCultureInfo(CultureInfo cultureInfo)
{
this.name = cultureInfo.Name;
this.numberFormat = cultureInfo.NumberFormat;
this.dateTimeFormat = cultureInfo.DateTimeFormat;
}
}

Para usar tudo isto em suas Views do MVC basta fazer isto:




<%= Ajax.MicrosoftAjaxLibraryInclude() %>
<%= Ajax.MicrosoftAjaxMvcLibraryInclude() %>
<%= Ajax.JQueryLibraryInclude() %>
<%= Ajax.ScriptInclude(ResolveUrl("~/Scripts/fg.menu.js"))%>
<%= Ajax.SetScriptCulture() %>

Agora eu também vou colocar, de brinde, o extender para usar o CalendarExtender do AjaxControlToolkit, veja:



public static class CalendarExtensions
{
public static string Calendar(this AjaxHelper helper, string elementId)
{
StringBuilder sb = new StringBuilder();

// Add Microsoft Ajax library
sb.AppendLine(helper.MicrosoftAjaxLibraryInclude());

// Add toolkit scripts
sb.AppendLine(helper.ToolkitInclude
(
AjaxExtensions.GetLocalizedExtenderBaseScript(),
"AjaxControlToolkit.Common.Common.js",
"AjaxControlToolkit.Common.DateTime.js",
"AjaxControlToolkit.Animation.Animations.js",
"AjaxControlToolkit.PopupExtender.PopupBehavior.js",
"AjaxControlToolkit.Animation.AnimationBehavior.js",
"AjaxControlToolkit.Common.Threading.js",
"AjaxControlToolkit.Compat.Timer.Timer.js",
"AjaxControlToolkit.Calendar.CalendarBehavior.js"
));

// Add Calendar CSS file
sb.AppendLine(helper.DynamicToolkitCssInclude("AjaxControlToolkit.Calendar.Calendar.css"));

// Perform $create
sb.AppendLine(helper.Create("AjaxControlToolkit.CalendarBehavior", elementId));

return sb.ToString();
}

public static string Calendar(this AjaxHelper helper, string elementId, string popupElementId)
{
StringBuilder sb = new StringBuilder();

// Add Microsoft Ajax library
sb.AppendLine(helper.MicrosoftAjaxLibraryInclude());

// Add toolkit scripts
sb.AppendLine(helper.ToolkitInclude
(
AjaxExtensions.GetLocalizedExtenderBaseScript(),
"AjaxControlToolkit.Common.Common.js",
"AjaxControlToolkit.Common.DateTime.js",
"AjaxControlToolkit.Animation.Animations.js",
"AjaxControlToolkit.PopupExtender.PopupBehavior.js",
"AjaxControlToolkit.Animation.AnimationBehavior.js",
"AjaxControlToolkit.Common.Threading.js",
"AjaxControlToolkit.Compat.Timer.Timer.js",
"AjaxControlToolkit.Calendar.CalendarBehavior.js"
));

var props = new
{
button = string.Format(CultureInfo.CurrentCulture, "$get(\"{0}\")", popupElementId)
};
// Add Calendar CSS file
sb.AppendLine(helper.DynamicToolkitCssInclude("AjaxControlToolkit.Calendar.Calendar.css"));

// Perform $create
sb.AppendLine(helper.Create("AjaxControlToolkit.CalendarBehavior", props, elementId));

return sb.ToString();
}
}

Pronto, agora no seu código basta colocar o seguinte para o calendário funcionar:


<input type="text" name="DataX" id="DataX"><img src="calendario.gif" id="DataXPopup" />
<%= Ajax.Calendar("DataX", "DataXPopup") %>

Bom é isto ai.
Este artigo e código foi baseado no artigo "Create a Popup Calendar Helper" do Stephen Walther.

Nenhum comentário:

Postar um comentário