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

Mostrar mensagens com a etiqueta Visual Studio 2008. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta Visual Studio 2008. Mostrar todas as mensagens

VB.NET: Microsoft Reporting Services

Os Microsoft Reports são baseados na definição do relatório, que é na realidade um ficheiro XML, que descreve a informação e o layout do relatório, com uma extensão diferente. Pode-se criar um relatório no cliente – Client-Side Report Definition Language (*.rldc) – usando o Visual Studio, e construir excelentes relatórios para a aplicação.

O objectivo deste artigo é mostrar os passos básicos para a criação de um relatório, definir a Data Source em modo runtime, trabalhar com Parameters, incluir imagens, usar o Expression Editor, e como alimentar um SubReport e um gráfico. Finalmente irá também mostrar algumas personalizações simples ao controlo ReportViewer.

Introdução

Para começar, foi criada uma tabela em SQL com alguma informação para este exemplo. É uma lista de equipamento electrónico como PDA, computadores, etc. Da tabela criada é necessário criar um DataSet, com duas DataTables. Chamar a este DataSet “dsReport.xsd”.

A primeira DataTable, “products”, contém a lista de todos os produtos na tabela SQL. A segunda DataTable, “groupTotal”, é uma vista agrupada (Group By) com os grupos e os somatórios das quantidades. que serão usados para o SubReport e para o gráfico.

Depois disto é necessário adicionar o relatório à aplicação. Seleccionar o relatório e indicar um nome apropriado. Para o exemplo podem utilizar “rptProducts”.

Com o novo relatório criado e aberto, existe uma nova opção na barra de ferramentas Report. Seleccionar o “Page Header” e “Page footer” para mostrar estas duas secções no relatório. Depois disto vamos arrastar da Toolbox para a secção “Body” (corpo) do relatório.

DataSource

Agora é altura para definir a Data Source. No menu Report, seleccionar Data Source. Irá aparecer uma nova janela onde se pode escolher as Data Sources (DataSets) disponíveis na nossa aplicação. Seleccionar a DataSet criada.

NOTA: O nome do “Report Data Sources” pode ser renomeado, e será usado no código mais tarde.

Depois de definir a Data Source para o relatório, as DataTables estarão disponíveis no Data Source Explorer (normalmente disponível na janela da Soluction Explorer). Da tabela Produtos arrastar as colunas para o objecto Table que foi inserido na secção “Body”


O objecto Table funciona como uma folha de Excel. Pode-se unir células, alterar a cor de fundo, fontes, etc. Este exemplo utiliza um campo do tipo Moeda. Para mostrar como é simples formatar as células, clicar com o botão direito do rato sobre o campo e seleccionar Propriedades. Na janela das propriedade ir à Tab Format e definir a célula com o formato Currency.

 

Alterar o formato dos restantes campos, como negrito, títulos, etc., para obter um aspecto profissional.

Imagens

Para incluir imagens na nossa aplicação, pode-se embeber e depois usar como um logótipo (por exemplo). Pode-se também ir buscar directamente as imagens à base de dados, se estiverem armazenados como binário (Binary).

Para embeber as imagens no relatório é apenas necessário ir ao menu Report e seleccionar Embedded Images. Na nova janela seleccionar o botão New Image e escolher a imagem pretendida.


Fechar a janela e depois, na Toolbox, adicionar um controlo Image ao relatório. Nas propriedades do controlo, seleccionar a imagem adicionada da Combobox disponível na propriedade Value.

Expression Editor

Uma das novidades do Visual Studio 2008 para o Microsoft Reporting Services Expression Editor é o intellissense disponível quando se precisa de criar alguma fórmula ou expressão.

Na secção Footer, pode-se adicionar duas TextBoxes. Numa delas, iremos colocar o número da pagina e o total de páginas. Para isso, já existem fórmulas criadas na categoria Global.


Como se pode ver na imagem seguinte, pode-se usar fórmulas de VB (a maioria delas) no Expression Editor. O intellissence ajuda bastante para prevenir erros e para lembrar o sintaxe das fórmulas . Na segunda Textbox vamos colocar simplesmente a data de impressão.

 

Parâmetros (Parameters)

Pode-se utilizar Parameters para diversas coisas. Neste exemplo será apenas usado para passar informação da aplicação para o relatório.

No menu Report, seleccionar Report Parameters. Definir dois parâmetros: “ApplicationUser” e “ApplicationLevel” do tipo String.

Depois, no relatório, pode-se usar o Expression Editor para definir os parâmetros (eles serão definidos no código) para as TextBoxes. Eles estarão disponíveis na categoria Parameters.

Gráficos (Chart)

Pode-se usar vários gráficos no relatório e eles são muito fáceis de usar e alimentar com informação.

Adicionar um controlo Chart da Toolbox para a secção Body do relatório. Efectuar um duplo-clique no gráfico e irá mostrar as áreas “Data Field” e “Category Field”.

Arrastar os campos “Group” e Totals” da janela DataSource Explorer.


Pode-se também fazer isto nas propriedades do gráfico. Nesta janela pode-se também personalizar as cores das séries, legendas, efeitos 3D, filtros, eixos, etc.

Usando o código

