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: Exportar DataGridView para Excel

O Microsoft Excel é uma ferramenta muito utilizada e normalmente é um dos formatos que se disponibiliza quando se exporta informação. Esta opção permite ao utilizador modificar dados, criar gráficos personalizados, alterar a informação, etc.  

Early Binding vs Late Binding

Para realizar automação através do VB.NET, e para explicar um pouco as principais diferenças, existem dois métodos que se podem utilizar: Late Binding e Early Binding. O Late Binding utiliza o CreateObject() para criar uma nova instância do Excel ou o GetObject() para manipular uma instância já criada. Um exemplo das suas utilizações:

‘ Criar uma nova instância do Excel
Dim objExcelApp As Object
objExcelApp = CreateObject("Excel.Application")

    ‘ Manipular uma instância já criada
    Dim objExcelApp As Object
    objExcelApp = GetObject(, "Excel.Application")


As suas principais vantagens são:

  • Independente da versão utilizada, ou seja, não é específico para uma versão e pode ser utilizado tanto na versão 97 do Excel com na 2007. Existem incompatibilidades entre versões e um código desenvolvido em Excel 2003 provavelmente não funcionará em Excel 2007 (por exemplo);
  • Redução do tamanho do projecto, ou seja, quanto mais referências forem adicionadas ao projecto, maior a tamanho do mesmo e mais demorado é o tempo de compilação;
  • Alguns ambientes de programação não permitem adicionar referências ao projecto;

    Para utilizar o método Early Binding é necessário adicionar a referência à livraria a utilizar (ex. Microsoft Excel 11.0 Object Library).


   
‘ Criar uma nova instância do Excel
  
Dim objExcelApp As New Excel.Application

As principais vantagens são:

  • O código é mais rápido uma vez que é compilado antes da execução. Utilizando o Late Binding o código é compilado quando é executado reduzindo o performance;
  • Acesso ao intellisense o que permite aceder às constantes, métodos, objectos, etc. Com o Late Binding o programar é “às cegas”.
  • Permite detectar erros no código antes da execução, através do debug, uma vez que este é compilado.

Por estas diferenças, e sempre que possível, é recomendável a utilização do Early Binding. O seguinte exemplo será efectuado usando o Early Binding.

Exportação para Excel

A automação para Excel permite fazer tudo (ou quase tudo) o que se pode fazer no Excel. Desde formações, gráficos, fórmulas, etc, etc. Neste exemplo será mostrado como exportar o conteúdo de uma DataGridView permitindo gravar para o disco ou abrir o Excel com os resultados.

Para utilizar este exemplo é necessário adicionar como referência ao nosso projecto o “Microsoft Excel XX.0 Object Library”. Dependendo da versão do Excel poderá ser necessário (para facilitar) importar as classes Microsoft.Office.Interop ou não.

Imports Microsoft.Office.Interop
Imports System.Threading.Thread
Imports System.Globalization   

    Enum xlsOption
        xlsSaveAs
        xlsOpen
    End Enum   

    ''' <summary>
    ''' Exporta a informação de uma DataGridView para o Microsoft Excel
    ''' </summary>
    ''' <param name="dgvName">Nome da DataGridView </param>
    ''' <param name="option">SaveAs ou Open</param>
    ''' <param name="fileName">Nome completo do ficheiro</param>
    Public Sub ExportToExcel(ByVal dgvName As DataGridView, ByVal [option] As xlsOption, Optional ByVal fileName As String = "")

        Dim objExcelApp As New Excel.Application()
        Dim objExcelBook As Excel.Workbook
        Dim objExcelSheet As Excel.Worksheet   

        Try

            ' Se foi seleccionada a opção xlsSaveAs e não foi indicado ficheiro
            If [option] = xlsOption.xlsSaveAs And fileName = String.Empty Then
                MessageBox.Show("É necessário indicar um nome para o ficheiro")
                Exit Sub
            End If

            ' Altera o tipo/localização para Inglês. Existe incompatibilidade 
            ' entre algumas versões de Excel vs Sistema Operativo
            Dim oldCI As CultureInfo = CurrentThread.CurrentCulture
            CurrentThread.CurrentCulture = New CultureInfo("en-US")

            ' Adiciona um workbook e activa a worksheet actual
            objExcelBook = objExcelApp.Workbooks.Add
            objExcelSheet = CType(objExcelBook.Worksheets(1), Excel.Worksheet)

            ' Ciclo nos cabeçalhos para escrever os títulos a bold/negrito
            Dim dgvColumnIndex As Int16 = 1
            For Each col As DataGridViewColumn In dgvName.Columns
                objExcelSheet.Cells(1, dgvColumnIndex) = col.HeaderText
                objExcelSheet.Cells(1, dgvColumnIndex).Font.Bold = True
                dgvColumnIndex += 1
            Next

            ' Ciclo nas linhas/células
            Dim dgvRowIndex As Integer = 2

           
