Visual Basic em Português

Página pessoal de Jorge Paulino sobre o Visual Basic (VB.NET, ASP.NET, VB6, VBA) e algumas noticias de tecnologia

VB.NET: Sistema de Classificação numa DataGridView

O controlo DataGridView é realmente muito útil e versátil. Tem inúmeras funcionalidades e diversos tipos de células (TextBox, ComboBox, Button, etc.) e permite ainda alojar outros tipos personalizados. A criação de tipos personalizados ou pré-definidos de células permite uma expansão a este controlo.

       

Um bom exemplo de criação de células personalizadas na DataGridView é a criação de uma sistema de classificação ou votação, onde normalmente se utiliza estrelas para identificar a preferência ou gosto (1 fraco, 5 excelente).

       

       

Para implementar este sistema é necessário adicionar algumas imagens aos Resources do nosso projecto – 12 para ser preciso). As seguintes imagens poderão ser alteradas, sendo apenas necessário manter o nome e o tamanho.

       


       

Depois, para criar uma coluna/células de estrelas que permitam a classificação de um determinado item é necessário criar uma classe que herde as classes DataGridViewImageColumn e DataGridViewImageCell. Esta classe deverá ter o seguinte código:

       


Imports System

Imports System.Collections.Generic

Imports System.ComponentModel

Imports System.Drawing

Imports System.Data

Imports System.Text

Imports System.Windows.Forms

       

' Definição do namespace

Namespace CustomDataGridViewColumn

       

    ' Cria um novo tipo de columa

    Public Class RatingColumn

        Inherits DataGridViewImageColumn

       

        ' Definições da coluna

        Public Sub New()

            Me.CellTemplate = New RatingCell()

            Me.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter

            Me.ValueType = GetType(Integer)

        End Sub

       

    End Class

       

       

    ' Cria um novo tipo de célula

    Public Class RatingCell

        Inherits DataGridViewImageCell

       

        Public Sub New()

            Me.ValueType = GetType(Integer)

        End Sub

       

        ' Faz o overrides à função GetFormattedValue retornando a imagem

        Protected Overloads Overrides Function GetFormattedValue(ByVal value As Object, ByVal rowIndex As Integer, ByRef cellStyle As DataGridViewCellStyle, ByVal valueTypeConverter As TypeConverter, ByVal formattedValueTypeConverter As TypeConverter, ByVal context As DataGridViewDataErrorContexts) As Object

            Return starImages(CInt(value))

        End Function

       

       

        ' Define o valor por defeito

        Public Overloads Overrides ReadOnly Property DefaultNewRowValue() As Object

            Get

                Return 3

            End Get

        End Property

       

       

        ' Faz o overrides ao Paint da imagem

        Protected Overloads Overrides Sub Paint(ByVal graphics As Graphics, ByVal clipBounds As Rectangle, _

                ByVal cellBounds As Rectangle, ByVal rowIndex As Integer, ByVal elementState As DataGridViewElementStates, _

                ByVal value As Object, ByVal formattedValue As Object, ByVal errorText As String, _

                ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, _

                ByVal paintParts As DataGridViewPaintParts)

       

            Dim cellImage As Image = DirectCast(formattedValue, Image)

       

            ' Verifica qual a estrela

            Dim point As Point = Me.DataGridView.PointToClient(Control.MousePosition)

            Dim starNumber As Integer = GetStarFromMouse(cellBounds, point)

       

            ' Caso o número seja válido

            If starNumber <> -1 Then

                cellImage = starHotImages(starNumber)

            End If

       

            ' Suspende o desenho da selecção

            MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, _

                 cellImage, errorText, cellStyle, advancedBorderStyle, _

                (paintParts And Not DataGridViewPaintParts.SelectionBackground))

       

        End Sub

       

       

        ' Actualiza o valor da célula quando o utilizador clica na estrela

        Protected Overloads Overrides Sub OnContentClick(ByVal e As DataGridViewCellEventArgs)

            MyBase.OnContentClick(e)

       

            Dim starNumber As Integer

            Dim x As Integer = Me.DataGridView.CurrentCellAddress.X

            Dim y As Integer = Me.DataGridView.CurrentCellAddress.Y

            Dim ret As Rectangle = Me.DataGridView.GetCellDisplayRectangle(x, y, False)

       

            ' Verifica qual o valor

            starNumber = GetStarFromMouse(ret, Me.DataGridView.PointToClient(Control.MousePosition))

       

            ' Caso o número seja válido

            If starNumber <> -1 Then

                Me.Value = starNumber

            End If

        End Sub

       

       

        ' Invalida as células quando o rato move ou sai

        Protected Overloads Overrides Sub OnMouseLeave(ByVal rowIndex As Integer)

            MyBase.OnMouseLeave(rowIndex)

            Me.DataGridView.InvalidateCell(Me)

        End Sub

       

        Protected Overloads Overrides Sub OnMouseMove(ByVal e As DataGridViewCellMouseEventArgs)

            MyBase.OnMouseMove(e)

            Me.DataGridView.InvalidateCell(Me)

        End Sub

       

       

        Shared starImages As Image()

        Shared starHotImages As Image()

       

        Const IMAGEWIDTH As Integer = 58

       

        ' Função que verifica qual a estrela onde está o rato

        Private Function GetStarFromMouse(ByVal cellBounds As Rectangle, ByVal mouseLocation As Point) As Integer

            If cellBounds.Contains(mouseLocation) Then

       

                Dim mouseXRelativeToCell As Integer = (mouseLocation.X - cellBounds.X)

                Dim imageXArea As Integer = (cellBounds.Width / 2) - (IMAGEWIDTH / 2)

       

                If ((mouseXRelativeToCell + 4) < imageXArea) OrElse (mouseXRelativeToCell >= (imageXArea + IMAGEWIDTH)) Then

                    Return -1

                Else

                    Dim value As Integer = CInt(Math.Round(((CSng((mouseXRelativeToCell - imageXArea + 5)) / CSng(IMAGEWIDTH)) * 5.0F), MidpointRounding.AwayFromZero))

                    If value > 5 OrElse value < 0 Then

                        System.Diagnostics.Debugger.Break()

                    End If

                    Return value

                End If

            Else

                Return -1

            End If

        End Function

       

        ' Carrega as imagens

        Shared Sub New()

       

            starImages = New Image(5) {}

            starHotImages = New Image(5) {}

       

            For i As Integer = 0 To 5

                starImages(i) = DirectCast(My.Resources.ResourceManager.GetObject("star" + i.ToString()), Image)

            Next

       

            For i As Integer = 0 To 5

                starHotImages(i) = DirectCast(My.Resources.ResourceManager.GetObject("starhot" + i.ToString()), Image)

            Next

        End Sub

       

    End Class

       

