ASP .NET - Construindo um Carrinho de Compras


Eu estou voltando a este assunto por que ele é recorrente e também para mostrar outra forma de construir um carrinho de compras com ASP .NET usando um enfoque mais orientado a objetos.

Neste enfoque eu não vou usar um banco de dados vou definir o modelo de classes e a partir dele criar um web site ASP .NET que mostra o funcionamento básico do carrinho de compras.

Criando o Projeto

Abra o Visual Web Developer 2010 Express Edition e crie um novo WebSite usando o modelo ASP .NET Empty Web Site com o nome CarrinhoComprasVB;

Vamos começar criando o modelo de classes da aplicação o qual irá conter as seguintes classes:

  1. Produto - Define os produtos com os quais iremos trabalhar;
  2. CarrinhoItem - Define os itens selecionados;
  3. CarrinhoCompras - Trata os itens do carrinho de compras;

Vamos incluir uma nova classe na aplicação através do menu WebSite -> Add New Item e selecionando o template Class informando o nome Produto.vb;

A seguir inclua o código abaixo neste arquivo:

 A classe produto
' Usada para simular o acesso aos dados dos produtos
Public Class Produto

#Region "Propriedades"

    Private _id As Integer
    Public Property Id() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            _id = value
        End Set
    End Property

    Private _preco As Decimal
    Public Property Preco() As Decimal
        Get
            Return _preco
        End Get
        Set(ByVal value As Decimal)
            _preco = value
        End Set
    End Property

    Private _descricao As String
    Public Property Descricao() As String
        Get
            Return _descricao
        End Get
        Set(ByVal value As String)
            _descricao = value
        End Set
    End Property

#End Region

    Public Sub New(ByVal id As Integer)
        Me.Id = id
        If id = 1 Then
            Me.Preco = 1219.95
            Me.Descricao = "NoteBook LG"
        ElseIf id = 2 Then
            Me.Preco = 1399.95
            Me.Descricao = "IPad 2 16 GB"
        ElseIf id = 3 Then
            Me.Preco = 1815.55
            Me.Descricao = "IPhone 4S"
        ElseIf id = 4 Then
            Me.Preco = 1025.10
            Me.Descricao = "Pen Drive 16 Gb"
        End If
    End Sub
End Class
Nesta classe definimos as seguintes propriedades:
  1. Id
  2. Preco
  3. Descricao

E um construtor (Sub New) que inicia os membros da classe.

Repita o procedimento e inclua uma nova classe ao projeto com o nome CarrinhoItem.vb a qual deverá ter o seguinte código:

Imports Microsoft.VisualBasic

' Classe CarrinhoItem
' Uma estrutura básica para tratar os dados dos itens

Public Class CarrinhoItem
    Implements IEquatable(Of CarrinhoItem)

#Region "Propriedades"

 
  ' Armazena a quantidade no carrinho
    Private _quantidade As Integer
    Public Property Quantidade() As Integer
        Get
            Return _quantidade
        End Get
        Set(ByVal value As Integer)
            _quantidade = value
        End Set
    End Property

    Private _produtoId As Integer
    Public Property ProdutoId() As Integer
        Get
            Return _produtoId
        End Get
        Set(ByVal value As Integer)
            ' Garante que o objeto Prod será recriado
            _prod = Nothing
            _produtoId = value
        End Set
    End Property

    Private _prod As Produto = Nothing
    Public ReadOnly Property Prod() As Produto
        Get
          
 ' Inicialização tardia (Lazy initialization) - o objeto não será criado até que seja requisitado
            If _prod Is Nothing Then
                _prod = New Produto(ProdutoId)
            End If
            Return _prod
        End Get
    End Property

    Public ReadOnly Property Descricao() As String
        Get
            Return Prod.Descricao
        End Get
    End Property


    Public ReadOnly Property PrecoUnitario() As Decimal
        Get
            Return Prod.Preco
        End Get
    End Property

    Public ReadOnly Property PrecoTotal() As Decimal
        Get
            Return PrecoUnitario * Quantidade
        End Get
    End Property