Nos passos anteriores, foram usadas diversas Data Sources no relatório. Agora, iram ser adicionadas novas Data Sources, filtradas ou não, e essa será a informação que será visível no relatório.

Imports System.Data.SqlClient
Imports Microsoft.Reporting.WinForms

Public Class Form1

    ' Connection string
  
Private connString As String = _ 
"DataSource=.\SQLEXPRESS;AttachDbFilename='|DataDirectory|\myDatabase.mdf'”_
";
Integrated Security=True;User Instance=True"

  
''' <summary>
  
''' Evento Form load
    '''
</summary>
  
Private Sub Form1_Load(ByVal sender As Object, _
                                   ByVal e As System.EventArgs) Handles Me.Load

        Try

          
' Chama a personalização
          
Call customizeReportViewer(Me.ReportViewer1)

            ' Adiciona um novo botão
          
Call AddReportViewerButton()

            With Me.ReportViewer1.LocalReport

                ' Caminho para o relatório
              
.ReportPath = Application.StartupPath & "\..\..\rptProducts.rdlc"
              
.DataSources.Clear()

                ' Define os parameters
              
Dim parameters(1) AsReportParameter
                parameters(0) = _
                         New ReportParameter("ApplicationUser", "jpaulino")
                parameters(1) = _
                         New ReportParameter("ApplicationLevel", "Administrator")

                .SetParameters(parameters) 
            End With

          
' ----------------------------------------------------
            ' Datasource para o relatório principal (where price > 200)
            ' ----------------------------------------------------
          
Dim SQL As String = "SELECT * FROM products WHERE price > @price"
          
Using da As New SqlDataAdapter(SQL, connString)
                da.SelectCommand.Parameters.Add("@price", SqlDbType.Int).Value = 200

                Using ds As New DataSet
                    da.Fill(ds, "products")

                    ' É preciso usar o mesmo nome como foi de definido 
                    ' no Data Source Definition
                  
Dim rptDataSource As New ReportDataSource _
                                          ("dsReport_products", ds.Tables("products"))
                    Me.ReportViewer1.LocalReport.DataSources.Add(rptDataSource)

                End Using

            End Using

          
' ----------------------------------------------------
            ' Datasource para o gráfico
            ' ----------------------------------------------------
          
Dim SQL_Chart As String = "SELECT [group], SUM(quantity) AS " _ 
                   “
Total FROM products GROUP BY [group]"
          
Using da As New SqlDataAdapter(SQL_Chart, connString)
                Using ds As New DataSet
                    da.Fill(ds, "groupTotal")

                    Dim rptDataSource As New ReportDataSource("dsReport_groupTotal", _
    ds.Tables("groupTotal"))
                    Me.ReportViewer1.LocalReport.DataSources.Add(rptDataSource)

                End Using
            End Using


          
' Refresh do relatório
          
ReportViewer1.RefreshReport()


        Catch ex As Exception
            MessageBox.Show(ex.Message, My.Application.Info.Title, _
                                              MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

    End Sub

End Class

SubReports

Pode-se usar um ou vários subreports no relatório. Para fazer isso, é necessário preencher a Data Source para cada um deles. Uma vez que não se pode aceder directamente à Data Source do relatório, é preciso definir um evento (handler) ao SubreportProcessing, e depois quando o relatório está a ser carregado, recolhe-se a informação da base de dados e preenche-se a Data Source.

Pode parecer difícil de fazer, mas na verdade é bastante simples!

Definir a Data Source para o subreport e usar um objecto Table, como no relatório principal. Adicionar os campos ao objecto Table. No relatório principal, adicionar um objecto SubReport da Toolbox e escolher o subreport criado.

Depois, no evento Form Load onde está o relatório principal, adicionar o evento para o SubreportProcessing.


AddHandler
ReportViewer1.LocalReport.SubreportProcessing, _
                  AddressOf SubreportProcessingEvent

Finalmente, no evento SubreportProcessing, define-se a nova Data Source, da mesma maneira dos anteriores.


 ''' <summary>
''' Quando o subreport está a ser carregado/processado, preenche a DataSource
''' 
</summary>
Sub SubreportProcessingEvent(ByVal sender As Object, _
                                         ByVal e As SubreportProcessingEventArgs)

     Try

         Dim
SQL As String = "SELECT [group], SUM(quantity) AS " _
             “
Total FROM products GROUP BY [group]"
        
Using da As New SqlDataAdapter(SQL, connString)
             Using ds As New DataSet
                 da.Fill(ds, "groupTotal")
                 Dim rptDataSource As New ReportDataSource _
                                           ("dsReport_groupTotal", ds.Tables("groupTotal"))
                 e.DataSources.Add(rptDataSource)
             End Using
         End Using

     Catch
ex As Exception
         MessageBox.Show(ex.Message, My.Application.Info.Title, _ 
                                      MessageBoxButtons.OK, MessageBoxIcon.Error)
     End Try

End Sub

Personalização do ReportViewer

O ReportViewer é o controlo que mostra os Microsoft Reports num Form ou numa Webpage. Pode-se personalizar a maioria dos controlos deste, para melhorar ou personalizar como pretendido.

Neste artigo, será mostrado duas personalizações simples.

