Setembro 2005 - Posts

ObjectDataSource, GridView e paginação
30-9-2005 23:07

O controlo GridView consegue efectuar a paginação desde que o controlo ObjectDataSource tenha sido previamente configurado. Acontece que a partir da CTP de Agosto, isto simplesmente deixou de funcionar. Já apresentei o bug no feedback center, mas parece que já não há tempo para resolver este problema! Ora bem, eu acho que este problema tem de ser resolvido! Se concordam comigo, então votem no bug aqui. Obrigado.

por Luis Abreu | 2 comment(s)
Filed under: ,
Como funcionam os cross-page postings - internals
30-9-2005 22:19

Ao longo dos próximos tempos quero ver se apresento um conjunto de posts sobre o funcionamento interno de alguns dos componentes da plataforma ASP.NET (claro, se o tempo o permitir  ). Hoje consegui arranajr algum tempo de forma a seguir todos os passos associados ao cross-posting. O resultado é este post algo longo…

A nova versão da plataforma permite-nos efectuar a navegação de uma página para outra através da utilização da propriedade PostBackUrl exposta pela classe Button. O que não é do conhecimento geral é que, se quisermos, podemos configurar qualquer controlo para efectuar este tipo de operação. Vamos começar por examinar o que acontece quando adicionamos um botão a uma página e atribuimos uma valor válido à propriedade PostbackUrl:

<html>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button runat="server" ID="p" Text="postback" PostBackUrl="~/page2.aspx" />
    </div>
    </form>
</body>
</html>

Ao visualizarmos esta página no browser, obtemos o seguinte HTML (apresento apenas a parte interessante):

<input type="submit" name="p" value="postback" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(&quot;p&quot;, &quot;&quot;, false, &quot;&quot;, &quot;page2.aspx&quot;, false, false))" id="p" />

<div> <input type="hidden" name="__PREVIOUSPAGE" id="__PREVIOUSPAGE" value="28syFYJQ6nikNW14B7byKC1UfoJMDOegDH-zYvMP8s01" />
 <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgKttZiiBwLQ76ruDOuou7R1ovb+R3uMdD4Ug/lKi/eD" />
</div></form>

Como veremos, o campo __PREVIOUSPAGE é o responsável pelo correcto funcionamento da página! O método WebForm_DoPostBackWithOptions é injectado pela página e é responsável por 1.) efectuar a validação da página (quando tal é necessário), 2.) Modificar a propriedade action do formulário (que indica o destino da submissão do formulário), 3.) obter o elemento que possui o foco (quando for necessário) e 4.) efectuar a submissão da página. Este último passo é feito à custa do método __doPostBack e limita-se a preencher correctamente os campos __EVENTTARGET e __EVENTARGUMENT que indicam o controlo responsável pela operação de submissão e respectivo valor predefinido. Portanto, o processo de submissão para outra página a partir do lado cliente resume-se a configurar a propriedade action do formulário e a invocar o método submit desse formulário. Se tentarmos adicionar código a uma página que efectua as operações descritas iremos deparar-nos com uma excepção. Vejamos o seguinte código:

<html>
<body>
    <script type="text/javascript">
        function ChangePage()
        {
            document.forms[0].action = "page2.aspx";
            document.forms[0].submit();
        }
    </script>
    <form id="form1" runat="server">
    <div>
        <asp:Button runat="server" ID="p" Text="postback" OnClientClick="ChangePage()" />
    </div>
    </form>
</body>
</html>

O resultado é o seguinte:

Image00046

 O problema é o mesmo da primeira versão: a página destino tenta recuperar o viewstate mantido no controlo HTML __VIEWSTATE como se fosse seu (neste caso, o VIEWSTATE aí contido é da página 1!). Durante a primeira versão, a solução passava pela modificação do nome desse campo antes de efectuarmos o postback de forma a não obtermos este erro. Contudo, se analisarmos o código cliente usado durante a submissão da primeira página, reparamos que o campo __VIEWSTATE não é alterado (basta consultar o método WebForm_DoPostBackWithOptions situado no ficheiro WebForms.js que se encontra embebido na assembly System.Web.dll). Logo, podemos concluir que falta-nos algo para obtermos o mesmo comportamento da página inicial…

Se usarmos  o Reflector para analisar o código associado ao ciclo de vida de uma página, reparamos no seguinte (método ProcessRequest da classe Page):

VirtualPath path1 = null;
if (this._requestValueCollection["__PREVIOUSPAGE"] != null)
{
  try{
    path1 = VirtualPath.CreateNonRelativeAllowNull(Page.DecryptString(this._requestValueCollection["__PREVIOUSPAGE"]));
  }
  catch (CryptographicException)  {
   this._pageFlags[8] = true;
  }
  if ((path1 != null) && (path1 != this.Request.CurrentExecutionFilePathObject))  {
   
this._pageFlags[8] = true;
    this._previousPagePath = path1;
  }
}

Esta verificação é feita pela página no inicio do ciclo de vida. Sempre que é detectado o campo __PREVIOUSPAGE, a página instancia o caminho até essa página (note-se que este campo contém o caminho virtual até uma página – normalmente, é indicada a página que iniciou o pedido de cross-posting). Existe um pormenor extremamente importante para o desfecho da operação: sempre que existe um campo __PREVIOUSPAGE, a flag _pageFlags[8] é colocado a true. O carregamento do view state é feito sempre da mesma forma, ou seja, sempre que existe um postback, a página utiliza o mesmo algoritmo para recuperar o seu estado interno. O código do método LoadPageStateFromPersistenceMedium explica porque é que neste caso não obtemos a excepção anterior:

[EditorBrowsable(EditorBrowsableState.Advanced)]
protected internal virtual object LoadPageStateFromPersistenceMedium()
{
      PageStatePersister persister1 = this.PageStatePersister;
      try
      {
            persister1.Load();
      }
      catch (HttpException exception1)
      {
           
if (this._pageFlags[8])
            {
                  return null;
            }
            exception1.WebEventCode = 0xbba;
            throw;
      }
      return new Pair(persister1.ControlState, persister1.ViewState);
}

Ou seja, apesar de obtermos à mesma a excepção, neste caso o método limita-se apenas a enviar null já que foi encontrado o campo __PREVIOUSPAGE no inicio do ciclo de vida. Ora bem, com estes dados podemos configurar qualquer controlo para efectuar uma operação de cross-posting! Em vez de adicionarmos o campo escondido __PREVIOUSPAGE com o caminho virtual encriptado até à pagina inicial, vamos recorrer ao método GetPostBackEventReference da classe ClientScriptManager já que esta classe encarrega-se de configurar a página para efectuar todas essas operações. Para ilustrarmos estes principios, vamos construir uma página que efectua a navegação para uma página final sempre que o utilizador selecciona um radio button:

<%@ Page Language="C#" %>
<script runat="server">
    public string Txt
    {
        get
        {
            return txt.Text;
        }
    }
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        option.Attributes["onclick"] = this.ClientScript.GetPostBackEventReference(new PostBackOptions(option, "", "page2.aspx", true, true, false, true, false, ""));
    }
</script>
<html>
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:TextBox runat="Server" ID="txt" />
         <asp:RadioButton runat="server" ID="option" Text="navigate" />
    </div>
    </form>
</body>
</html>

 
A classe PostBackOptions encapsula um conjunto de dados que geram o código cliente responsável pelo postback. Neste caso, o terceiro parâmetro é importante já que permite definir o destino do postback. Sempre que atribuimos um valor a esse parâmetro, o método GetPostBackEventReference atribui o valor true à propriedade ContainsCrossPagePost da página. Durante o rendering de HTML, a página consulta esta propriedade de forma a saber se deve ou não registar o campo escondido __PREVIOUSPAGE e respectivo valor (esta operação é feita pelo método interno EndFormRender da classe Page). Para verificarmos se a página anterior funciona, só temos mesmo de construir a página final (page2.aspx):

<%@ Page Language="C#" %>
<%@ PreviousPageType VirtualPath="~/page1.aspx" %>
<script runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        info.Text = this.PreviousPage.Txt;
    }  
</script>

<html>
<body>
    <form id="form1" runat="server">
    <div>
     
        <asp:Label ID="info" runat="server" Text="Label" />
    </div>
    </form>
</body>
</html>


 

por Luis Abreu | 4 comment(s)
Filed under: ,
Ajax patterns
28-9-2005 23:17

Um wiki sobre padrões associados a AJAX.

[UPDATE: cá está o link]

por Luis Abreu | with no comments
Filed under:
Cassini v2
27-9-2005 23:24

A nova versão do Cassini já está disponível. Podem obter mais informações aqui.

por Luis Abreu | with no comments
Atlas para RC
27-9-2005 19:23
Pode ser obtido aqui.
por Luis Abreu | with no comments
Filed under: ,
Introducing Microsoft Windows Workflow Foundation
23-9-2005 19:23
O David Chappell escreveu um artigo introdutório ao Windows Workflow Foundation.
por Luis Abreu | 2 comment(s)
Filed under:
Vance Morrison e multithreading
22-9-2005 22:58
Num artigo muito interessante publicado na MSDN.
por Luis Abreu | with no comments
Filed under:
101 LINQ samples
22-9-2005 22:48

Ainda não é agora que vou ter tempo de ver o LINQ, mas cá fica o link os interessados.

por Luis Abreu | with no comments
Filed under: ,
Bertrand e Atlas
21-9-2005 22:04
O Bertrand apresenta a construção de uma vista master/details com o Atlas. Ainda não o li, mas promete ser interessante.
por Luis Abreu | with no comments
Filed under: ,
Thread.Start
21-9-2005 21:31
Aqui há uns dias o Israel publicou um post sobre com o enviar parâmetros para uma thread. Apesar do Pedro ter alguma razão quando diz que a utilização da classe Thread não é recomendada, a verdade é que em certas situações a suautilização é mesmo necessária (já escrevi um pouco sobre este assunto há algum tempo atrás). O objectivo deste post é indicar que todo o trabalho apresentado pelo Israel é apenas necessário para .NET 1.X já que a nova versão da plataforma possui um overload do método Start que recebe um elemento do tipo object que nos permite passar qualquer valor para thread (note-se que neste caso temos de passar um elemento do tipo ParameterizedThreadStart ao construtor da classe Thread).
por Luis Abreu | 1 comment(s)
Filed under:
Resource Management
20-9-2005 23:18
Hoje enquanto navegava na internet, acabei por deparar-me com esta relíquia.
por Luis Abreu | with no comments
Filed under:
Versões RC da familia Express disponiveis para download
19-9-2005 21:15

Mais informações podem ser obtidas aqui.

por Luis Abreu | with no comments
Filed under:
Documentação sobre IIS7
19-9-2005 19:21
Parece que já começa a haver alguma informação sobre o IIS 7 (pode ser consultada aqui).
por Luis Abreu | with no comments
Filed under:
Visualizar pastas do GAC
19-9-2005 19:16
Parece que já descobriram a chave que controla a visualização das dlls mantidas em cache através do explorador do Windows.
por Luis Abreu | with no comments
Filed under:
Sql Service broker
17-9-2005 22:15
Hoje encontrei este site com alguma informação interessante sobre o Service Broker [via Paul Wilson]
por Luis Abreu | with no comments
Filed under:
Mais Entradas Página seguinte »