Tag Archives: ASP.NET

ASP.NET – How to call a server side method through Ajax in WebForms

[TRANSLATING – If you are reading the text below in portuguese, it’s because I’m lazy e I still not finished the translation. Keep calm, drink a coffee and come back a few minutes, or hours, or days…]

PageMethods in ASP.NET

First, a bit of history. If you want to read the code now, click >here!

Faz tempo que queria publicar este macete. Gosto de chamá-lo assim pois foge um pouco da linha de pensamento de quem programa há muito tempo usando WebForms. Chamadas AJAX tornaram-se algo tão natural no mundo web que quase não se pensa em sistema sem pelo menos uma chamada dessas. E, convenhamos, não é algo que o WebForms faz muito bem.

É claro que sempre tivemos o AJAX Control Toolkit (ACT) em mãos, mas assim como todo o “disfarce” que o WebForms faz sobre o protocolo HTTP, o ACT não faz diferente.

Mas e se for preciso fazer uma simples chamada a um método, mesmo que isso seja um retorno de um array de strings, ou um JSON, como fica? Há formas de fazê-lo se você pensar em um Handler puro (ASHX), mas você teria que re-estruturar toda sua aplicação só por conta disso. Mas calma, existe um jeito “bonito” de fazer isso. PageMethods!

PageMethods

Até agora não consegui encontrar uma explicação boa sobre o que são PageMethods. O que entendi foi, PageMethods são uma forma de expor funções server side da sua página ASP.NET WebForms para ser chamada por Javascript. Um exemplo é a melhor maneira de entender.

O que é preciso para usar PageMethods?

Primeiro, uma página ASPX. A página ou sua MasterPage deve possuir um controle ScripManager e nele, a propriedade EnablePageMethods definida para true.

<asp:ScriptManager ID="ScriptManager1"  runat="server" EnablePageMethods="true">
       <Scripts>
       </Scripts>
</asp:ScriptManager>

Se o ScriptManager já existir, basta acrescentar a propriedade, sem precisar mexer em referências que já existam.

Agora, a função que será chamada do lado server side. Como esta função trafegará por HTTP, o retorno desta precisa ser um tipo serializável, ou seja, que tenha uma representação em texto. No caso do exemplo, vamos retornar uma string simples. Crie a seguinte função no código da sua página:

[WebMethod]
// Get session state value.
public static string GetSessionValue(string key)
{
   return (string)HttpContext.Current.Session[key];
}
 
[WebMethod]
// Set session state value.
public static string SetSessionValue(string key, string value)
{
     HttpContext.Current.Session[key] = value;
     return (string)HttpContext.Current.Session[key];
}

Como é possível notar, o exemplo “brinca” com variáveis de servidor para provar que o trabalho está ocorrendo nele, e não no cliente. Os detalhes importantes são:

  • a função precisa retornar um tipo serializável
  • a função precisa ser static
  • a função precisa ser marcada com o atributo [WebMethod] do namespace System.Web.Services.

Por fim, o Javascript que vai chamar as funções. O código é auto explicativo. O importante a saber é que as funções criadas no código servidor serão expostas no Javascript através do objeto/function PageMethods. Assim, a função GetSessionValues criada no servidor será chamada no javascript com a sintaxe “PageMethods.GetSessionValues(parâmetros)”.

Além dos parâmetros da própria função, o PageMethods insere dos argumentos em cada uma delas, sendo eles duas funções que devem ser criadas na página, OnSucceeded e OnFailed. A primeira é invocada quando a chamada AJAX ocorre sem erros. A segunda serve exatamente para tratar erros vindos desta chamada. Veja mais detalhes no código:

// Initializes global variables and session state.
function pageLoad()
{
    PageMethods.SetSessionValue("SessionValue", Date(), 
        OnSucceeded, OnFailed);
}
 
// Gets the session state value.
function GetSessionValue(key) 
{
    PageMethods.GetSessionValue(key, OnSucceeded, OnFailed);
}
 
//Sets the session state value.
function SetSessionValue(key, value) 
{
    PageMethods.SetSessionValue(key, value, OnSucceeded, OnFailed);
}
 