Primeiro, pode-se alterar os títulos dos controlos. Pode-se também fazer outras coisas, como desabilitar itens, renomear tooltips, etc. Isto é especialmente interessante para alterar a linguagem do controlo, uma vez que está disponível apenas em Inglês.

Para isto, será criado um método recursivo que irá fazer um ciclo em todos os controlo do ReportViewer e fazer algumas alterações.

'''  <summary>
''' Ciclo em todos os controlos do ReportViewer
'''
</summary>
''' <remarks></remarks>
Sub customizeReportViewer(ByVal ctrl AsControl)

    For Each c As Control In ctrl.Controls

        ' ----------------------------------------------------
        ' Verifica o texto das labels disponíveis 
        ' ----------------------------------------------------
      
If TypeOf c Is Label Then
            Dim
lbl As Label = DirectCast(c, Label)
            Select Case lbl.Name
                Case "LblGeneratingReport"
                  
lbl.Text = "My report is loading now ... "
              
Case Else
                  
' Podem ser adicionadas mais personalizações
          
End Select
        End If

      
' ----------------------------------------------------
        ' Altera os textos da ToolStrip para Português
        ' ----------------------------------------------------
      
If TypeOf c Is ToolStrip Then
            Dim
ts AsToolStrip = DirectCast(c, ToolStrip)
            For Each item AsToolStripItem In ts.Items

                Select Case item.Text
                    Case "Find"
                      
item.Text = "Pesquisar"
                  
Case "Next"
                      
item.Text = "Próximo"
                  
Case "of"
                       
item.Text = "de"
                    
Case Else
                       
' Podem ser adicionadas mais personalizações
              
End Select
            Next
        End If

      
' Se o controlo tiver child controls
       
If c.HasChildren Then
          
customizeReportViewer(c)
        End If

    Next

End Sub

Finalmente é apenas necessário chamar o método criado no evento Form Load, onde se encontra o ReportViewer.

   ' Chama o método criado
Call customizeReportViewer(Me.ReportViewer1)

A segunda personalização mostra como incluir um novo botão na Toolstrip. Irá encontrar a ToolStrip principal do relatório e adicionar um botão alinhado à direita.

'''  <summary>
'''  Encontra a ToolStrip principal e adiciona um novo botão alinhado à direita
'''
</summary>
Sub AddReportViewerButton()

    Dim ts() As Control = Me.ReportViewer1.Controls.Find("toolStrip1", True)
    If ts IsNot Nothing Then
        Dim
tsItem As ToolStrip = DirectCast(ts(0), ToolStrip)
        Dim item As New ToolStripButton
        item.Name = "newButton"
       
item.Text = "New Button"
       
item.BackColor = Color.Green
        item.ForeColor = Color.White
        item.Alignment = ToolStripItemAlignment.Right

        tsItem.Items.Add(item)

        AddHandler item.Click, AddressOf newButtonClick
   End If

End Sub

Quando o botão for pressionado:

'''  <summary>
''' Mostra uma mensagem quando o botão no ReportViewer for pressionado
''' 
</summary>
Sub newButtonClick()
    MessageBox.Show("This is my new button!")
End Sub


Este artigo mostra os primeiros passo, e os mais importantes, para usar os Microsoft Reports, e como utilizar o código. Espero que isto ajude a utiliza-los e mostrar como é simples trabalhar com eles.


Download do Exemplo

VB.NET: AndAlso, OrElse e Using

A utilização de operadores lógicos é tão comum que um pouco por todo o código se usam operadores como o And ou o Or . No entanto o VB.NET tem novos operadores, já disponíveis nas primeiras versões .NET, que muitos não usam por desconhecimento da sua real funcionalidade.

Os operadores AndAlso e OrElse, são designados short-circuiting operators, ou seja, são operadores de rápida validação ou de circuito reduzido.

Vejamos o funcionamento de uma validação:

CONDIÇÃO 1 CONDIÇÃO 2 RESULTADO
True True True
False True False
True False False
False False False

Ou seja, qualquer condição a False irá produzir um resultado False.

O que o AndAlso faz é quando encontrar uma condição a False, decide logo e não executa a condição seguinte. Isto pode reduzir bastante, dependendo das condições, o tempo de execução da aplicação.

A seguinte função leva algum tempo a executar e retorna o resultado (True ou False) indicado no parâmetro. Serve apenas para mostrar os diferentes resultados, na utilização de diferentes operadores, nos exemplos seguintes.