End Namespace

       

       

Finalmente a implementação!

       

O seguinte exemplo utiliza uma lista de musicas que estão numa base de dados. A base de dados está ligada à DataGridView e existe um campo que é a classificação da música. Ao ser carregada a lista ou alterada a classificação, será actualizado o respectivo campo na DataGridView e posterioremente na base de dados. Para ligar uma base de dados a uma DataGridView podem ver o seguinte exemplo: Utilizando o controlo DataGridView

       

No evento Form Load, além de definir a DataSource da DataGridView, é necessário adicionar a nova coluna e indicar quais as classificações actuais.   

     

    ' Definição da localização da coluna onde estão as

    ' classificações - Alterar de acordo com a coluna

    Private ratingBoundColumn As Byte = 2

    Private ratingColumn As Byte

       

       

    Private Sub frmRating_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

       

        Application.EnableVisualStyles()

       

        ' Suspende a actualização da DataGridView

        Me.DataGridView1.SuspendLayout()

       

       

        ' Definição de dataSource da datagridview

        ' ---------------------------------------------------------------------

        Me.DataGridView1.DataSource = ...

        ' ---------------------------------------------------------------------

       

        ' Adiciona a nova coluna à DataGridView

        Dim myRatingColumn As New CustomDataGridViewColumn.RatingColumn

        myRatingColumn.HeaderText = "Classificação"

        Me.DataGridView1.Columns.Insert(Me.DataGridView1.ColumnCount, myRatingColumn)

       

        ' Verifica qual o número da coluna

        ratingColumn = Me.DataGridView1.ColumnCount - 1

       

        ' Faz um ciclo em todas as linhas e define o nº de estrelas

        For Each row As DataGridViewRow In Me.DataGridView1.Rows

            If Not row.IsNewRow Then

                Dim rc As DataGridViewImageCell

                rc = (CType(Me.DataGridView1(ratingColumn, row.Index), DataGridViewImageCell))

                rc.Value = row.Cells(ratingBoundColumn).Value

            End If

        Next

       

        ' Esconde a coluna onde se encontra o valor

        Me.DataGridView1.Columns(ratingBoundColumn).Visible = False

       

        ' Actualiza a DataGridView

        Me.DataGridView1.ResumeLayout()

       

    End Sub

       

       

