Israel Aece

Software Developer

Criando um DebuggerVisualizer

A arquitetura do debugger está dividida em duas partes:

  • O debugger side corre dentro do debugger do Visual Studio .NET, podendo ser criado e exibido uma interface para seu visualizador.
  • O debuggee side corre dentro do processo que o Visual Studio .NET está depurando.

O objeto que você está querendo visualizar (uma String ou Image, por exemplo) existe dentro do processo debuggee. Logo, o debuggee side deve mandar os dados para o debugger side que, por sua vez, exibe o objeto para que o desenvolvedor possa depurar. Essa visualização é criada por nós que, ainda nesse artigo, veremos como criá-la.

O debugger side recebe o objeto que será visualizado através de um object provider, o qual implementa uma interface chamada IVisualizerObjectProvider. O debuggee side envia o objeto através de um object source, o qual deriva de uma classe chamada VisualizerObjectSource. O object provider também devolve o objeto para o object source, permitindo assim, além de exibir o objeto, editá-lo no visualizador (se assim desejar) e devolvê-lo para a aplicação. Essa comunicação é efetuada através de um objeto Stream.

Para criarmos este visualizador, primeiramente precisamos marcar a classe como sendo uma classe de DebuggerVisualizer e, para isso, é fornecido um atributo chamado DebuggerVisualizer (usado a nível de assembly ou classe) para definí-la como um visualizador. Além disso, a classe deverá obrigatoriamente herdar de uma classe abstrata chamada DialogDebuggerVisualizer e implementar o método chamado Show para customizar a visualização.

O atributo DebuggerVisualizer, contido dentro do Namespace System.Diagnostics, fornece mais alguns parâmetros para a configuração do visualizador. O primeiro parâmetro é tipo, ou seja, a classe derivada DialogDebuggerVisualizer que é o nosso visualizador efetivamente; já o segundo especifica o tipo de objeto que fará a comunicação entre o debugger side e o debuggee side e, se não informado, um default será utilizado. O restante, chamado de "Named Parameters", você deve especificar o tipo de objeto que o visualizador irá trabalhar (uma String ou Image, por exemplo) e no outro, é o nome que aparecerá dentro do Visual Studio .NET 2005, para permitir visualização. Para ilustrar, veremos abaixo o código que cria o visualizador:

using System;
using Microsoft.VisualStudio.DebuggerVisualizers;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;

[
    assembly: DebuggerVisualizer(typeof(DebugTools.ImageVisualizer),
    typeof(VisualizerObjectSource),
    Target = typeof(System.Drawing.Image),
    Description = "Image Visualizer")
]
namespace DebugTools
{
    public class ImageVisualizer : DialogDebuggerVisualizer
    {
        protected override void Show(IDialogVisualizerService windowService, 
            IVisualizerObjectProvider objectProvider)
        {
            Image data = (Image)objectProvider.GetObject();

            using(ImageVisualizerForm f = new ImageVisualizerForm())
            {
                f.Image = data;
                windowService.ShowDialog(f);
            }
        }
    }
}

Note que foi criado um formulário chamado ImageVisualizerForm. Este formulário é encarregado de receber a imagem e exibí-la. Não há segredos nele, ou seja, apenas foi criada uma propriedade chamada Image que recebe a imagem vinda do object provider. O primeiro parâmetro do método Show, chamado windowService do tipo IDialogVisualizerService, fornece métodos para que o visualizador possa estar exibindo formulários, controles e janelas de diálogo.

Se você quiser que o seu visualizador edite o objeto e o devolva para a aplicação, terá que sobrescrever os métodos TransferData ou CreateReplacementObject da classe VisualizerObjectSource.

Deploy

Para que possamos utilizar o visualizador dentro do Visual Studio .NET 2005 teremos que compilar o projeto e, com a DLL gerada, colocá-la dentro do diretório <Diretorio VS.NET>\Common7\Packages\Debugger\Visualizers. Quando abrir o Visual Studio, já terá acesso ao visualizador, assim como é mostrado através da imagem abaixo:

Figura 1 - DebuggerVisualizer em funcionamento.

De String para Double

Em algumas situações, temos um determinado valor (moeda) em uma String e queremos traze-lo de volta para uma váriavel do tipo Double. Neste caso, muitos utilizam os métodos fornecidos pela classe String para manipular o valor e, depois que o(s) caracter(es) de moeda forem retirados, convertem o número que ficou para Double.

Mas para efetuar isso, há uma forma mais fácil, que é utilizando um dos overloads do método estático chamado Parse, qual se encontra dentro da estrutura Double. Um exemplo disso, é mostrado aqui:

using System.Globalization;
//....
double valor = 1256.44;
string valorString = valor.ToString("C2");
double novoDouble = Double.Parse(valorString, NumberStyles.Currency);

Outputcache - CacheProfile

Estou trabalhando em um projeto ASP.NET 2.0 e, como algumas páginas estavam requerendo o uso de Outputcache, para cada uma dessas páginas estava ajustando o mesmo valor de segundos no atributo Duration e também especificando algumas outras propriedades desta mesma diretiva, customizando assim, o cache da forma que necessitava.