''' <summary>
''' Efectua um ciclo retornando o resultado indicado no parâmetro
'''
</summary>
Private Function doTestLoop(ByVal result As Boolean) As Boolean
    For
x As Byte = 0 To 200
        Threading.Thread.Sleep(5)
        Application.DoEvents()
    Next

   
Debug.WriteLine("Executado às " & Now.ToString)
    Return result
End Function

Este exemplo executa duas vezes a função doTestLoop() e irá depois parar na condição falsa


If
doTestLoop(False) And doTestLoop(False) Then
    Stop
' Condição verdadeira
Else
    Stop
' Condição falsa
End If

Usando o operador AndAlso, e no mesmo exemplo, irá ser executado apenas a primeira condição e salta para a condição falsa, reduzindo o tempo de ciclo.

If doTestLoop(False) AndAlso doTestLoop(False) Then
    Stop
' Condição verdadeira
Else
    Stop
' Condição falsa
End If

Isto permite que se criem validações, apenas em uma linha, sem erros indesejados.

Por exemplo, o seguinte código tenta encontrar um Form na aplicação que não existe, ficando a variável frm a Nothing. Neste caso é verificada a primeira condição, que é falsa, e sai da validação, mas se utilizássemos o operador And, iria ser feita a primeira condição e depois a segunda, originando um erro "Object reference not set to an instance of an object".


Dim
frm As Form = My.Application.OpenForms("Nome não Existente")
If frm IsNot Nothing AndAlso frm.Text = "Nome Pretendido" Then
    Stop
' Condição verdadeira
End If

O mesmo acontece com o operador OrElse. Se encontrar uma condição verdadeira ele decide, sem ter de executar a próxima.

A validação de uma condição Or funciona desta forma, ou seja, qualquer condição verdadeira irá ter um resultado verdadeiro:

CONDIÇÃO 1 CONDIÇÃO 2 RESULTADO
True True True
False True True
True False True
False False False

Neste exemplo simples será executado apenas a primeira condição, saltando logo para a condição verdadeira:


If doTestLoop(True) OrElse doTestLoop(True) Then
    Stop
' Condição verdadeira
End If


Resumindo: são dois operadores que podem e devem ser utilizados, especialmente se as condições/validações podem levar algum tempo a executar. Podem ainda simplificar o código, evitando-se utilizar If’s dentro de If’s para se conseguir o mesmo resultado.


O bloco Using é uma novidade da Visual Studio 2005/.NET Framework 2.0 e permite criar um bloco, onde no final, os recursos utilizados são libertados. Isto é muito útil e prático, especialmente quando se utilizam unmanaged resources, como ligações a base de dados, ligações a ficheiros de texto, objectos COM, etc.

Os unmanaged resources, são recursos que o CLR (Common Language Runtime) não liberta e que ficam a consumir desnecessariamente recursos do sistema. Os managed resources, por sua vez, são geridos pelo CLR através do .NET Framework Garbage Collector (GC).

No seguinte exemplo, mesmo que aconteça um erro, a ligação à base de dados é fechada e limpa, porque o Using faz o .Disposal() automaticamente sem código adicional.

Dim myConnectionString As String = String.Empty

Using conn As New SqlConnection(myConnectionString)
    conn.Open()

    Using command As New SqlCommand("SELEC * FROM myTable", conn)
        command.ExecuteNonQuery()
    End Using

End Using

Como se pode ver neste exemplo simples, é declarada a variável, usada dentro do bloco e finalmente faz o Disposal da variável.


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


VB.NET: MergedDataGridView Control

Este artigo foi originalmente escrito para o site www.codeproject.com e agora traduzido para Português. Deste modo não terá o formato normal dos artigos disponíveis neste blog. Se quiserem ver o artigo original ou simplesmente votar, podem fazê-lo no seguinte endereço: MergedDataGridView Control 

 

Introdução
Como todos sabemos, o controlo DataGridView não nos permite juntar células e se pensarmos um pouco nisso podemos perguntar: "Porque motivo ?". Bem, a DataGridView está ligada a uma fonte de dados (mesmo que não tenhamos definido) e cada célula representa um campo de um registo, então a que registo essa célula unida pertenceria? Provavelmente foi por causa disto que a Microsoft não incluiu esta funcionalidade.

Mas às vezes podemos querer mostrar alguma informação adicional e a única solução é criando uma mensagem/formulário ou então “roubar” algum espaço ao form e preencher com textboxes, combo’s, etc

O objectivo desta personalização é mostrar alguma informação adicional numa DataGridView. Basicamente usa uma RichTextBox que é inserida na grelha e redimensionada de acordo com a linha dependente. Esta linha dependente tem de ter um número único, que servirá para  o nome da RichTextBox. Isto será então usado para redimensionar e posicionar a RichTextBox no sítio correcto.

Também inclui alguma animação de ícones e personalização da grelha para melhorar o aspecto final.

Usando o Código

Para começar é necessário incluir a classe MergedDataGridView.vb na aplicação. Depois de compilar o projecto, o controlo MergedDataGridView estará disponível na toolbox. Depois, é só arrastar para o form.

De seguida, é necessário definir algumas propriedades da MergedDataGridView:

With Me.MergedDataGridView1

    ' Define a datasource
   
.DataSource = ds.Tables(0).DefaultView

    '  Definições personalizadas para a RichTextBox
   
.StartColumnIndex = 1
    .EndColumnIndex = 7
    .RowHeight = 60

    ' Definições personalizadas para a DataGridView
   
.AllowUserToAddRows = False
   
.AllowUserToDeleteRows = False

   
.RowsDefaultCellStyle.BackColor = Color.White
    .AlternatingRowsDefaultCellStyle.BackColor = Color.AliceBlue
    .AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells)

End With

Depois disto pode-se incluir duas DataGridViewImageColumns que serão usadas para mostrar a RichTextBox e para mostrar uma MessageBox com a informação.

' Cria uma coluna de imagens na datagrid que irá abrir a linha unica
Dim ImageColumn1 As New DataGridViewImageColumn
ImageColumn1.DefaultCellStyle.Alignment = DataGridViewContentAlignment.TopCenter
ImageColumn1.Image = My.Resources.DownArrow
ImageColumn1.Width = 25

' Cria uma coluna de imagens na datagrid que irá mostrar uma msgbox
Dim ImageColumn2 As New DataGridViewImageColumn
ImageColumn2.DefaultCellStyle.Alignment = DataGridViewContentAlignment.TopCenter
ImageColumn2.Image = My.Resources.Info
ImageColumn2.Width = 25

' Adiciona as duas colunas à DataGridView
Me.MergedDataGridView1.Columns.AddRange(New DataGridViewImageColumn() {ImageColumn1, ImageColumn2})


Finalmente, no evento CellMouseClick, detecta-se se o utilizador carregou na coluna correcta, e se sim, adiciona-se uma nova linha:

' Adiciona uma nova linha na posição correcta (e.RowIndex + 1)
Dim rowPos As Integer = e.RowIndex + 1

Dim dv AsDataView = Me.MergedDataGridView1.DataSource
Dim row AsDataRow = dv.Table.NewRow()
dv.Table.Rows.InsertAt(row, rowPos)

' Recolhe o texto das colunas escondidas que serão usadas para preencher a RichTextBox
Dim mergedRowText As New System.Text.StringBuilder
mergedRowText.AppendLine(Me.MergedDataGridView1("Description", e.RowIndex).Value.ToString)
mergedRowText.AppendLine(Me.MergedDataGridView1("Link", e.RowIndex).Value.ToString)

' Chama o método AddMergedRow
Me.MergedDataGridView1.AddMergedRow(rowPos, mergedRowText.ToString)

Ou apaga-a:

' Remove a linha da datasource
Dim rowPos As Integer = e.RowIndex + 1

Dim dv AsDataView = Me.MergedDataGridView1.DataSource
dv.Table.Rows.RemoveAt(rowPos)

' Chama o método RemoveMergedRow
Me.MergedDataGridView1.RemoveMergedRow(Me.MergedDataGridView1(0, e.RowIndex).Value)


O restante código usado no form, como podem ver no exemplo em anexo, é para validações, tratamento de erros e animação geral.

Olhando para o Controlo

O controlo tem apenas dois métodos: AddMergedRow e RemoveMergedRow, e três propriedades. As propriedades apenas guardam a indicação da coluna onde a RichTextBox irá começar e terminar, e da altura que terá.

O AddMergedRow olha para o número (ID) da linha anterior, que será a linha dependente, e cria uma nova nova RichTextBox na nova linha, usando o ID como o nome.

''' <summary>
''' Adiciona a nova linha com uma célula unida usando a RichTextBox
'''
</summary>
'''<param name="rowIndex">Index onde a linha será adicionada</param>
'''<param name="cellText">Texto que irá ser mostrado na RichTextBox</param>
'''<remarks></remarks>
Public Sub AddMergedRow(ByVal rowIndex As Integer, ByVal cellText As String)

    Try

        Me
.SuspendLayout()

        ' Define a localização/tamanho da RichTextBox
      
Dim x As Integer = Me.GetColumnDisplayRectangle(Me.StartColumnIndex, False).Left + 1
        Dim y As Integer = Me.GetRowDisplayRectangle(rowIndex, False).Top
        Dim w As Integer = Me.GetColumnDisplayRectangle(Me.EndColumnIndex, False).Right - x - 2
        Dim h As Integer = Me.GetRowDisplayRectangle(rowIndex, False).Size.Height - 1

        ' Verifica o ID da linha anterios, que será usado como nome da RichTextBox
      
Dim parentRowID As Integer = Me(0, rowIndex - 1).Value


        ' Cria a nova RichTextBox e coloca-a na posição correcta
      
Dim rtb As New RichTextBox
        With rtb
            .Name = parentRowID
            .Text = cellText
            .Multiline = True
          
.BorderStyle = BorderStyle.None
            .ScrollBars = ScrollBars.Vertical
            .ReadOnly = True
          
.Font = New Font(Me.DefaultCellStyle.Font, Me.DefaultCellStyle.Font.Style)
            .SetBounds(x, y, w, h)
        End With

        Me
.Controls.Add(rtb)


        ' Define para a RichTextBox a mesma cor que a da linha
       
rtb.BackColor = Me(0, rowIndex).InheritedStyle.BackColor

        ' Define a altura da linha
       
Me.Rows(rowIndex).Height = Me.RowHeight

        ' Define a nova imagem para a coluna (seta para cima)
       
Dim arrow As DataGridViewImageCell = Me(Me.ColumnCount - 2, rowIndex - 1)
        arrow.Value = My.Resources.UpArrow


    Catchex AsException
        Throw NewArgumentException(ex.Message)
    Finally
        Me
.ResumeLayout()
    End Try

End Sub

O segundo método, RemoveMergedRow, verifica o ID da linha anterior e remove a RichTextBox da grelha.

''' <summary>
''' Remove a RichTextBox da DataGridView
'''
</summary>
''' <param name="rowID">ID da linha</param>
''' <remarks></remarks>
Public Sub RemoveMergedRow(ByVal rowID As Integer)

    Try

       