For Each row As DataGridViewRow In dgvName.Rows

                Dim dgvCellIndex As Integer = 1

               
For Each cell As DataGridViewCell In row.Cells
                    objExcelSheet.Cells(dgvRowIndex, dgvCellIndex) = cell.Value
                    dgvCellIndex += 1
                Next

                dgvRowIndex += 1

            Next

            ' Ajusta o largura das colunas automaticamente
            objExcelSheet.Columns.AutoFit()

            ' Caso a opção seja gravar (xlsSaveAs) grava o ficheiro e fecha
            ' o Workbook/Excel. Caso contrário (xlsOpen) abre o Excel
            If [option] = xlsOption.xlsSaveAs Then
                objExcelBook.SaveAs(fileName)
                objExcelBook.Close()
                objExcelApp.Quit()
                MessageBox.Show("Ficheiro exportado com sucesso para: " & fileName)

            Else
                objExcelApp.Visible = True
            End If

            ' Altera a tipo/localização para actual
            CurrentThread.CurrentCulture = oldCI

        Catch ex As Exception
            MessageBox.Show("Erro não identificado. Mensagem original:" & vbNewLine + ex.Message)

        Finally
            objExcelSheet = Nothing
            objExcelBook = Nothing
            objExcelApp = Nothing   

           ' O GC(garbage collector) recolhe a memória não usada pelo sistema. 
           ' O método Collect() força a recolha e a opção WaitForPendingFinalizers 
            ' espera até estar completo. Desta forma o EXCEL.EXE não fica no 
            ' Task Manager(gestor tarefas) ocupando memória desnecessariamente
            ' (devem ser chamados duas vezes para maior garantia)
            GC.Collect()
            GC.WaitForPendingFinalizers()
           
GC.Collect()
            GC.WaitForPendingFinalizers()

        End Try

    End Sub

Finalmente para utilizar no programa:

' Exporta e abre o Microsoft Excel
ExportToExcel(Me.DataGridView1, xlsOption.xlsOpen)

' Exporta e grava o ficheiro na localização indicada
ExportToExcel(Me.DataGridView1, xlsOption.xlsSaveAs, "c:\meuFicheiroExcel.xls")

   

O objectivo deste artigo é mostrar como utilizar a automação do Excel através do VB.NET e alguns truques que facilitam e optimizam este processo. O exemplo de exportar uma DataGridView é também muito util uma vez que é um controlo bastante utilizado.



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


VB.NET: Dicas de Programação #4

DataGridView – Sequência de Movimento (Após tecla ENTER)

 

A DataGridView tem como movimento após a tecla ENTER, de linha em linha, e não de coluna em coluna. Quando se está a preencher registos, em que se preenche um registo de cada vez, é prático que o utilizador após pressionar a tecla ENTER desloque a selecção para a coluna seguinte e não para o próximo registo (próxima linha).

 

Para conseguirmos alterar este comportamento podemos, no evento KeyDown, verificar qual a tecla pressionada e alterá-la. No entanto, e se a célula estiver a ser editada, é necessário fazer o override à ProcessCmdKey.

 