#End Region

  
 ' Construtor CarrinhoItem somente precisa do codigo do produto
    Public Sub New(ByVal _produtoId As Integer)
        Me.ProdutoId = _produtoId
    End Sub

  
 ' Equals() - Necessario implementar a interface IEquatable
    ' Para testar se o item é igual ou não ao parametro
    ' Este método é chamado pelo método Contains() na classe List
    ' Usamos o método Contains() no método AddItem() da classe CarrinhoCompras

    Public Overloads Function Equals(ByVal item As CarrinhoItem) As Boolean Implements IEquatable(Of CarrinhoItem).Equals
        Return item.ProdutoId = Me.ProdutoId
    End Function

End Class

Agora inclua a última classe no projeto com o nome CarrinhoCompras.vb e o código abaixo:

Imports Microsoft.VisualBasic

' Classe carrinho de compras
' trata os itens que estão no carrinho e fornece métodos para manipulá-los

Public Class CarrinhoCompras

#Region "Propriedades"

    Private _items As List(Of CarrinhoItem)
    Public Property Items() As List(Of CarrinhoItem)
        Get
            Return _items
        End Get
        Private Set(ByVal value As List(Of CarrinhoItem))
            _items = value
        End Set
    End Property

#End Region

#Region "Implementação Singleton"

    ' Variáveis Readonly podem ser definidas na inicialização ou construção
    Public Shared ReadOnly Instancia As CarrinhoCompras
    ' O construtor static é chamado tão logo a classe é carregada na memoria
    Shared Sub New()
        ' Se o carrinho não esta na sessão cria um novo carrinho e poe na memoria
        ' caso contrario pega o que esta na sessão

        If HttpContext.Current.Session("ASPNETCarrinhoCompras") Is Nothing Then
            Instancia = New CarrinhoCompras()
            Instancia.Items = New List(Of CarrinhoItem)
            HttpContext.Current.Session("ASPNETCarrinhoCompras") = Instancia
        Else
            Instancia = CType(HttpContext.Current.Session("ASPNETCarrinhoCompras"), CarrinhoCompras)
        End If
    End Sub
    ' Um construtor protected garante que um objeto não pode ser criado fora da classe
    Protected Sub New()
    End Sub
#End Region

#Region "Método modificação de Item"

    ' AddItem() - Inclui um item no carrinho
    Public Sub AddItem(ByVal produtoId As Integer)
        ' Cria um novo item para incluir no carrinho
        Dim novoItem = New CarrinhoItem(produtoId)

        ' Se o item já existe na lista de itens aumenta a quantidade
        ' senão inclui um novo item na lista

        If Items.Contains(novoItem) Then
            For Each item As CarrinhoItem In Items
                If item.Equals(novoItem) Then
                    item.Quantidade += 1
                    Return
                End If
            Next
        Else
            novoItem.Quantidade = 1
            Items.Add(novoItem)
        End If

    End Sub

    ' setItemQuantidade() - altera a quantidade de um item no carrinho
    Public Sub setItemQuantidade(ByVal produtoId As Integer, ByVal quantidade As Integer)
        ' Se estamos definindo a quantidade para 0 , remove o item
        If quantidade = 0 Then
            RemoveItem(produtoId)
            Return
        End If
        ' Procura o item e autaliza a quantidade
        Dim atualizaItem = New CarrinhoItem(produtoId)
        For Each item As CarrinhoItem In Items
            If item.Equals(atualizaItem) Then
                item.Quantidade = quantidade
                Return
            End If
        Next
    End Sub

    ' RemoveItem() - Remove um item do carrinho
    Public Sub RemoveItem(ByVal produtoId As Integer)
        Dim itemRemovido = New CarrinhoItem(produtoId)

        For Each item As CarrinhoItem In Items
            If item.Equals(itemRemovido) Then
                Items.Remove(item)
                Return
            End If
        Next
    End Sub

#End Region

    ' GetSubTotal() - retorn ao preco total de todos  os itens antes dos impostos
    Public Function GetSubTotal() As Decimal
        Dim subTotal As Decimal = 0
        For Each item As CarrinhoItem In Items
            subTotal += item.PrecoTotal
        Next

        Return subTotal
    End Function

End Class

Agora vamos incluir um novo web form no projeto via menu WebSite -> Add New Item selecionando o template Web Form e aceitando o nome Default.aspx.

Vamos usar o controle LinkButton e construir o seguinte leiaute no formulário Default.aspx:

A seguir vamos incluir o código abaixo no code-behind arquivo Default.aspx.vb:

Partial Class _Default
    Inherits System.Web.UI.Page

    Protected Sub lnkInclui1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkInclui1.Click
   
    ' Incluir o produto 1 no carrinho
        CarrinhoCompras.Instancia.AddItem(1)
       