Depois, quando é alterada alguma classificação, utilizando o evento CellContentClick

       

       

    ' Sempre que é alterada a classificação escreve na coluna/célula respectiva

    Private Sub DataGridView1_CellContentClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick

       

        If e.ColumnIndex = ratingColumn And e.RowIndex <> -1 Then

       

            ' Verifica o valor nas coluna de classificação

            Dim rc As DataGridViewImageCell

            rc = (CType(Me.DataGridView1(ratingColumn, e.RowIndex), DataGridViewImageCell))

       

            ' Caso o valor seja diferente (seleccionado/existente)

            If rc.Value <> Me.DataGridView1(ratingBoundColumn, e.RowIndex).Value Then

                ' Escreve o novo valor

                Me.DataGridView1(ratingBoundColumn, e.RowIndex).Value = rc.Value

            End If

        End If

       

    End Sub

       


Exemplo do artigo: DOWNLOAD DO FICHEIRO

       

       

PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!


VB6: Forms Transparentes

A transparência é a capacidade de ser transparente, ou seja, de permitir ver através, neste caso, do objecto. Esta transparência permite criar formulários que deixam ver através dele, que abrem com um efeito fade in (começar a aparecer até ficar opaco) ou fade out (do opaco ao invisível). Estas são algumas aplicações possíveis, que para além de um efeito bonito, melhoram o visual da aplicação.     

O design de uma aplicação é muito importante e o seu sucesso pode também ser o sucesso da aplicação. Uma aplicação por muito boa que esteja (funcional), dificilmente vende ou convence se não tiver um bom design. 

O VB.NET já inclui esta propriedade nos Forms mas o VB6 não e para a criarmos necessitamos de recorrer a alguns API’s. O API que permite este efeito é o SetLayeredWindowAttributes() embora se utilizem outros auxiliares, como é o caso das funções GetWindowLong() e SetWindowLong().

Para tornarmos um form transparente necessitamos do seguinte código (embora apenas seja usado o Sub MakeTransparent() e não o MakeOpaque() que “apenas” transforma o form em opaco):

No módulo:

    ' Declaração de API's necessários
    Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hWnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long

    Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

    ' Definição de constantes
    Private Const GWL_EXSTYLE = (-20)
    Private Const LWA_COLORKEY = &H1
    Private Const LWA_ALPHA = &H2
    Private Const ULW_COLORKEY = &H1
    Private Const ULW_ALPHA = &H2
    Private Const ULW_OPAQUE = &H4
    Private Const WS_EX_LAYERED = &H80000

    ' Define o Form como transparente
    Public Sub MakeTransparent(ByVal hWnd As Long, ByVal bAlpha As Integer)
        Dim msg As Long

        ' Ignora possíveis erros
        On Error Resume Next

        ' Caso o valor seja inferior a 255 e superior
        ' a 0 aplica uma nova transparência
        If bAlpha > 0 Or bAlpha < 255 Then

            msg = GetWindowLong(hWnd, GWL_EXSTYLE)
            msg = msg Or WS_EX_LAYERED
            SetWindowLong hWnd, GWL_EXSTYLE, msg     

            SetLayeredWindowAttributes hWnd, 0, bAlpha, LWA_ALPHA

        End If

    End Sub

    ' Define o form com opaco
    Public Sub MakeOpaque(ByVal hWnd As Long)
        Dim msg As Long     

        ' Ignora possíveis erros
        On Error Resume Next

        msg = GetWindowLong(hWnd, GWL_EXSTYLE)
        msg = msg And Not WS_EX_LAYERED
        SetWindowLong hWnd, GWL_EXSTYLE, msg

        SetLayeredWindowAttributes hWnd, 0, 0, LWA_ALPHA

    End Sub

