Compartilhar via


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.

Quatro novas páginas e um arquivo de Web.config foram adicionados ao site

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.

O mapa do site inclui um nó intitulado Administração de usuário

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 UserAccountsID 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, IsApprovede 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 UserNamepropriedades , Emaile e Comment CheckBoxFields para as IsApprovedpropriedades , IsLockedOute 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.

Adicionar três BoundFields e três CheckBoxFields ao GridView

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.

As contas de usuário são listadas no GridView

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.

The Repeater Listas 27 Filtering LinkButtons

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.

Use o LinkButtons de Filtragem para exibir os usuários cujo nome de usuário começa com uma determinada letra

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.

Adicionar Primeiro, Anterior, Próximo e Último LinkButtons Abaixo do GridView

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 PageIndexque 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).

As primeiras 10 contas de usuário são exibidas

Figura 8: As primeiras 10 contas de usuário são exibidas (clique para exibir a imagem em tamanho real)

Clicar no próximo link exibe a segunda página de contas de usuário

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:

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