' Procura o controlo na DataGridView e remove-o
       
Dim ctrl() As Control = Me.Controls.Find(rowID.ToString, False)
        If ctrl.Length = 1 Then
            Me
.Controls.Remove(ctrl(0))
        End If

       
' Define a nova imagem para a imagecell (seta para baixo)
       
Dim arrow As DataGridViewImageCell = Me(Me.ColumnCount - 2, Me.CurrentRow.Index)
        arrow.Value = My.Resources.DownArrow


    Catch ex As Exception
        Throw New ArgumentException(ex.Message)
    Finally
        Me
.ResumeLayout()
    End Try

End Sub

No evento Paint define-se a posição da RichTextBoxes a ajusta-se o tamanho.

Uma vez que não é simples de calcular a posição, de modo e deixar a linha em branco por baixo da linha mãe depois de ordenar a lista, foi desabilitada a ordenação no evento ColumnAdded


Pontos de Interesse

Este controlo não só possibilita mostra informação adicionar na DataGridView como também demonstra como personalizar a grelha e manipular o posicionamento nas linhas, que poderá ser útil em outros projectos.

Eu realmente espero que isto ajude a melhorar os vossos projecto ou ajudar a ter algumas novas ideias.

Download do Exemplo


VB.NET: Extraindo executáveis dos Resources