Fica um exemplo de como fazer:

 

 Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean

 

    ' Verifica se a tecla pressionada foi a ENTER e se a célula está a ser editada

    If keyData = Keys.Enter And Me.DataGridView1.IsCurrentCellInEditMode Then

 

      ' Executa um TAB

      SendKeys.Send("{TAB}")

      ' Ignora a tecla pressionada

      Return True

 

    Else

      ' Executa o processo normal

      Return MyBase.ProcessCmdKey(msg, keyData)

    End If

 

  End Function

 

 

  ' Quando é pressionada uma tecla

  Private Sub DataGridView1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown

 

    ' Verifica se a tecla é a ENTER

    If e.KeyCode = Keys.Enter Then

 

      ' Desabilita a tecla pressionada

      e.SuppressKeyPress = True

 

      With Me.DataGridView1

 

        ' Caso seja a ultima coluna salta para a primeira da próxima linha

        If .CurrentCell.ColumnIndex = .ColumnCount - 1 Then

 

          ' Caso não seja a última linha

          If .CurrentRow.Index < .RowCount - 1 Then

            .CurrentCell = .Item(0, .CurrentRow.Index + 1)

          End If

 

        Else

          ' Move para a próxima coluna

          .CurrentCell = .Item(.CurrentCell.ColumnIndex + 1, .CurrentRow.Index)

        End If

 

      End With

    End If

 

  End Sub

 

 

@@IDENTITY – Verificando Registo Inserido

 

Quando se inserem registos, é frequente necessitarmos do último número de identificação inserido (ID) para mostrarmos ao utilizador ou para utilizarmos na inserção de outros registos. Por exemplo se inserirmos um novo utilizador e de seguida usarmos o seu número de registo para inserir a sua lista de favoritos, temos de efectuar os seguintes passos:

 

1 – Inserir o registo do utilizador

2 – Verificar-mos qual o registo inserido (ID)

3 – Inserir os favoritos utilizando o ID recolhido

 

Para simplificar este processo podemos utilizar o comando T-SQL - @@IDENTITY - que retorna o valor gerado pelo SQL Statement. Ou seja, no momento que inserimos o registo, verificamos qual o número do registo inserido.

 

Fica aqui um exemplo de como simplificar este processo:

 

    ' Texto de ligação à base de dados

    Dim myConnectionString As String = _

        "Data Source=.\SQLEXPRESS;AttachDbFilename='c:\MYDATABASE.MDF';" & _

        ";Integrated Security=True;User Instance=True"

 

    ' Comando que irá inserir dados na tabela "MyTable" em que o campo "username" será

    ' passados através de parâmetros e retornará o ID do registo inserido

    Dim SQL As String = "INSERT INTO myTable([username]) VALUES (@username); Select @@IDENTITY;"

 

    ' Cria uma nova ligação à base de dados

    Dim connection As New SqlConnection(myConnectionString)

 

    ' Criação do comando indicando a instrução e a ligação

    Dim command As New SqlCommand(SQL, connection)

    command.Parameters.Add("@username", SqlDbType.VarChar).Value = "teste"

    connection.Open()

 

    ' Insere o registo e guarda na variável IdRegistoInserido o ID

    Dim IdRegistoInserido As Integer = command.ExecuteScalar()

    Debug.WriteLine("Registo Inserido: " + IdRegistoInserido.ToString)

 

    ' Fecha a ligação e limpa as variáveis

    connection.Close()

    connection = Nothing

    command = Nothing

 

 

Deste modo reduzimos um processo, de verificação do registo inserido, melhorando a performace da aplicação.

 

 

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


VB6: Posicionamento e Redimensionamento de Controlos

O VB.NET tem uma  propriedade nos controlos que permite-os redimensionar e posicionar no formulário através da opção Anchor. Este ancoramento permite que um controlo aumente/diminua ou desloque-se quando o formulário é redimensionado, melhorando bastante esteticamente e funcionalmente a aplicação.

O Visual Basic 6 não tem esta possibilidade de uma forma automática sendo necessário recorrer a cálculos para o fazer. Uma das opções mais comuns que os programadores utilizam para ultrapassar este processo demorado e normalmente complexo é retirar a possibilidade de aumentar o tamanho do formulário. Mas isto não é algo que o utilizador esteja habituado em ambiente Windows nem é algo que traga valor acrescentado à aplicação.