Mas em um certo momento me perguntei: e se esses valores e condições mudarem? Sim, terei que ir a cada página e especificar tudo novamente. Foi nesse momento que recorri a documentação do .NET Framework e encontrei o elemento outputCacheProfiles que é especificado dentro do arquivo Web.Config.

Com isso, ao invés de especificar os mesmos valores em cada página ASPX, eu criei uma "profile" dentro do Web.Config e passo a utilizar este "profile" nas páginas ASPX. Um exemplo do uso é mostrado abaixo:

[ Web.Config ]
<outputCacheSettings>
  <outputCacheProfiles>
    <add name = "PaginasEmCache"
      varyByParam = "TituloID"
      enabled = "true"
      duration = "180" />
  </outputCacheProfiles>
</outputCacheSettings>

[ *.ASPX ]
<%@ OutputCache CacheProfile="PaginasEmCache" %>

Recuperando a linha do GridView

Muitas pessoas me perguntam como acessar o conteúdo de uma linha do GridView dentro do evento RowCommand (sim, elas NÃO ESTÃO fazendo o uso do DataKeys). Pois bem, nas versões 1.x do ASP.NET, o evento ItemCommand do DataGrid tinha um argumento do tipo DataGridCommandEventArgs e, dentro deste, uma propriedade chamada Item que retorna a instancia da linha corrente.

Isso não existe mais no GridView e, se ainda desejar acessar os dados da linha clicada no evento RowCommand do GridView, terá que proceder da seguinte forma: no evento RowCreated do GridView, terá que definir à propriedade CommandArgument do controle LinkButton (Button, ImageButton, etc) responsável por disparar o comando, a propriedade RowIndex que vem como parametro para este evento. Esta propriedade retorna um valor inteiro contendo o índice da linha e, como o evento RowCommand te fornece uma propriedade chamada CommandArgument, conseguirá recuperar o índice da linha e, consequentemente, acessar os valores da mesma.

Para ilustar o processo, o código (em C#) é mostrado abaixo:

private void GridView1_RowCommand(Object sender, GridViewCommandEventArgs e)
{
   if(e.CommandName=="Add")
   {
      int index = Convert.ToInt32(e.CommandArgument);
      GridViewRow row = GridView1.Rows[index];
      Response.Write(row.Cells[2].Text);
   }
}

private void GridView1_RowCreated(Object sender, GridViewRowEventArgs e)
{
   if(e.Row.RowType == DataControlRowType.DataRow)
   {
      LinkButton addButton = (LinkButton)e.Row.Cells[0].Controls[0];
      addButton.CommandArgument = e.Row.RowIndex.ToString();
   }
}

ListItem

Um dos grandes problemas (bug?) que temos nas versões 1.x do ASP.NET é que os atributos do objeto ListItem (usado para preencher controles como DropDownLists, ListBoxes, etc.) não são renderizados, logo, se quisésemos definir uma cor de background em um item do DropDownList, não era possível, a menos que fizessemos algo como o Scott Mitchell mostra neste artigo.

A boa notícia é que isso foi resolvido na versão 2.0 do ASP.NET e agora podemos fazer o que já cansamos de tentar nas versões anteriores:

foreach (ListItem item in this.DropDownList1.Items)
{
    item.Attributes.Add("style", "background-color: Red");
}

Atributo debug

O ScottG fala neste post sobre o atributo debug do elemento compilation do arquivo Web.Config e me fez lembrar o meu primeiro projeto em ASP.NET. A performance estava muito baixa e depois de uma checagem antes de mandar ao servidor de produção, alterei o atributo, definindo-o para false. A performance aumenta bastante, já que muitas coisas que devem ser usadas somente em tempo de desenvolvimento, deixam de ser utilizadas. Só vale lembrar que isso não é uma característica exclusiva do ASP.NET 2.0.

Uma das outras técnicas interessantes, esta somente para ASP.NET 2.0, é definir no arquivo machine.config do servidor de aplicações, o atributo retail do elemento deployment para true, para evitar que, pessoas despercebidas façam o deployment de aplicações sem mudar/definir o atributo debug para false do arquivo Web.Config da respectiva aplicação.

Imagem da Web em um PictureBox

Um amigo me perguntou algo interessante. Justamente por isso, resolvi postar aqui para ajudar outras pessoas: como recuperar em um controle PictureBox do Windows Forms a imagem que está em um local da Web acessível somente via HTTP. Pois bem, o .NET Framework fornece uma classe chamada WebClient que fornece algumas métodos para trabalharmos com envio e recebimento de dados através de uma URI. Para atender a necessidade dele, basta fazermos:

        Imports System.IO
        Imports System.Net

        '.....

        Dim ms As MemoryStream
        Try
            Dim url As String = "http://www.projetando.net/Images/Logo.gif"
            Dim webClient As New WebClient
            Dim buffer As Byte() = webClient.DownloadData(url)
            ms = New MemoryStream(buffer)
            PictureBox1.Image = Image.FromStream(ms)
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        Finally
            If Not IsNothing(ms) Then
                ms.Close()
            End If
        End Try

Através do método DownloadData recuperarmos a imagem e atribuímos a um buffer. Este, por sua vez, carregamos em um MemoryStream e passamos para o método compartilhado/estático da classe Image que retorna a imagem já para o controle PictureBox.