Os resources de uma aplicação permitem guardar diversa informação, como ícones, imagens, etc. Essa informação é usada normalmente dentro da própria aplicação e simplifica no desenvolvimento do código, pois temos acesso a ela através do intellissense.

No entanto, e por diversos motivos, podemos querer guardar dentro do próprio executável outro ficheiro e extrai-lo para, por exemplo, criar um instalador personalizado.


Para se conseguir isto é apenas necessário criar um novo projecto, ir às propriedades do projecto (My Properties ou menu Project – «Nome da Aplicação» Properties) e no separador Resources adicionar o executável pretendido.

Depois, no Solution Explorer, selecciona-se o executável e na janela de propriedades (Properties Window) definir na Build Action como Embedded Resource.

Depois, no código, pode-se usar uma função semelhante a esta, que irá ler o executável dos resources usando Reflection para uma Stream, e que irá depois gravar para o disco usando um FileStream.

''' <summary>
''' Extrai um ficheiro executável dos resources da aplicação
'''
</summary>
'''<param name="fileName">Nome completo do ficheiro a extrair</param>
'''<returns>Localização do ficheiro extraido</returns>
'''<remarks></remarks>
Private Function GetResourceFile(ByVal fileName As String) As String

    Try

      
' Cria um nome/localização temporária para o ficheiro. Por defeito é criado um
        ' ficheiro com o a extensão *.tmp e por isso é necessário alterar o para *.exe
      
Dim tempPath As String = IO.Path.ChangeExtension(IO.Path.GetTempFileName(), ".exe")

        ' Verifica o nome da aplicação (Assembly)
      
Dim currentAssembly As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly()

        ' Verifica todos os objectos disponíveis nos resources
      
Dim arrResources As String() = currentAssembly.GetManifestResourceNames()
        For Each resource As String In arrResources

            ' Verifica se o resource tem o nome do ficheiro a extrair
          
If resource.Contains(fileName) Then

              
' Lê o executável dos resources para uma Stream
              
Using resourceStream As IO.Stream = currentAssembly.GetManifestResourceStream(resource)

                    ' Cria um novo FileStream que irá escrever o ficheiro final
                  
Using writer As New IO.FileStream(tempPath, IO.FileMode.Create, IO.FileAccess.Write)

                        Const size As Int16 = 4096
                        Dim bytes(size) As Byte
                        Dim
numBytes As Int32 = 0

                        ' Escreve todos os bytes da Stream criada, usando
                        ' o FileStream e o método Write() num ciclo Do
                      
Do
                          
numBytes = resourceStream.Read(bytes, 0, size)
                            writer.Write(bytes, 0, numBytes)

                        Loop While (numBytes > 0)

                    End Using ' writer

              
End Using ' resourceStream

                ' Retorna a localização do ficheiro
              
Return tempPath

            End If
        Next

      
' Caso não tenha encontrado o ficheiro pretendido
      
Return String.Empty

    Catch ex As Exception
        Return String.Empty
    End Try

End Function

Finalmente, é apenas necessário chamar a função indicando o nome do executável e, neste caso, executá-lo.

If file <> String.Empty Then 

     
' Inicia a aplicação da localização temporária. Se fosse necessário 
    
 ' podia-se copiar o ficheiro final para outra localização qualquer
      
Process.Start(file)
Else

  
MessageBox.Show("Não foi possível extrair o executável")

End If

Este exemplo, bastante simples, utiliza apenas um pequeno ficheiro, mas permite ver o funcionamento geral deste processo. Foi escolhido um executável para o exemplo mas pode ser utilizado outro tipo de ficheiros.

Exemplo do artigo: DOWNLOAD

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


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