Depois, na inicialização do Form:

    Private Sub Form_Initialize()

        MakeTransparent Me.hWnd, 150

    End Sub

O valor a indicar pode variar entre 0 e 255, onde 0 é o máximo de transparência.

Este exemplo mostra como iniciar um Form transparente, mas pode-se criar um efeito mais interessante onde o Form começa a aparecer até ficar opaco – fade in. Para criar este efeito é apenas necessário adicionar um Timer ao Form e utilizar o seguinte código:

    ' Variável que irá guardar o valor da transparência
    Private i As Integer

    ' Inicializa o form como transparente
    Private Sub Form_Initialize()

        MakeTransparent(Me.hWnd, 0)

    End Sub

    ' No intervalo definido
    Private Sub Timer1_Timer()

        ' Incrementa o valor da transparência
        i = i + 10

        ' Caso não tenha atingido 255 define nova 
        ' transparência, caso contrário pára o timer
        If i <= 255 Then
            MakeTransparent(Me.hWnd, i)
        Else
            Timer1.Enabled = False
        End If

    End Sub

    ' Definição do intervalo do timer e inicialização
    Private Sub Form_Load()
        Timer1.Interval = 100
        Timer1.Enabled = True
    End Sub

Outra das aplicações que esta função permite, com umas pequenas alterações, é dizer que apenas uma cor ficará transparente. Ora isto permite colocar um controlo dentro do form e dizer que este é transparente criando uma janela ou um formato diferente.

 
Um exemplo engraçado para mostrar a sua implementação é criar algo parecido com um queijo. É apenas necessário colocar umas shapes no Form, formatá-las e definir no Form BorderStyle = None. Depois o código:

    Private Sub Form_Load()
        Dim ctrl As Control

        ' Definição de todas as shapes do Form com o fundo a
        ' verde, estilo opaco e com os limites a transparente
        For Each ctrl In Me.Controls

            If TypeOf ctrl Is Shape Then
                ctrl.BackStyle = 1
                ctrl.BackColor = vbGreen
                ctrl.BorderStyle = 0
            End If

        Next

        ' Chamar a função que irá colocar
        ' tudo o que é verde como transparente
        MakeTransparent Me.hWnd, 0

    End Sub

Finalmente no Sub MakeTransparent() alterar a seguinte linha, de modo a transformar tudo o que está a verde (vbGreen) em invisível.

De:

SetLayeredWindowAttributes hWnd, 0, bAlpha, LWA_ALPHA

Para:

SetLayeredWindowAttributes hWnd, vbGreen, bAlpha, LWA_COLORKEY

São pequenos exemplos de como usar a transparência nos Forms e como criar alguns efeitos interessantes.

PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!


VB.NET: Utilizando Colecções List(Of T)

O namespace System.Collections.Generic contém um conjunto de interfaces e classes que permitem criar colecções muito seguras, simples de utilizar e com muitos bons performances. Estas colecções de Generics são do tipo strongly typed, ou seja, estão disponíveis no intelissense e verificadas pelo compilador, o que evita erros e torna mais fácil a vida ao programador.

   

Existem diversos tipos de colecções neste ou em outros namespaces (como por exemplo o Dictionary, SortedList, ArrayList, etc) mas um muito utilizado é o List(Of (T)), onde se define o tipo da colecção que se pretende.

   

Um exemplo simples de utilização:

   

        ' Cria uma nova lista do tipo "string"

        Dim lstEmpreg As New List(Of String)

   

        ' Adiciona alguns nomes à lista

        lstEmpreg.Add("João Paulo")

        lstEmpreg.Add("Pedro Sousa")

        lstEmpreg.Add("Luís Nascimento")

        lstEmpreg.Add("Carlos Sousa")

        lstEmpreg.Add("Nuno Luís")

   

        Dim nomePesquisar As String = "Pedro Sousa"

   

        ' Caso o nome a pesquisar seja encontrado

        If lstEmpreg.Contains(nomePesquisar) Then

   

            ' Mostra o nome e o total de nomes na lista

            Dim msg As String = "O nome {0} foi encontrado em {1} nome(s)"

            Debug.WriteLine(String.Format(msg, nomePesquisar, lstEmpreg.Count))

   

        Else

   

            ' Mensagem caso não encontre o nome na lista

            Debug.WriteLine("Nome não encontrado na lista!")

   

        End If

   

 