Uma das hipóteses que resolve este problema é utilizando as classes clsAutoPositioner.cls e clsAutoPositionerItem.cls. Estas classes são de código livre, encontram-se disponíveis na Internet e permitem efectuar com grande facilidade este processo. Podem ver o exemplo inicial (Figura 1) e o resultado final (Figura 2) .

 
   (Figura 1)



    (Figura 2)

Para as utilizarem é necessário criar duas classes com o seguinte código:

clsAutoPositioner.cls

‘ ***
‘ *** Início da classe clsAutoPositioner
‘ ***
Option Explicit

Dim m_oAssignments As New Collection

Public Function AddAssignment(ctl As Object, ctlRelative As Object, tPosType As tPOSITION_TYPE)

Dim x As New clsAutoPositionerItem
Set x.oCTL = ctl
Set x.oREL = ctlRelative
x.tPosType = tPosType

Select Case x.tPosType
    Case tCONTAINER_RELATIVE_POS_RIGHT
            x.lValue = x.oREL.Width - x.oCTL.Left
    Case tCONTAINER_RELATIVE_POS_BOTTOM
            x.lValue = x.oREL.Height - x.oCTL.Top
    Case tCONTAINER_WIDTH_DELTA_RIGHT
            x.lValue = x.oREL.Width - (x.oCTL.Left + x.oCTL.Width)
    Case tCONTAINER_HEIGHT_DELTA_BOTTOM
            x.lValue = x.oREL.Height - (x.oCTL.Top + x.oCTL.Height)
    Case tCONTROL_RELATIVE_SAME_POS_VERTICAL
            x.lValue = x.oCTL.Left - x.oREL.Left
    Case tCONTROL_RELATIVE_SAME_POS_HORIZONTAL
            x.lValue = x.oCTL.Top - x.oREL.Top
End Select

m_oAssignments.Add x

End Function

     

Public Function RefreshPositions()
Dim i As Long
Dim x As clsAutoPositionerItem
Dim erg As Long

For i = 1 To m_oAssignments.Count
    Set x = m_oAssignments.Item(i)

    Select Case x.tPosType
        Case tCONTAINER_RELATIVE_POS_RIGHT
            erg = x.oREL.Width - x.lValue
            If (erg > 0) Then x.oCTL.Left = erg
        Case tCONTAINER_RELATIVE_POS_BOTTOM
            erg = x.oREL.Height - x.lValue
            If (erg > 0) Then x.oCTL.Top = erg
        Case tCONTAINER_WIDTH_DELTA_RIGHT
            erg = x.oREL.Width - x.oCTL.Left - x.lValue
            If (erg > 0) Then x.oCTL.Width = erg
        Case tCONTAINER_HEIGHT_DELTA_BOTTOM
            erg = x.oREL.Height - x.oCTL.Top - x.lValue
            If (erg > 0) Then
                x.oCTL.Height = erg
            Else
                erg = erg
            End If           

        Case tCONTROL_RELATIVE_SAME_POS_VERTICAL
            erg = x.oREL.Left + x.lValue
            x.oCTL.Left = erg
        Case tCONTROL_RELATIVE_SAME_POS_HORIZONTAL
            erg = x.oREL.Top + x.lValue
            x.oCTL.Top = erg
    End Select
Next

End Function     

clsAutoPositionerItem.cls

‘ ***
‘ *** Início da classe clsAutoPositionerItem
‘ ***
Public Enum tPOSITION_TYPE
    tCONTAINER_RELATIVE_POS_RIGHT
    tCONTAINER_RELATIVE_POS_BOTTOM
    tCONTAINER_WIDTH_DELTA_RIGHT
    tCONTAINER_HEIGHT_DELTA_BOTTOM
    tCONTROL_RELATIVE_SAME_POS_VERTICAL
    tCONTROL_RELATIVE_SAME_POS_HORIZONTAL
End Enum