ReadOnly NumericUpDown

O controlo NumericUpDown, na propriedade ReadOnly, não tem um comportamento perfeito no seu funcionamento, pois embora esteja definido como ReadOnly, é possível alterar os números através das setas (Up e Down).

Uma das formas de resolver este problema é criar um novo controlo, que herda o controlo base, e que faz o Override ao UpButton e DownButton de modo a ignorar as setas, caso a propriedade ReadOnly esteja definida.

Como a definição desta propriedade altera também a cor de fundo (BackColor), foi criada uma Shadow Property para não fazer esta alteração e podes personalizar o controlo a gosto.

O resultado é esta classe que após compilada ficará disponível na Toolbox

Class ReadOnlyNumericUpDown
    Inherits NumericUpDown

    Private m_ReadOnly As Boolean

  
'''<summary>
  
''' Cria uma propriedade para definir o NumericUpDown como ReadOnly
    ''' e como já existe uma propriedade com este nome é necessário criar
    ''' uma Shadows Property. Além disso como é uma palavra reservada tem
    ''' de ser colocada entre chavetas rectas []
    '''
</summary>
  
Public Shadows Property [ReadOnly]() As Boolean
        Get
            Return
m_ReadOnly
        End Get
        Set
(ByVal value As Boolean)
            m_ReadOnly = value
        End Set
    End Property


  
'''<summary>
  
''' Ao ser carregado no botão para cima e se for ReadOnly ignora a acção
    '''
</summary>
  
'''<remarks></remarks>
  
Public Overrides Sub UpButton()
        If Not Me.ReadOnly Then
            MyBase
.UpButton()
        End If
    End Sub


  
'''<summary>
  
''' Ao ser carregado no botão para baixo e se for ReadOnly ignora a acção
    '''
</summary>
  
'''<remarks></remarks>
  
Public Overrides Sub DownButton()
        If Not Me.ReadOnly Then
            MyBase
.DownButton()
        End If
    End Sub


  
'''<summary>
  
''' Ao ser pressionada uma tecla e se for ReadOnly ignora a acção
    '''
</summary>
   
''' <remarks></remarks>
   
Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
        If Not Me.ReadOnly Then
            MyBase
.OnKeyDown(e)
        Else
           
e.SuppressKeyPress = True
        End If
    End Sub

End Class


Alterar o Wallpaper do Sistema Operativo

A alteração do wallpaper (ambiente de trabalho) do sistema operativo é sempre uma acção muito utilizada e que muitos aproveitam para criar pequenos aplicativos para o fazer (embora já existam várias ferramentas disponíveis).

A alteração do wallpaper pode-se fazer muito simplesmente através da API SystemParametersInfo. Um dos problemas que normalmente se vê em alguns códigos para alterar o wallpaper do computador, é a utilização de ficheiros *.jpg, *.png, etc, sem a conversão para um formato bmp (que o sistema aceita).

Fica um exemplo de como fazer esta alteração:

' Declaração da API que irá alterar o wallpaper
Private Declare Function SystemParametersInfo Lib "user32"Alias"SystemParametersInfoA"(ByVal uAction As Integer, ByVal uParam As Integer, ByVal lpvParam As String, ByVal fuWinIni As Integer) As Integer

' Definição das constantes
Private Const SPI_SETDESKWALLPAPER = 20
Private Const SPIF_UPDATEINIFILE = &H1


