Os Microsoft Reports permitem formatar diversas propriedades através da criação de expressões. Estas propriedades têm de ser alteradas desta forma, caso os resultados sejam dinâmicos, uma vez que não é possíveis de o fazer através do código. São diversas propriedades como BackgroundColor, ForeColor, BorderColor, FontFamily, FontStyle, TextAlign, etc, etc.
Tudo junto, e com algum bom gosto, podem-se criar relatórios bastante profissionais e de uma forma relativamente simples.
Quando seleccionamos algumas dessas propriedades, está disponível uma opção para construir uma expressão. Isso indica que é possível alterar essa propriedade de acordo com o resultado de um campo, com o número da linha, ou com outra condição qualquer. O resultado da expressão é uma String.

Depois, no Expression Editor, existem algumas constantes especificas para a propriedade, que escolhemos definir através de uma expressão. Neste caso a propriedade Color.

Finalmente, na expressão, podemos utilizar condições simples ou complexas. Este é um exemplo que verifica se o campo “ID1” tem o valor “AAA” e coloca a cor Gainsboro caso esta expressão seja verdadeira, e White caso seja falsa.
=IIF(Fields!ID1.Value="AAA","Gainsboro", “White")
Com algumas condições e manipulando diferentes propriedades, podemos obter resultados com este, onde um plano anual, tem diferentes cores para diferentes códigos.

Outro exemplo, e também para um plano anual, usando um objecto Table, pode-se seleccionar a TableRow e definir a seguinte expressão:
=IIF(RowNumber(Nothing) MOD 2 = 0,"White","LightBlue")
Isto fará com que as linhas tenham cores alternadas para uma mais fácil distinção.

São alguns exemplos de formatação em Microsoft Reports, que espero que ajudem a melhorar o resultado final dos relatórios.
Como explicado e demonstrado em artigos anteriores, os Microsoft Reports podem exportar directamente para 3 formatos: Microsoft Excel, Adobe Acrobat e Imagem.
Para simplificar o processo de escolha ao utilizador, resolvi criar um botão personalizado, utilizando um ContextMenuStrip, que permita ao utilizador, seleccionar uma opção de exportação. Para diferenciar dos outros botões, resolvi desenhar um triângulo, indicando que irá expandir-se.
Simples, mas prático e simples de utilizar, e como poderá ter interesse para outros programadores, resolvi partilhar o código. Além disso, mostra como desenhar um objecto, através do método FillPolygon().
Para começar é necessário criar uma classe que irá herdar as propriedades de um Windows.Forms.Button
Imports System.Configuration
Public Class PopupButton
Inherits Windows.Forms.Button
Public WithEvents PopupButtonMenuStrip As ContextMenuStrip
Private exportOptions As List(Of String)
#Region "Propriedades"
Private m_ArrowWidth As Integer
'''<summary>
''' Define o tamanho(horizontal)
'''</summary>
Public Property ArrowWidth() As Integer
Get
Return m_ArrowWidth
End Get
Set(ByVal value As Integer)
m_ArrowWidth = value
End Set
End Property
Private m_ArrowHeight As Integer
'''<summary>
''' Define o tamanho(vertical)
'''</summary>
Public Property ArrowHeight() As Integer
Get
Return m_ArrowHeight
End Get
Set(ByVal value As Integer)
m_ArrowHeight = value
End Set
End Property
Private m_ArrowBrushColor As Brush
'''<summary>
''' Define a cor da seta
'''</summary>
Public Property ArrowBrushColor() As Brush
Get
Return m_ArrowBrushColor
End Get
Set(ByVal value As Brush)
m_ArrowBrushColor = value
End Set
End Property
#End Region
#Region "Construtor"
Sub New()
' Algumas pré-definições da seta
Me.ArrowWidth = 14
Me.ArrowHeight = 14
Me.ArrowBrushColor = Brushes.DarkGray
' Opções de exportação
exportOptions = New List(Of String)
exportOptions.Add("Microsoft Excel (*.xls)")
exportOptions.Add("Adobe Acrobat (*.pdf)")
exportOptions.Add("Imagem (*.jpg)")
Me.PopupButtonMenuStrip = New ContextMenuStrip
' Adiciona os itens ao menu
With Me.PopupButtonMenuStrip
.Items.Add(New ToolStripMenuItem() With {.Text = exportOptions(0).ToString})
.Items.Add(New ToolStripMenuItem() With {.Text = exportOptions(1).ToString})
.Items.Add(New ToolStripMenuItem() With {.Text = exportOptions(2).ToString})
End With
End Sub
#End Region
Enum output
Excel
PDF
Image
End Enum
' Definição de um evento para quando for seleccionada uma opção
Public Event PopupButtonMenuClick(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs, _
ByVal output As output)
'''<summary>
''' Mostra o ContextMenuStrip
'''</summary>
Private Sub popupButton_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Click
Me.PopupButtonMenuStrip.Show(Me, New Point(0, Me.Height))
End Sub
''' <summary>
''' Desenha a seta no botão alinhada à direita
''' </summary>
Private Sub popupButton_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim h As Integer = (Me.Height / 2) + 3
Dim w As Integer = Me.Width - 15 ' offset
Dim points As New List(Of Point)
points.Add(New Point(w - (Me.ArrowWidth / 2), h - (Me.ArrowHeight / 2)))
points.Add(New Point(w, h))
points.Add(New Point(w + (Me.ArrowWidth / 2), h - (Me.ArrowHeight / 2)))
e.Graphics.FillPolygon(Me.ArrowBrushColor, points.ToArray)
End Sub
'''<summary>
''' Chama o evento PopupButtonMenuClick
'''</summary>
Private Sub PopupButtonMenuStrip_ItemClicked(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) _
Handles PopupButtonMenuStrip.ItemClicked
Select Case e.ClickedItem.Text
Case exportOptions(0).ToString
RaiseEvent PopupButtonMenuClick(sender, e, output.Excel)
Case exportOptions(1).ToString
RaiseEvent PopupButtonMenuClick(sender, e, output.PDF)
Case exportOptions(2).ToString
RaiseEvent PopupButtonMenuClick(sender, e, output.Image)
End Select
End Sub
End Class
Após compilado o projecto, este botão (PopupButton), irá estar disponível na Toolbox.
Finalmente, após arrastar o botão para o Form, utiliza-se o evento PopupButtonMenuClick para verificar e definir que tipo de exportação fazer:
Private Sub btnExportar_PopupButtonMenuClick(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs, _
ByVal output As PopupButton.output) _
Handles btnExportar.PopupButtonMenuClick
Select Case output
Case PopupButton.output.Excel
Debug.WriteLine("Exportar para Excel")
Case PopupButton.output.Image
Debug.WriteLine("Exportar para Imagem")
Case PopupButton.output.PDF
Debug.WriteLine("Exportar para PDF")
End Select
End Sub
Muito simples de utilizar, mas poderá simplificar o processo de selecção do tipo de exportação sem recorrer a um Form especifico ou sem utilizar múltiplos botões.
Espero que seja útil!
Num artigo anterior, mostrei como utilizar Custom Code num relatório. Isto é muito útil se for necessário personalizar alguma coisa, mas se precisarmos de fazer esta operação em diversos relatórios ? Bem, podemos usar as Custom Assemblies.
Para usar Custom Assemblies num relatório, é apenas necessário criar um projecto do tipo Class Library, que irá originar um *.dll, e usar esta biblioteca em todos os relatórios.
Depois de criado o ficheiro/biblioteca, é necessário copiar o *.dll para as pastas Release/Debug da aplicação, e para a pasta PrivateAssemblies (no Visual Studio 2008 está normalmente em C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\)
Para este exemplo criei uma pequena classe, que irá cortar algum texto caso o tamanho do texto seja igual ou superior a 30:
Public Class rptClass
Public Function CutText(ByVal txt As String) As String
If txt.Length >= 30 Then
Return txt.Substring(0, 26) & " ..."
Else
Return txt
End If
End Function
Public Shared Function SharedCutText(ByVal txt As String) As String
If txt.Length >= 30 Then
Return txt.Substring(0, 26) & " ..."
Else
Return txt
End If
End Function
End Class
Depois, é necessário adicionar uma referência no relatório para a biblioteca. Abrir o relatório, e no menu Reports, seleccionar a opção Report Properties. No separador References, seleccionar o *.dll criado.
NOTA: O nome da biblioteca (Assembly Name) irá ser usado mais tarde no código
Nesta janela existem 2 grelhas:
References: Se forem usados métodos definidos como Shared, é possível invocá-los directamente
Classes: Se não forem usados métodos Shared, é necessário criar uma nova instância da classe (tem de ser escrito directamente na grelha)
Depois, no relatório, podemos usar da seguinte forma:
Como podem ver, se for criada uma nova instância da classe, é necessário definir como =Code.<instância criada>.<nome do método>
Finalmente, e no código, é necessário definir que a classe criada (biblioteca) é Trusted Code, usando o Assembly Name usado nas referências:
Me.ReportViewer1.LocalReport.AddTrustedCodeModuleInCurrentAppDomain( _
"ClassLibrary1, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null")
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Os Microsoft Reports têm um conjunto de funções que permitem personalizar a informação que é colocada nos relatórios. Através do Expression Editor, podemos visualizar diversas funções já incorporadas, disponíveis na categoria Common Functions, e com isto, conseguir formatar, modificar, personalizar a visualização dos dados.
No entanto, é possível também a utilização de código personalizado, de modo a não nos limitamos às funções disponíveis, não obrigando à manipulação dos dados na aplicação/base de dados.
Para utilizar código personalizado no relatório, é necessário ir ás propriedades do relatório (menu Report – Report Properties) e escolher o separador Code.
Na caixa de texto disponível, colocar o código a usar.
Neste exemplo ilustrativo, irá ser utilizada uma função que através de um código de País, irá formatar o valor para o formato correcto (Euros ou Dólares).
Este é o código que está na imagem anterior:
Function currencyFormat(ByVal value As Double, ByVal countryCode As String) As String
Select Case countryCode
Case "PT"
Return String.Format("{0:n} €", value)
Case "US"
Return String.Format("$ {0:n}", value)
Case Else
Return String.Format("{0:n}", value)
End Select
End Function
Se editarmos o relatório com um editor XML ou mesmo com um editor de texto (por exemplo o Notepad), o código personalizado que definimos encontra-se entre as tags <Code> ... </Code>
Para utilizar o código criado, na indicação dos campos a listar, utilizar o Expression Editor, clicando com o botão direito do rato e seleccionando “Expression …”.
Aqui, indica-se =Code.<nome da função> de modo a indicar que estamos a utilizar código personalizado. Neste caso será a seguinte expressão, onde o primeiro campo indicará o valor, e o segundo indicará o código do País.
=Code.currencyFormat(Fields!price.Value,Fields!countryCode.Value)
Pode-se verificar também que o Intellissense não irá reconhecer a nova função, uma vez que não é compilado e não é possível interpretar o código criado. De qualquer maneira, é apenas necessário garantir que a função se encontra correctamente indicada, e os parâmetros bem definidos.
O resultado final é um relatório com diferentes formatos para diferentes códigos de Países
Este foi um exemplo de como utilizar código personalizado nos relatórios e mostrar que, com este método, é possível ter relatórios muito mais flexíveis.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
O armazenamento de imagens em bases de dados, designado por BLOB's(binary large object), é um processo muito comum quando se trabalha com dados. Existem vantagens e desvantagens em guardar as imagens na base de dados, ou apenas o seu caminho ou URL, e se no SQL Server o processo é simples, como podem ver no artigo Inserir Imagens no SQL Server, em outras bases de dados, como o Access, o processo é mais complicado.
O objectivo deste artigo não é enumerar as vantagens e desvantagens de ambos os métodos, mas sim mostrar com se pode mostrar as imagens num relatório em ambos os métodos. Neste caso será mostrado apenas para SQL Server, e deverá ser semelhante para outras bases de dados, excepto Access, uma vez que a imagem é guardada como objecto.
Imagem na base de dados
Caso a imagem esteja guardada na base de dados, é apenas necessário inserir um controlo Image, da Toolbox, no controlo Table.
Depois, e nas propriedades do controlo, definir como Value o campo respectivo da base de dados, a Source como Database e o MIMEType adequado. Neste exemplo será seleccionado image/png.
Caminho na base de dados
Caso na base de dados apenas esteja gravado o caminho para o ficheiro ou URL, pode-se inserir um controlo Image, da Toolbox, no controlo Table, como no exemplo anterior, mas na propriedade Value indica-se: ="File://" & Nome do Campo.
Define-se também a propriedade Source como External.
Finalmente, através de código ou nas propriedade do ReportViewer, é necessário indicar que este permite a utilização de imagens externas, definindo EnableExternalImages como True.
Me.ReportViewer1.LocalReport.EnableExternalImages = True
Como podem nestes dois exemplos, para os dois métodos possível, os relatórios são muito flexíveis e permitem de uma forma bastante simples mostrar as imagens guardadas ou através dos caminhos guardados
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Os Microsoft Reports são ficheiros XML, com uma extensão diferente (*.rdlc), que guardam o esquema do relatório, imagens embebidas, código personalizado, etc. No entanto, e por serem ficheiros fáceis de editar (usando um simples editor XML ou mesmo através do Notepad), podem ser modificados, alterando com isso, e sem ser necessário compilar a aplicação, o resultado pretendido.
Isto trás algumas vantagens e, obviamente, algumas desvantagens.
Se por um lado conseguimos ver o conteúdo do ficheiro e analisar um eventual problema, não colocamos toda a aplicação num só ficheiro e não aumentamos o tamanho do executável, por outro lado, não garantimos a protecção do relatório. Estes são alguns pontos que devem ser considerados na escolha da opção a usar.
Para se utilizar um relatório embebido no executável, é necessário apenas seleccionar o relatório, na janela Soluction Explorer, e definir na propriedade Build Action como Embedded Resource.
Depois, utilizar uma função semelhante à seguinte, para extrair o relatório embebido para uma Stream.
''' <summary>
''' Extrai dos Resources da aplicação, o relatório para uma Stream
'''</summary>
''' <param name="reportName">Nome do relatório</param>
Private Function GetReport(ByVal reportName As String) As IO.Stream
' Recolhe a informação da Assembly
Dim currentAssembly As Reflection.Assembly = _
Reflection.Assembly.GetExecutingAssembly()
' Irá guarda o caminho + nome do ficheiro
Dim resource As String = String.Empty
' Verifica nos Resources se encontra o relatório pretendido
Dim arrResources As String() = _
currentAssembly.GetManifestResourceNames()
For Each resource In arrResources
If resource.Contains(reportName) Then Exit For
Next
' Coloca o relatório embebido na Stream
Dim resourceStream As IO.Stream = _
currentAssembly.GetManifestResourceStream(resource)
Return resourceStream
End Function
Finalmente, definir como nome do relatório, a Stream retornada pela função, através do método LoadReportDefinition().
Dim rptStream As IO.Stream = GetReport("rptProducts.rdlc")
Me.ReportViewer1.LocalReport.LoadReportDefinition(rptStream)
E já está … um método simples, prático, e que poderá proteger os relatório, caso seja pretendido.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
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
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!
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!
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!