Public oCTL As Object
Public <?xml namespace="" ns="urn:schemas-microsoft-com:office:smarttags" prefix="st1" ?>oRELAs Object
Public tPosType As tPOSITION_TYPE
Public lValue As tPOSITION_TYPE



Para as implementar, e após a criação destas classes num projecto, deverão utilizar o seguinte código (com as adaptações necessárias):

‘ Declaração de uma variável a nível do form privada
Private m_oAutoPos As New clsAutoPositioner     

‘ No evento load do form define-se quais os controlos e que são afectados
‘ e que acção é efectuada (posicionamento e/ou redimensionamento)
Private Sub Form_Load()

  With m_oAutoPos
  ‘ posição relativo à direita
  .AddAssignment Me.Command1, Me, tCONTAINER_RELATIVE_POS_RIGHT

   ' Redimensionamento horizontal
  .AddAssignment Me.Command2, Me, tCONTAINER_WIDTH_DELTA_RIGHT

  ‘ Redimensionamento vertical
 .AddAssignment Me.Command3, Me, tCONTAINER_HEIGHT_DELTA_BOTTOM

 ' Posição relativo em baixo
.AddAssignment Me.Command4, Me, tCONTAINER_RELATIVE_POS_BOTTOM

   ' Redimensionamento horizontal e vertical
 .AddAssignment Me.Command5, Me, tCONTAINER_WIDTH_DELTA_RIGHT
 .AddAssignment Me.Command5, Me, tCONTAINER_HEIGHT_DELTA_BOTTOM
 End With

End Sub

‘ Quando é alterado o tamanho do form
Private Sub Form_Resize()
    m_oAutoPos.RefreshPositions
End Sub

‘ Limpa da memória a variável
Private Sub Form_Unload(Cancel As Integer)
    Set m_oAutoPos = Nothing
End Sub

Estas classes e este processo permitem melhorar a aplicação e dar ao utilizador algo que normalmente está habituado em ambiente Windows. É verdade que no VB.NET esta funcionalidade está disponível através de uma das propriedades dos controlos, mas no VB6, e com poucas linhas de código, pode-se também implementar.


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


VB.NET: Personalizando o CrystalReportViewer

Os relatórios são parte importante de uma aplicação e a utilização dos Crystal Reports, disponíveis na maioria nas versões do Visual Studio 2005, ajuda muito neste processo. É uma ferramenta poderosa, embora com diversas limitações na versão .NET, mas mesmo assim com inúmeras possibilidades e simplicidade de utilização.

O CystalReportViewer é uma ferramenta do Crystal Report que permite pré-visualizar o relatório antes de ser impresso ou exportado para diversos formatos disponíveis, como é o caso de *.pdf, *.xls, *.doc, etc. No entanto o CystalReportViewer é totalmente em Inglês e não existe a possibilidade de modifica-lo em design mode ou modo de edição.

O CystalReportViewer é composto pelos seguintes componentes:

     ReportGroupTree – Árvore de relatórios
     PageView – Onde se encontra o relatório
     Splitter – Divisão entre o PageView e ReportGroupTree
     StatusBar – Barra onde são mostradas algumas informações sobre o relatório
     ToolStrip – Barra de Ferramentas onde se encontram os botões

alt

Em modo runtime ou quando o programa está a executar, é possível personalizar grande parte dos componentes do CystalReportViewer. Ficam aqui alguns exemplos de personalizações possíveis, mostrando um pouco o que se pode e como se pode modificá-lo.

Esconder o botão “Main Menu”

Os relatórios aparecem por defeito com um TabControl e com uma TabPage por cada relatório visível. Estas TabPages servem para selecionar o relatório mas na grande maioria das vezes existem apenas um relatório. Nestes casos podemos “esconder” o botão e embora não seja possível na realidade esconder, é possível reduzir o tamanho de modo a este não ser visível.

 

        ' Faz um ciclo em todos os controlos do CrystalReportViewer1
        For Each ctrl As Control In Me.CrystalReportViewer1.Controls

            ' Se o controlo for do tipo PageView, ou seja, onde
            ' se encontra os relatórios e o TabControl
            If TypeOf ctrl Is PageView Then   

                ' Atribui a uma variável o primeiro controlo
                Dim tabCtrl As TabControl = CType(ctrl.Controls(0), TabControl)

                ' Define o tamanho como fixo
                tabCtrl.SizeMode = TabSizeMode.Fixed

                ' Define o tamanho em altura para 1
                tabCtrl.ItemSize = New Size(0, 1)

                Exit For

            End If

        Next