// Callback function invoked on successful 
// completion of the page method.
function OnSucceeded(result, userContext, methodName) 
{
    if (methodName == "GetSessionValue")
    {
        alert("Current session state value: " + result;
    }
}
 
// Callback function invoked on failure 
// of the page method.
function OnFailed(error, userContext, methodName) 
{
    if(error !== null) 
    {
        alert( "An error occurred: " + error.get_message());
    }
}

O código pode ser colocado diretamente na página ou num arquivo Javascript separado.

Voilá! Temos AJAX de verdade do WebForms! Pelo menos quase isso.

Referências

http://www.asp.net/ajax/documentation/live/ViewSample.aspx?sref=Sys.Net.PageMethod/cs/PageMethod.aspx

ASP.NET – Obtendo o valor de um WebControl no OnInit do WebForms

Bom, outro dia disse que também sabia escrever sobre .NET. Como o pessoal de tecnologia é cético e apesar dos artigos já escritos aqui, vou tentar me arriscar em mais para tirar a prova.

Se tem algo que gosto no WebForms, é a versatilidade. Montagem de páginas dinâmicas (entenda montar como adicionar controles na página em tempo de execução) foi algo com que sempre me dei bem. CMSs como DotNetNuke e Umbraco são exemplos dessa “dinamicidade”. Quem vive disso sabe do que estou falando, das suas vantagens, e problemas.

Dentre eles, é que, como trabalhamos com controle dinâmicos, temos o cuidado de montar todos eles no evendo OnInit da página para manter o ViewState intacto. Até aqui, sem segredos. O problema é quando precisamos aplicar alguma regra de negócio na montagem dos controles que dependa por exemplo, de um valor num TextBox. Isso é um problema porque no OnInit, o ViewState ainda não foi aplicado.

Bom, a informação está lá no ViewState, mas como chegar até ela? Na necessidade, vasculhando o próprio ViewState e com a ajuda deste “How To” da Microsoft, cheguei num método que recebe como parâmetro o UniqueID do controle e o HttpRequest, você consegue chegar no valor antes antes do Page_Load.

public static string getControlValueByRequest(string UniqueID, HttpRequest request)
{
    System.Collections.Specialized.NameValueCollection postedValues = request.Form;
    String nextKey;
    String value = string.Empty;
 
    for (int i = 0; i < postedValues.AllKeys.Length; i++)
    {
        nextKey = postedValues.AllKeys[i];
        if (nextKey.Substring(0, 2) != "__")
        {
            if (nextKey == UniqueID)
            {
                value = postedValues[i];
            }
        }
    }
 
    return value;
}

Aos mais entendidos, sabemos que a informação pode ser recuperada pelo Request.Form, mas o vínculo do valor com o controle de origem da informação que é o complicado, e é onde o método acima faz o trabalho sujo de passar por todo o conjunto de chaves/valor guardado no Request.Form e aplicando uma “regrinha” (atenção ao if (nextKey.Substring(0, 2) != “__”)) para chegar no valor desejado.

Isto posto, mais um helper para seus projetos WebForms, pelo menos para que ainda os cria. 🙂

Bom proveito!

How To da Microsoft

http://msdn.microsoft.com/en-us/library/6c3yckfw.aspx

DotNetNuke – Importação de usuários em massa

DotNetNuke
DotNetNuke

O DotNetNuke destaca-se por ser um dos poucos gerenciadores de conteúdo online que rodam na plataforma .NET, da Microsoft. Bom, quem chegou até este post sabe muito bem do que estou falando. Para o restante, reservarei um outro post para falar de uma forma geral sobre este conhecido CMS.

Sob a pressão de uma necessidade na empresa (sempre é), que trabalha com o DotNetNuke, houve uma necessidade específica de efetuar uma grande carga de usuários para um portal específico. Para isso, seria necessário desenvolver algumas rotinas específicas, já que a inserção de um Usuário no Portal DotNetNuke segue algumas regras, a começar pela criptografia da senha. Bom, como toda boa necessidade, antes de desenvolver, vamos ver o que tem na comunidade certo? Bom, o resultado foi uma solução dada por John Mitchell’s, onde em uma simples página, ele criou uma rotina que através de informações de um XML baseado nos campos o objeto UserInfo da biblioteca do DotNetNuke, estes dados são automaticamente importandos para o banco de dados do portal onde a página é hospedada.

O XML é algo parecido com o seguinte:

<Users>
   <User>
      <Username>User1</Username>
      <Email>user1@yahoo.com</Email>
      <FirstName>User</FirstName>
      <LastName>One</LastName>
      <Password>password</Password>
      <Unit></Unit>
      <Street></Street>
      <City></City>
      <Region></Region>
      <PostalCode></PostalCode>
      <Country></Country>
      <Telephone></Telephone>
   </User>
   <User>
      <Username>User2</Username>
      <Email>user2@yahoo.com</Email>
      <FirstName>User</FirstName>
      <LastName>Two</LastName>
      <Password>password</Password>
      <Unit></Unit>
      <Street></Street>
      <City></City>
      <Region></Region>
      <PostalCode></PostalCode>
      <Country></Country>
      <Telephone></Telephone>
   </User>
</Users>

São apenas dois arquivos, o .aspx e o .vb do codebehind, que obviamente executam interpretados, mas executam a proeza com maestria, economizando algumas boas horas de trabalho. Na execução, algumas inconsistências foram detectadas, mas nada que comprometa o trabalho.

Link original:

http://blogs.snapsis.com/CommentView,guid,c9d3082b-d7b1-4dfb-8c9c-67c0e864964d.aspx

Bom proveito!

ASP.NET – Adicionando mensagens do ValidationSummary

Faz tempo que tinha essa necessidade. Já que utilizamos o ValitadionSummary como um concentrador de mensagens de erro, porque não mantê-lo, inclusive para mostrar nossas próprias mensagens, além daquelas que os controles de validação do ASP.NET WebForms nos fornece?

Haviam algumas opções a isso, como criar seus próprios validators, utilizar o CustomValidator em alguns casos ou até um BulletList apontando para o mesmo CssClass no ValitadionSummary para dar a impressão de se tratar da mesma lista. Bom, nenhuma delas era uma solução completa, já que cheiravam a POG.

Bom, uma navegada na internet é o código abaixo me é apresentado:

public class ValidationError : IValidator
{
    private ValidationError(string message)
    {
        ErrorMessage = message;
        IsValid = false;
    }
 
    public string ErrorMessage { get; set; }
 
    public bool IsValid { get; set; }
 
    public void Validate()
    {
        // no action required
    }
 
    public static void Display(string message)
    {
        Page currentPage = HttpContext.Current.Handler as Page;
        currentPage.Validators.Add(new ValidationError(message));
    }
}

Crie esta classe em um local acessível ao seu projeto Web. Agora, no momento em que desejar adicionar uma mensagem, basta usar a seguinte linha de código:

ValidationError.Display("Oops, Aconteceu algum erro.");

Heresia ou não, quase tão simples quando ativar um MessageBox, não? Quando esse código é chamado, o efeito é o mesmo do que tivesse ativado uma função de um CustomValidator (sem client side) e a mensagem aparecerá dentro do ValidationSummary. Mais explicações técincas e créditos no post do autor da proeza:

http://blogs.msdn.com/b/simonince/archive/2008/02/28/adding-messages-to-a-validation-summary.aspx

Bom proveito!