' Redireciona o usuário para ver o carrinho

        Response.Redirect("VerCarrinho.aspx")
    End Sub

    Protected Sub lnkInclui2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkInclui2.Click
        CarrinhoCompras.Instancia.AddItem(2)
        Response.Redirect("VerCarrinho.aspx")
    End Sub

    Protected Sub lnkInclui3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkInclui3.Click
        CarrinhoCompras.Instancia.AddItem(3)
        Response.Redirect("VerCarrinho.aspx")
    End Sub

    Protected Sub lnkInclui4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkInclui4.Click
        CarrinhoCompras.Instancia.AddItem(4)
        Response.Redirect("VerCarrinho.aspx")
    End Sub
End Class

Este código apenas cria uma instância dos itens e inclui no carrinho de compras redirecionando para a página que exibe o carrinho.

Vamos criar agora a página VerCarrinho.aspx que exibe os itens do carrinho realizando a totalização e o controle da quantidade dos itens.

No menu WebSite clique em Add New Item e selecione o template Web Form e informe o nome VerCarrinho.aspx

Neste web forma inclua um controle GridView(ID=gvCarrinhoCompras) e um controle Button(ID=btnAtualizarCarrinho) conforme o leiaute abaixo:

Defina a propriedade DataKeyNames do GridView como ProdutoId e a proriedade GridLines = None;