Alterar o nome do botão “Main Menu”

Este exemplo é semelhante ao anterior, apenas mudando o tipo de controlo. Desta vez, e para alterar o nome de “Main Menu” para outro à nossa escolha, devemos selecionar uma TabPage do TabControl.

        ' Faz um ciclo em todos os controlos do CrystalReportViewer1
        For Each ctrl As Control In Me.CrystalReportViewer1.Controls

            ' Se o controlo for do tipo PageView, ou seja, onde
            ' se encontra os relatórios e o TabControl
            If TypeOf ctrl Is PageView Then

                ' Atribui a uma variável o primeiro controlo da primeira TabPage
                Dim tabPag As TabPage = CType(ctrl.Controls(0), TabControl).TabPages(0)

                ' Altera o nome visível
                tabPag.Text = "Relatório"

                Exit For

            End If

        Next

   

Adicionar controlos à Barra de Ferramentas

De modo a personalizar o CystalReportViewer é possível adicionar controlos, remove-los, modificar os existentes, etc. Este exemplo mostra com inserir um ToolStripButton e uma ToolStripLabel à barra de ferramentas.

        For Each ctrl As Control In Me.PtCrystalReportViewer2.Controls
            If TypeOf ctrl Is Windows.Forms.ToolStrip Then

                Dim ts As ToolStrip = CType(ctrl, ToolStrip)

                ' Cria um novo botão e define algumas propriedades
                Dim btnSave As New ToolStripButton
                btnSave.Text = "Gravar no Disco"
                btnSave.BackColor = Color.SeaGreen
                btnSave.ForeColor = Color.White

                ' Adiciona o botão
                ts.Items.Add(btnSave)

                ' Cria uma nova label e define o texto
                Dim lblLink As New ToolStripLabel
                lblLink.Text = "http://vbtuga.blogspot.com"

                ' Define como link e alinha-a à direita
                lblLink.IsLink = True
                lblLink.Alignment = ToolStripItemAlignment.Right

                ' Adiciona a ToolStripLabel
                ts.Items.Add(lblLink)

                ' Define quando for efectuado um clique em cada
                ' um dos objectos irá ser executado o sub DoWork
                AddHandler btnSave.Click, AddressOf DoWork
                AddHandler lblLink.Click, AddressOf DoWork

                Exit For

            End If

        Next

Depois para executar o código quando for clicado nos novos controlos:

    ' Verifica qual controlo em que foi efectuado o clique
    Private Sub DoWork(ByVal sender As System.Object, ByVal e As System.EventArgs)

        If TypeOf sender Is ToolStripButton Then

            ' Se for no botão 
            Debug.WriteLine("Pressionado o botão 'Gravar'")

        ElseIf TypeOf sender Is ToolStripLabel Then

            ' Se for no label
            Debug.WriteLine("Carregado no link ...")

        End If

    End Sub

   

CystalReportViewer personalizado em Português

Finalmente, e utilizando um pouco do que foi mostrado anteriormente, e incluindo mais algumas funcionalidades como renomear todos os ToolTips (textos de ajuda), remover algumas opções, criar uma nova barra de estado em Português (indicação da página e total de páginas) são algumas das alterações que estão efectuadas.

Existem ainda algumas personalizações possíveis em design mode como o StatusBarStyle e StatusBarStyle (System, Professional) e DisplayMainMenu, que permite esconder o botão “Main Menu” quando o relatório for iniciado.

Podem testar, verificar o código, solicitar ou apresentar melhorias ao controlo personalizado:

          Dowload (inclui dll e projecto de demonstração): CrystalReportViewerPT

Nota: Este controlo encontrasse em testes e é apenas uma demonstração, pelo que deverá ser testado e validado antes da sua utilização.

 

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