Criação de uma interface para selecionar uma conta de usuário dentre muitas (C#)
por Scott Mitchell
Neste tutorial, criaremos uma interface do usuário com uma grade paginável e filtrada. Em particular, nossa interface do usuário consistirá em uma série de LinkButtons para filtrar os resultados com base na letra inicial do nome de usuário e um controle GridView para mostrar os usuários correspondentes. Começaremos listando todas as contas de usuário em um GridView. Em seguida, na Etapa 3, adicionaremos o filtro LinkButtons. A etapa 4 analisa a paginação dos resultados filtrados. A interface construída nas Etapas 2 a 4 será usada nos tutoriais subsequentes para executar tarefas administrativas para uma conta de usuário específica.
Introdução
No tutorial Atribuindo funções a usuários, criamos uma interface rudimentar para um administrador selecionar um usuário e gerenciar suas funções. Especificamente, a interface apresentou ao administrador uma lista suspensa de todos os usuários. Essa interface é adequada quando há apenas uma dúzia de contas de usuário, mas é desordada para sites com centenas ou milhares de contas. Uma grade paginada e filtreável é uma interface do usuário mais adequada para sites com bases de usuário grandes.
Neste tutorial, criaremos essa interface do usuário. Em particular, nossa interface do usuário consistirá em uma série de LinkButtons para filtrar os resultados com base na letra inicial do nome de usuário e um controle GridView para mostrar os usuários correspondentes. Começaremos listando todas as contas de usuário em um GridView. Em seguida, na Etapa 3, adicionaremos o filtro LinkButtons. A etapa 4 analisa a paginação dos resultados filtrados. A interface construída nas Etapas 2 a 4 será usada nos tutoriais subsequentes para executar tarefas administrativas para uma conta de usuário específica.
Vamos começar!
Etapa 1: adicionando novas páginas de ASP.NET
Neste tutorial e nos próximos dois, examinaremos várias funções e funcionalidades relacionadas à administração. Precisaremos de uma série de páginas ASP.NET para implementar os tópicos examinados ao longo desses tutoriais. Vamos criar essas páginas e atualizar o mapa do site.
Comece criando uma nova pasta no projeto chamado Administration
. Em seguida, adicione duas novas páginas ASP.NET à pasta, vinculando cada página à Site.master
página master. Nomeie as páginas:
ManageUsers.aspx
UserInformation.aspx
Adicione também duas páginas ao diretório raiz do site: ChangePassword.aspx
e RecoverPassword.aspx
.
Essas quatro páginas devem, neste ponto, ter dois controles de conteúdo, um para cada um dos ContentPlaceHolders da página master: MainContent
e LoginContent
.
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="LoginContent" Runat="Server">
</asp:Content>
Queremos mostrar a marcação padrão da página master para o LoginContent
ContentPlaceHolder para essas páginas. Portanto, remova a marcação declarativa para o Content2
controle Conteúdo. Depois de fazer isso, a marcação das páginas deve conter apenas um controle Conteúdo.
As páginas ASP.NET na Administration
pasta destinam-se exclusivamente a usuários administrativos. Adicionamos uma função Administradores ao sistema no tutorial Criando e Gerenciando Funções; restringimos o acesso a essas duas páginas a essa função. Para fazer isso, adicione um Web.config
arquivo à Administration
pasta e configure seu <authorization>
elemento para admitir usuários na função Administradores e negar todos os outros.
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<allow roles="Administrators" />
<deny users="*"/>
</authorization>
</system.web>
</configuration>
Neste ponto, a Gerenciador de Soluções do projeto deve ser semelhante à captura de tela mostrada na Figura 1.
Figura 1: Quatro novas páginas e um Web.config
arquivo foram adicionados ao site (clique para exibir a imagem em tamanho real)
Por fim, atualize o mapa do site (Web.sitemap
) para incluir uma entrada na ManageUsers.aspx
página. Adicione o XML a seguir após o <siteMapNode>
que adicionamos para os tutoriais de Funções.
<siteMapNode title="User Administration" url="~/Administration/ManageUsers.aspx"/>
Com o mapa do site atualizado, visite o site por meio de um navegador. Como mostra a Figura 2, a navegação à esquerda agora inclui itens para os tutoriais de Administração.
Figura 2: o mapa do site inclui um nó intitulado Administração de usuário (clique para exibir a imagem em tamanho real)
Etapa 2: Listando todas as contas de usuário em um GridView
Nossa meta final para este tutorial é criar uma grade paginada e filtrável por meio da qual um administrador possa selecionar uma conta de usuário para gerenciar. Vamos começar listando todos os usuários em um GridView. Depois que isso for concluído, adicionaremos as interfaces e a funcionalidade de filtragem e paginação.
Abra a ManageUsers.aspx
página na Administration
pasta e adicione um GridView, definindo-o UserAccounts
ID
como . Em um momento, escreveremos código para associar o conjunto de contas de usuário ao GridView usando o Membership
método da GetAllUsers
classe. Conforme discutido em tutoriais anteriores, o método GetAllUsers retorna um MembershipUserCollection
objeto , que é uma coleção de MembershipUser
objetos . Cada MembershipUser
uma na coleção inclui propriedades como UserName
, Email
, IsApproved
e assim por diante.
Para exibir as informações de conta de usuário desejadas no GridView, defina a propriedade do AutoGenerateColumns
GridView como False e adicione BoundFields para as UserName
propriedades , Email
e e Comment
CheckBoxFields para as IsApproved
propriedades , IsLockedOut
e IsOnline
. Essa configuração pode ser aplicada por meio da marcação declarativa do controle ou por meio da caixa de diálogo Campos. A Figura 3 mostra uma captura de tela da caixa de diálogo Campos depois que a caixa de seleção Gerar campos automaticamente foi desmarcada e BoundFields e CheckBoxFields foram adicionados e configurados.
Figura 3: Adicionar três BoundFields e três CheckBoxFields ao GridView (clique para exibir a imagem em tamanho real)
Depois de configurar o GridView, verifique se a marcação declarativa é semelhante à seguinte:
<asp:GridView ID="UserAccounts" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="UserName" HeaderText="UserName"/>
<asp:BoundField DataField="Email" HeaderText="Email" />
<asp:CheckBoxField DataField="IsApproved" HeaderText="Approved?"/>
<asp:CheckBoxField DataField="IsLockedOut" HeaderText="Locked Out?" />
<asp:CheckBoxField DataField="IsOnline" HeaderText="Online?"/>
<asp:BoundField DataField="Comment" HeaderText="Comment"/>
</Columns>
</asp:GridView>
Em seguida, precisamos escrever um código que associe as contas de usuário ao GridView. Crie um método chamado BindUserAccounts
para executar essa tarefa e, em seguida, chame-a do Page_Load
manipulador de eventos na primeira visita à página.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
BindUserAccounts();
}
private void BindUserAccounts()
{
UserAccounts.DataSource = Membership.GetAllUsers();
UserAccounts.DataBind();
}
Reserve um momento para testar a página por meio de um navegador. Como mostra a Figura 4, o UserAccounts
GridView lista o nome de usuário, o endereço de email e outras informações de conta pertinentes para todos os usuários no sistema.
Figura 4: As contas de usuário são listadas no GridView (clique para exibir a imagem em tamanho real)
Etapa 3: Filtrando os resultados pela primeira letra do nome de usuário
Atualmente, o UserAccounts
GridView mostra todas as contas de usuário. Para sites com centenas ou milhares de contas de usuário, é imperativo que o usuário possa parar rapidamente as contas exibidas. Isso pode ser feito adicionando a filtragem de LinkButtons à página. Vamos adicionar 27 LinkButtons à página: um intitulado Todos junto com um LinkButton para cada letra do alfabeto. Se um visitante clicar em Todos os LinkButton, o GridView mostrará todos os usuários. Se eles clicarem em uma letra específica, somente os usuários cujo nome de usuário começa com a letra selecionada serão exibidos.
Nossa primeira tarefa é adicionar os 27 controles LinkButton. Uma opção seria criar os 27 LinkButtons declarativamente, um de cada vez. Uma abordagem mais flexível é usar um controle Repeater com um ItemTemplate
que renderiza um LinkButton e associa as opções de filtragem ao Repeater como uma string
matriz.
Comece adicionando um controle Repeater à página acima do UserAccounts
GridView. Defina a propriedade do ID
Repetidor como FilteringUI
. Configure os modelos do Repeater para que ele ItemTemplate
renderize um LinkButton cujas Text
propriedades e CommandName
estão associadas ao elemento de matriz atual. Como vimos no tutorial Atribuindo funções a usuários, isso pode ser feito usando a Container.DataItem
sintaxe de associação de dados. Use o Repeater's SeparatorTemplate
para exibir uma linha vertical entre cada link.
<asp:Repeater ID="FilteringUI" runat="server">
<ItemTemplate>
<asp:LinkButton runat="server" ID="lnkFilter"
Text='<%# Container.DataItem %>'
CommandName='<%# Container.DataItem %>'></asp:LinkButton>
</ItemTemplate>
<SeparatorTemplate>|</SeparatorTemplate>
</asp:Repeater>
Para preencher esse Repetidor com as opções de filtragem desejadas, crie um método chamado BindFilteringUI
. Certifique-se de chamar esse método do Page_Load
manipulador de eventos no carregamento da primeira página.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
BindUserAccounts();
BindFilteringUI();
}
}
private void BindFilteringUI()
{
string[] filterOptions = { "All", "A", "B", "C","D", "E", "F", "G", "H", "I","J", "K", "L", "M", "N", "O","P", "Q", "R", "S", "T", "U","V", "W", "X", "Y", "Z" };
FilteringUI.DataSource = filterOptions;
FilteringUI.DataBind();
}
Esse método especifica as opções de filtragem como elementos na string
matriz filterOptions
. Para cada elemento na matriz, o Repeater renderizará um LinkButton com suas Text
propriedades e CommandName
atribuídas ao valor do elemento de matriz.
A Figura 5 mostra a ManageUsers.aspx
página quando exibida por meio de um navegador.
Figura 5: O Repetidor Listas 27 LinkButtons de Filtragem (Clique para exibir a imagem em tamanho real)
Observação
Os nomes de usuário podem começar com qualquer caractere, incluindo números e pontuação. Para exibir essas contas, o administrador precisará usar a opção Todos os LinkButton. Como alternativa, você pode adicionar um LinkButton para retornar todas as contas de usuário que começam com um número. Eu deixo isso como um exercício para o leitor.
Clicar em qualquer um dos LinkButtons de filtragem causa um postback e gera o evento do ItemCommand
Repetidor, mas não há nenhuma alteração na grade porque ainda não escrevemos nenhum código para filtrar os resultados. A Membership
classe inclui um FindUsersByName
método que retorna as contas de usuário cujo nome de usuário corresponde a um padrão de pesquisa especificado. Podemos usar esse método para recuperar apenas as contas de usuário cujos nomes de usuário começam com a letra especificada pelo CommandName
do LinkButton filtrado que foi clicado.
Comece atualizando a ManageUser.aspx
classe code-behind da página para que ela inclua uma propriedade chamada UsernameToMatch
. Essa propriedade persiste a cadeia de caracteres de filtro de nome de usuário entre postbacks:
private string UsernameToMatch
{
get
{
object o = ViewState["UsernameToMatch"];
if (o == null)
return string.Empty;
else
return (string)o;
}
set
{
ViewState["UsernameToMatch"] = value;
}
}
A UsernameToMatch
propriedade armazena seu valor atribuído à ViewState
coleção usando a chave UsernameToMatch. Quando o valor dessa propriedade é lido, ele verifica se existe um valor na ViewState
coleção; caso contrário, retorna o valor padrão, uma cadeia de caracteres vazia. A UsernameToMatch
propriedade exibe um padrão comum, ou seja, persistir um valor para exibir o estado para que as alterações na propriedade sejam persistidas entre postbacks. Para obter mais informações sobre esse padrão, leia Noções básicas sobre ASP.NET estado de exibição.
Em seguida, atualize o BindUserAccounts
método para que, em vez de chamar Membership.GetAllUsers
, ele chame Membership.FindUsersByName
, passando o valor da UsernameToMatch
propriedade acrescentada com o caractere curinga SQL, %.
private void BindUserAccounts()
{
UserAccounts.DataSource = Membership.FindUsersByName(this.UsernameToMatch + "%");
UserAccounts.DataBind();
}
Para exibir apenas os usuários cujo nome de usuário começa com a letra A, defina a UsernameToMatch
propriedade como A e chame BindUserAccounts
. Isso resultaria em uma chamada para Membership.FindUsersByName("A%")
, que retornará todos os usuários cujo nome de usuário começa com A. Da mesma forma, para retornar todos os usuários, atribua uma cadeia de caracteres vazia à propriedade para UsernameToMatch
que o BindUserAccounts
método invoque Membership.FindUsersByName("%")
, retornando assim todas as contas de usuário.
Crie um manipulador de eventos para o evento repeater ItemCommand
. Esse evento é gerado sempre que um dos LinkButtons de filtro é clicado; ele é passado o valor do CommandName
LinkButton clicado por meio do RepeaterCommandEventArgs
objeto . Precisamos atribuir o valor apropriado à UsernameToMatch
propriedade e, em seguida, chamar o BindUserAccounts
método . Se for CommandName
All, atribua uma cadeia de caracteres vazia para UsernameToMatch
que todas as contas de usuário sejam exibidas. Caso contrário, atribua o CommandName
valor a UsernameToMatch
.
protected void FilteringUI_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "All")
this.UsernameToMatch = string.Empty;
else
this.UsernameToMatch e.CommandName;
BindUserAccounts();
}
Com esse código em vigor, teste a funcionalidade de filtragem. Quando a página é visitada pela primeira vez, todas as contas de usuário são exibidas (consulte a Figura 5). Clicar em Um LinkButton causa um postback e filtra os resultados, exibindo apenas as contas de usuário que começam com A.
Figura 6: Usar o LinkButtons de Filtragem para exibir os usuários cujo nome de usuário começa com uma determinada letra (clique para exibir a imagem em tamanho real)
Etapa 4: Atualizando o GridView para usar paginação
O GridView mostrado nos Números 5 e 6 lista todos os registros retornados do FindUsersByName
método . Se houver centenas ou milhares de contas de usuário, isso poderá levar à sobrecarga de informações ao exibir todas as contas (como é o caso ao clicar em Todos os LinkButton ou ao visitar inicialmente a página). Para ajudar a apresentar as contas de usuário em partes mais gerenciáveis, vamos configurar o GridView para exibir 10 contas de usuário por vez.
O controle GridView oferece dois tipos de paginação:
- Paginação padrão – fácil de implementar, mas ineficiente. Em poucas palavras, com a paginação padrão, o GridView espera todos os registros de sua fonte de dados. Em seguida, ele exibe apenas a página de registros apropriada.
- Paginação personalizada – requer mais trabalho para implementar, mas é mais eficiente do que a paginação padrão porque, com a paginação personalizada, a fonte de dados retorna apenas o conjunto preciso de registros a ser exibido.
A diferença de desempenho entre a paginação padrão e personalizada pode ser bastante substancial ao paginar milhares de registros. Como estamos criando essa interface supondo que possa haver centenas ou milhares de contas de usuário, vamos usar a paginação personalizada.
Observação
Para obter uma discussão mais detalhada sobre as diferenças entre a paginação padrão e personalizada, bem como os desafios envolvidos na implementação da paginação personalizada, consulte Paginação eficiente em grandes quantidades de dados.
Para implementar a paginação personalizada, primeiro precisamos de algum mecanismo para recuperar o subconjunto preciso de registros que estão sendo exibidos pelo GridView. A boa notícia é que o Membership
método da FindUsersByName
classe tem uma sobrecarga que nos permite especificar o índice da página e o tamanho da página e retorna apenas as contas de usuário que se enquadram nesse intervalo de registros.
Em particular, essa sobrecarga tem a seguinte assinatura: FindUsersByName(usernameToMatch, pageIndex, pageSize, totalRecords)
.
O parâmetro pageIndex especifica a página de contas de usuário a ser retornada; pageSize indica quantos registros exibir por página. O parâmetro totalRecords é um out
parâmetro que retorna o número total de contas de usuário no repositório de usuários.
Observação
Os dados retornados por FindUsersByName
são classificados por nome de usuário; os critérios de classificação não podem ser personalizados.
O GridView pode ser configurado para utilizar a paginação personalizada, mas somente quando associado a um controle ObjectDataSource. Para que o controle ObjectDataSource implemente a paginação personalizada, ele requer dois métodos: um que é passado um índice de linha inicial e o número máximo de registros a serem exibidos e retorna o subconjunto preciso de registros que se enquadram nesse intervalo; e um método que retorna o número total de registros que estão sendo paginado. A FindUsersByName
sobrecarga aceita um índice de página e um tamanho de página e retorna o número total de registros por meio de um out
parâmetro. Portanto, há uma incompatibilidade de interface aqui.
Uma opção seria criar uma classe proxy que exponha a interface esperada por ObjectDataSource e, em seguida, chama internamente o FindUsersByName
método . Outra opção - e a que usaremos para este artigo - é criar nossa própria interface de paginação e usá-la em vez da interface de paginação interna do GridView.
Criando uma interface de paginação first, previous, next
Vamos criar uma interface de paginação com First, Previous, Next e Last LinkButtons. O Primeiro LinkButton, quando clicado, levará o usuário para a primeira página de dados, enquanto Previous o retornará à página anterior. Da mesma forma, Avançar e Último moverão o usuário para a próxima e a última página, respectivamente. Adicione os quatro controles LinkButton abaixo do UserAccounts
GridView.
<p>
<asp:LinkButton ID="lnkFirst" runat="server"> First</asp:LinkButton> |
<asp:LinkButton ID="lnkPrev" runat="server"> Prev</asp:LinkButton>|
<asp:LinkButton ID="lnkNext" runat="server">Next </asp:LinkButton>|
<asp:LinkButton ID="lnkLast" runat="server">Last </asp:LinkButton>
</p>
Em seguida, crie um manipulador de eventos para cada um dos eventos do Click
LinkButton.
A Figura 7 mostra os quatro LinkButtons quando exibidos por meio do modo de exibição Design do Desenvolvedor da Web Visual.
Figura 7: Adicionar Primeiro, Anterior, Próximo e Último LinkButtons Abaixo do GridView (Clique para exibir a imagem em tamanho real)
Mantendo o controle do índice de página atual
Quando um usuário visita a ManageUsers.aspx
página pela primeira vez ou clica em um dos botões de filtragem, queremos exibir a primeira página de dados no GridView. No entanto, quando o usuário clica em um dos LinkButtons de navegação, precisamos atualizar o índice da página. Para manter o índice da página e o número de registros a serem exibidos por página, adicione as duas propriedades a seguir à classe code-behind da página:
private int PageIndex
{
get
{
object o = ViewState["PageIndex"];
if (o == null)
return 0;
else
return (int)o;
}
set
{
ViewState["PageIndex"] = value;
}
}
private int PageSize
{
get
{
return 10;
}
}
Assim como a UsernameToMatch
propriedade , a PageIndex
propriedade persiste seu valor para exibir o estado. A propriedade somente PageSize
leitura retorna um valor embutido em código, 10. Convido o leitor interessado a atualizar essa propriedade para usar o mesmo padrão PageIndex
que e, em seguida, aumentar a ManageUsers.aspx
página de modo que a pessoa que visita a página possa especificar quantas contas de usuário exibir por página.
Recuperando apenas os registros da página atual, atualizando o índice da página e habilitando e desabilitando o LinkButtons da interface de paginação
Com a interface de paginação em vigor e as PageIndex
propriedades e PageSize
adicionadas, estamos prontos para atualizar o BindUserAccounts
método para que ele use a sobrecarga apropriada FindUsersByName
. Além disso, precisamos que esse método habilite ou desabilite a interface de paginação, dependendo de qual página está sendo exibida. Ao exibir a primeira página de dados, os links Primeiro e Anterior devem ser desabilitados; Next e Last devem ser desabilitados ao exibir a última página.
Atualize o método BindUserAccounts
pelo seguinte código:
private void BindUserAccounts()
{
int totalRecords;
UserAccounts.DataSource = Membership.FindUsersByName(this.UsernameToMatch + "%",this.PageIndex, this.PageSize, out totalRecords);
UserAccounts.DataBind();
// Enable/disable the paging interface
bool visitingFirstPage = (this.PageIndex == 0);
lnkFirst.Enabled = !visitingFirstPage;
lnkPrev.Enabled = !visitingFirstPage;
int lastPageIndex = (totalRecords - 1) / this.PageSize;
bool visitingLastPage = (this.PageIndex >= lastPageIndex);
lnkNext.Enabled = !visitingLastPage;
lnkLast.Enabled = !visitingLastPage;
}
Observe que o número total de registros que estão sendo paginado é determinado pelo último parâmetro do FindUsersByName
método. Esse é um out
parâmetro, portanto, precisamos primeiro declarar uma variável para manter esse valor (totalRecords
) e, em seguida, prefixá-lo com o out
palavra-chave.
Depois que a página especificada de contas de usuário é retornada, os quatro LinkButtons são habilitados ou desabilitados, dependendo se a primeira ou a última página de dados está sendo exibida.
A última etapa é escrever o código para os quatro manipuladores de eventos do Click
LinkButtons. Esses manipuladores de eventos precisam atualizar a PageIndex
propriedade e, em seguida, reassociar os dados ao GridView por meio de uma chamada para BindUserAccounts
. Os manipuladores de eventos First, Previous e Next são muito simples. O Click
manipulador de eventos para o Last LinkButton, no entanto, é um pouco mais complexo porque precisamos determinar quantos registros estão sendo exibidos para determinar o último índice de página.
protected void lnkFirst_Click(object sender, EventArgs e)
{
this.PageIndex = 0;
BindUserAccounts();
}
protected void lnkPrev_Click(object sender, EventArgs e)
{
this.PageIndex -= 1;
BindUserAccounts();
}
protected void lnkNext_Click(object sender, EventArgs e)
{
this.PageIndex += 1;
BindUserAccounts();
}
protected void lnkLast_Click(object sender, EventArgs e)
{
// Determine the total number of records
int totalRecords;
Membership.FindUsersByName(this.UsernameToMatch + "%", this.PageIndex,this.PageSize, out totalRecords);
// Navigate to the last page index
this.PageIndex = (totalRecords - 1) / this.PageSize;
BindUserAccounts();
}
Os números 8 e 9 mostram a interface de paginação personalizada em ação. A Figura 8 mostra a ManageUsers.aspx
página ao exibir a primeira página de dados para todas as contas de usuário. Observe que apenas 10 das 13 contas são exibidas. Clicar no link Avançar ou Último causa um postback, atualiza o PageIndex
para 1 e associa a segunda página de contas de usuário à grade (consulte a Figura 9).
Figura 8: As primeiras 10 contas de usuário são exibidas (clique para exibir a imagem em tamanho real)
Figura 9: Clicar no próximo link exibe a segunda página de contas de usuário (clique para exibir a imagem em tamanho real)
Resumo
Os administradores geralmente precisam selecionar um usuário na lista de contas. Nos tutoriais anteriores, examinamos o uso de uma lista suspensa preenchida com os usuários, mas essa abordagem não é bem dimensionada. Neste tutorial, exploramos uma alternativa melhor: uma interface filtráveis cujos resultados são exibidos em um GridView paginado. Com essa interface do usuário, os administradores podem localizar e selecionar de forma rápida e eficiente uma conta de usuário entre milhares.
Programação feliz!
Leitura Adicional
Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:
- Paginação personalizada em ASP.NET com SQL Server 2005
- Paginação eficiente por grandes quantidades de dados
Sobre o autor
Scott Mitchell, autor de vários livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Scott pode ser contatado em mitchell@4guysfromrolla.com ou através de seu blog em http://ScottOnWriting.NET.
Agradecimentos Especiais
Esta série de tutoriais foi revisada por muitos revisores úteis. O revisor principal deste tutorial foi Alicja Maziarz. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com