O código desta página é exibido a seguir:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="VerCarrinho.aspx.vb" Inherits="ViewCart" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Carrinho de Compras</title>
	<link href="Styles/Estilo.css" rel="stylesheet" type="text/css" />
    <style type="text/css">
        .style1{color: #0066CC;}
        .style2{color: #000066;}
    </style>
</head>
<body>
    <form id="form1" runat="server">
	<div class="container">
		<h1><asp:Image ID="Image1" runat="server" ImageUrl="~/maco10.jpg" /> -  <span class="style1">Loja Virtual</span></h1>
		<a href="Default.aspx" class="style2"><strong>&lt; Continuar Comprando</strong></a>
		<br /><br />
		<asp:GridView runat="server" ID="gvCarrinhoCompras" AutoGenerateColumns="False" 
                               EmptyDataText="Não há nenhum item no seu carrinho de compras." GridLines="None" 
                               Width="100%" CellPadding="5" ShowFooter="True" DataKeyNames="ProdutoId" 
                              OnRowDataBound="gvCarrinhoCompras_RowDataBound"  OnRowCommand="gvCarrinhoCompras_RowCommand">
		<HeaderStyle HorizontalAlign="Left" BackColor="#3D7169" ForeColor="#FFFFFF" />
		<FooterStyle HorizontalAlign="Right" BackColor="#6C6B66" ForeColor="#FFFFFF" />
		<AlternatingRowStyle BackColor="#F8F8F8" />
		<Columns>
		<asp:BoundField DataField="Descricao" HeaderText="Descrição" />
		<asp:TemplateField HeaderText="Quantidade">
		<ItemTemplate>
		<asp:TextBox runat="server" ID="txtQuantidade" Columns="5" Text='<%# Eval("Quantidade") %>'></asp:TextBox><br />
		<asp:LinkButton runat="server" ID="btnRemove" Text="Remover" CommandName="Remove" CommandArgument='<%# Eval("ProdutoId") %>'  style="font-size:12px;"></asp:LinkButton>
		</ItemTemplate>
	</asp:TemplateField>
	<asp:BoundField DataField="PrecoUnitario" HeaderText="Preço" ItemStyle-HorizontalAlign="Right" HeaderStyle-HorizontalAlign="Right" DataFormatString="{0:C}" > 
                      <HeaderStyle HorizontalAlign="Right"></HeaderStyle>
                     <ItemStyle HorizontalAlign="Right"></ItemStyle>
                    </asp:BoundField>
	<asp:BoundField DataField="PrecoTotal" HeaderText="Total" ItemStyle-HorizontalAlign="Right" HeaderStyle-HorizontalAlign="Right" DataFormatString="{0:C}" >
                    <HeaderStyle HorizontalAlign="Right"></HeaderStyle>
                     <ItemStyle HorizontalAlign="Right"></ItemStyle>
                    </asp:BoundField>
	</Columns>
	</asp:GridView>
	<br />
	<asp:Button runat="server" ID="btnAtualizarCarrinho" Text="Atualiza Carrinho"  OnClick="btnAtualizarCarrinho_Click" />
     </div>
    </form>
</body>
</html>

Note que criamos um templateField para o exibir a quantidade de itens vinculado a propriedade Quantidade da classe CarrinhoItem.

Definimos também dois eventos:

O evento  RowDataBound  é disparado quando um linha de dados (representado pelo objeto GridViewRow) é vinculado aos dados em um controle GridView.  Isto permite implementar um tratamento de evento para realizar operações que alterem as propriedades dos dados vinculados a linha sempre que o evento ocorrer.

O código do arquivo code-behind VerCarrinho.aspx.vb é mostrado a seguir:

Partial Class VerCarrinho
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ' ASP.NET gerencia o estado de todos os controles de servidor
        ' (como o GridView).  Por isso, não precisamos fornece a ele os dados
        ' toda a vez que a pagina carrega somente se não for um postback
        If Not IsPostBack Then
            BindData()
        End If
    End Sub

    Protected Sub BindData()
        ' Atribui os dados ao GridView
        ' O GridView irá tomar os itens do carrinho um a um e usar as propriedades
        ' que declaramos como sendo os nomes das colunas
        gvCarrinhoCompras.DataSource = CarrinhoCompras.Instancia.Items
        gvCarrinhoCompras.DataBind()
    End Sub

    Protected Sub gvCarrinhoCompras_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles gvCarrinhoCompras.RowDataBound
        ' Se estamos vinculando no rodape vamos adicionar o total
        If e.Row.RowType = DataControlRowType.Footer Then
            e.Row.Cells(3).Text = "Total: " & CarrinhoCompras.Instancia.GetSubTotal().ToString("C")
        End If
    End Sub

    ' Este método responde ao evento Click do botão Remover
    Protected Sub gvCarrinhoCompras_RowCommand(ByVal sender As Object, ByVal e As GridViewCommandEventArgs) Handles gvCarrinhoCompras.RowCommand
        If e.CommandName = "Remove" Then
            Dim produtoId = Convert.ToInt32(e.CommandArgument)
            CarrinhoCompras.Instancia.RemoveItem(produtoId)
        End If
        ' temos que redefinir os dados para atualizar a exibi~ção
        BindData()
    End Sub
    Protected Sub btnAtualizarCarrinho_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnAtualizarCarrinho.Click
        For Each row As GridViewRow In gvCarrinhoCompras.Rows
            If row.RowType = DataControlRowType.DataRow Then
                ' Vamos usar um bloco try/catch no caso de algum outro codigo de produto for informado
                Try
                    ' Pega o codigo do produto na propriedade datakeys do GridView
                    Dim produtoId = Convert.ToInt32(gvCarrinhoCompras.DataKeys(row.RowIndex).Value)
                    ' Encontra o TextBox da quantidade e retorna o valor
                    Dim quantidade = Integer.Parse(CType(row.Cells(1).FindControl("txtQuantidade"), TextBox).Text)
                    CarrinhoCompras.Instancia.setItemQuantidade(produtoId, quantidade)
                Catch ex As FormatException
                    'Throw ex
                End Try
            End If
        Next
        ' temos que redefinir os dados para atualizar a exibi~ção
        BindData()
    End Sub
End Class

Executando o projeto iremos obter a pagina Default.aspx:

Após incluir alguns itens no carrinho e/ou clicar no link - Ver Carrinho - teremos:

Podemos alterar a quantidade e clicar no botão Atualiza Carrinho para atualizar os valores.

Para excluir um item informe o valor 0 e clique em Atualiza Carrinho ou clique no link Remover.

Pegue o projeto completo com os exemplos aqui : CarrinhoComprasVB.zip

João 6:35 Declarou-lhes Jesus. Eu sou o pão da vida; aquele que vem a mim, de modo algum terá fome, e quem crê em mim jamais tará sede.
João 6:36
Mas como já vos disse, vós me tendes visto, e contudo não credes.
João 6:37
Todo o que o Pai me dá virá a mim; e o que vem a mim de maneira nenhuma o lançarei fora.
João 6:38
Porque eu desci do céu, não para fazer a minha vontade, mas a vontade daquele que me enviou.

João 6:39
E a vontade do que me enviou é esta: Que eu não perca nenhum de todos aqueles que me deu, mas que eu o ressuscite no último dia.

Referências:


José Carlos Macoratti