'''<summary>
''' Muda o wallpaper do computador
'''
</summary>
'''<param name="imagePath">Endereço completo da imagem</param>
'''<remarks></remarks>
Private Sub SetWallpaper(ByVal imagePath As String)

    ' Verifica se o ficheiro existe
  
If Not IO.File.Exists(imagePath) Then
        Throw New
Exception("O ficheiro indicado não existe!")
    End If

    Try

        Dim
imgName As String

      
' Caso o ficheiro indicado não seja um *.bmp é necessário
        ' converter para tal, de modo a que este funcione
      
If IO.Path.GetExtension(imagePath) <> ".bmp"Then

          
' Cria o ficheiro *.bmp
          
imgName = IO.Path.ChangeExtension(imagePath, "bmp")

            ' Cria uma nova image e grava como *.bmp
          
Using bm AsBitmap = Image.FromFile(imagePath)
                bm.Save(imgName, Imaging.ImageFormat.Bmp)
            End Using

        Else
          
imgName = imagePath
        End If

      
' Define o novo wallpaper
      
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, imgName, _
                                SPIF_UPDATEINIFILE)

    Catch ex AsException
        Throw New Exception(ex.Message)
    End Try

End Sub


Para alterar o wallpaper é apenas necessário fazer:

SetWallpaper("c:\imagem.jpg")

 

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


VB.NET: Usando Controlos WPF em WinForms

Windows Presentation Foundation (WPF) permite criar aplicações muito ricas, muito versáteis e com muitos recursos visuais. Uma aplicação que usa WPF é normalmente composta por duas partes: um arquivo XML com características especiais chamado XAML (eXtended Aplication Markup Language, pronuncia-se “zémel”), e um código .NET (que pode ser escrito numa linguagem compatível como o VB.net ou C#).

Mas nem todos gostam de WPF para criar aplicações, até porque é necessário um processo de aprendizagem significativo, quer pelo XAML quer pela arquitectura geral de funcionamento.

Mas porquê não utilizar as potencialidades do WPF em Windows Forms?

É bastante simples utilizar WPF em Windows Forms e com isso usar todas as suas potencialidades. Muitos utilizadores queixam-se dos controlos disponíveis no Visual Studio e esta é uma oportunidade de criar os seus próprios controlos quase sem limites. Estes dois exemplos iram mostrar como incorporar um controlo WPF em Windows Forms e como fazer algumas alterações com o objectivo de melhorar visualmente os mesmos.

Para criar um controlo WPF é apenas necessário adicionar um novo item ao projecto (desde que esteja seleccionado a plataforma . NET Framework 3.0 ou superior) e seleccionar User Control (WPF)


 

Depois, para cada controlo, indicar o nome pretendido de acordo com os exemplos.

wpfPictureBox




Este controlo tem algumas personalizações com é o caso de:

  • Sombra na PictureBox
  • Possibilidade de alterar a opacidade da imagem


No editor de XAML utilizar o seguinte código:

<UserControl x:Class="wpfPictureBox"
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">

    <!-- Definição da imagem e da sombra –>
    <Image Margin="10" x:Name="Image" Stretch="Uniform" Opacity="1">
      <Image.BitmapEffect>
            <DropShadowBitmapEffect Opacity="1" />
        </Image.BitmapEffect>
     </Image>

</UserControl>

Após compilar a aplicação ficará disponível na Toolbox o controlo wpfPictureBox. Arrastar para o Form, adicionar também uma TrackBar e utilizar o seguinte código para definir a imagem e alterar a opacidade da imagem:

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

        ' Definições da TrackBar
        Me.TrackBar1.Maximum = 100
        Me.TrackBar1.Minimum = 0
        Me.TrackBar1.Value = 100 

        ' Carrega uma imagem no nosso controlo
        WpfPictureBox1.Image.Source = New Windows.Media.Imaging.BitmapImage(New Uri("c:\imagem.jpg"))

    End Sub

    ' Quando se mexe na TrackBar diminui/aumenta a opacidade da imagem
    Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll

        WpfPictureBox1.Image.Opacity = Me.TrackBar1.Value / 100

    End Sub

wpfComboBox




Este controlo tem algumas personalizações (algumas apenas para ilustração das potencialidades do WPF) com é o caso de:

  • Sombra na ComboBox
  • Botão dentro da lista para adicionar novos itens
  • Efeito de fundo na caixa e lista

No editor de XAML utilizar o seguinte código: 

<UserControl  x:Class="wpfComboBox" 
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
    Width="200" Height="30">

    <ComboBox x:Name="wpfCombo" Margin="5" Foreground="White" BorderBrush="White" MaxDropDownHeight="150">

        <!-- Define o efeito da sombra –>
        <ComboBox.BitmapEffect>
            <DropShadowBitmapEffect ShadowDepth="1" Opacity="1" Color="DarkSeaGreen" />
        </ComboBox.BitmapEffect>

        <!-- Define o efeito do fundo –>
        <ComboBox.Background>
            <LinearGradientBrush>

                <LinearGradientBrush.GradientStops>
                    <GradientStopCollection>
                        <GradientStop Color="DarkSeaGreen" Offset="0" />
                        <GradientStop Color="White" Offset="1" />
                    </GradientStopCollection>
                </LinearGradientBrush.GradientStops>

           </LinearGradientBrush>
        </ComboBox.Background>

        <ComboBox.Resources>
            <Style TargetType="{x:Type ComboBoxItem}">               

                <!-- Define o efeito do fundo para os itens –>
                <Setter Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush>
                            <GradientStop Color="DarkSeaGreen" Offset="0"/>
                            <GradientStop Color="White" Offset="1"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>

                <!-- Define a cor do texto dos items –>
                <Setter Property="Foreground" Value="White" />

            </Style>
        </ComboBox.Resources>

         <!-- Insere um botão para criar um novo item –>
        <Button x:Name="btnCombo" Width="150" Background="White" Height="22" Foreground="DarkSeaGreen">
            <TextBlock>Inserir novo item ... </TextBlock>
        </Button>

    </ComboBox>

</UserControl>

Após compilar a aplicação ficará disponível na Toolbox o controlo wpfComboBox. Arrastar para o Form e utilizar o seguinte código para o preencher e detectar que o botão foi pressionado:

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

        ' Insere alguns itens na wpfCombobox
        For x As Byte = 0 To 20
            WpfComboBox1.wpfCombo.Items.Add("Combobox Item " & x.ToString.PadLeft(2, "0"c))
        Next

        ' Define uma acção para quando for pressionado o botão da wpfComboBox
        AddHandler WpfComboBox1.btnCombo.Click, AddressOf btnComboBoxClick 

    End Sub

    Sub btnComboBoxClick()
        MessageBox.Show("Adicionar um novo item à wpfComboBox")
    End Sub

São pequenos exemplos que mostram como utilizar controlos WPF numa aplicação WinForms. Com estes controlos consegue-se personalizar bastante a aplicação e melhorar o aspecto visual.

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