Mas esta colecção pode ser muito mais complexa, sendo necessário guardar vários dados (para além do nome), como por exemplo o número, idade, telefone, etc. Neste caso será criada uma estrutura e guardada na lista.

   

O método de pesquisa implementa um IEquatable e utiliza um Predicate. Desta forma não é necessário percorrer todos os items da lista para verificar qual o item correspondente.

   

Um exemplo de utilização

Construção de uma classe simples, sem utilização de  propriedades (get/set), que implementa o IEquatable (apenas para demonstração):

   

Exemplo:

   

' Construção de uma classe simples, sem utilização de

‘ propriedades (get/set) que implementa o IEquatable

Public Class Empregado

    Implements IEquatable(Of Empregado)

   

    ' Campos da classe

    Public ID As Byte

    Public Nome As String

    Public Idade As Byte

   

    Public Function ProcuraEmpregado(ByVal EmpID As Empregado) _

        As Boolean Implements System.IEquatable(Of Empregado).Equals

        ' Retorna o ID mas poderá ser utilizado outro campo

        ' para pesquisa, sendo apenas necessário altera para

        ' o campo correctos (ex. Me.Nome = EmpID.Nome)

        Return Me.ID = EmpID.ID

    End Function

   

End Class

   

   

Utilização da List(Of T) que neste caso adiciona alguns empregados e pesquisa por um campo mostrando os resultados. O campo escolhido para o exemplo foi o ID mas pode-se pesquisar por qualquer campo.

   

   

        ' Cria uma nova lista do tipo "Empregado"

        Dim lstEmpregados As New List(Of Empregado)

   

        ' Adiciona empregados à lista

        Dim Emp1 As New Empregado

        Emp1.ID = 1

        Emp1.Nome = "João Paulo"

        Emp1.Idade = 28

        lstEmpregados.Add(Emp1)

   

        Dim Emp2 As New Empregado

        Emp2.ID = 2

        Emp2.Nome = "Pedro Sousa"

        Emp2.Idade = 25

        lstEmpregados.Add(Emp2)

   

        Dim Emp3 As New Empregado

        Emp3.ID = 3

        Emp3.Nome = "Luís Nascimento"

        Emp3.Idade = 41

        lstEmpregados.Add(Emp3)

   

        ' Empregado a procurar na lista

        Dim Emp As New Empregado

        Emp.ID = 2

   

        ' Guarda o resultado da procura na variável EmpResult

        Dim EmpResult As Empregado

   

        ' Pesquisa na lista de empregados usando um Predicate do tipo empregado

        EmpResult = lstEmpregados.Find(New System.Predicate(Of Empregado)(AddressOf Emp.ProcuraEmpregado))

   

        ' Caso tenha encontrado mostra os resultados ou a mensagem de erro

        If EmpResult IsNot Nothing Then

            Debug.WriteLine("Número: " & EmpResult.ID.ToString)

            Debug.WriteLine("Empregado: " & EmpResult.Nome.ToString)

            Debug.WriteLine("Idade: " & EmpResult.Idade.ToString)

        Else

            Debug.WriteLine("Número de empregado não encontrado")

        End If

   

 

 

Resultado:

 

Número: 2

Empregado: Pedro Sousa

Idade: 25

 

 

As colecções foram completamente revolucionadas com a versão .NET e a sua utilização vem melhorar o que, no passado com o VB6, se fazia apenas com as Collections (muito limitadas) ou com a utilização de arrays.

   

São bastante simples de utilizar e é recomendável que se verifique qual a colecção que mais se adequa às necessidades no programa.

   

   

PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!




Microsoft Office Especialist

Membro da Comunidade
Experts-Exchange


Administ. da Comunidade
Portugal-a-Programar



Twitter

Artigos no CodeProject

Artigos no CodeProject

Subscrever Novidades

Endereço de Email:

Delivered by FeedBurner

Seguidores

Histórico