domingo, 10 de junho de 2007

Criando Relatórios com PrintDocument

Ola, vou falar hoje de criação de relatórios com PrintDocument, este e um componente para criação de relatórios que é muito simples de usar, só exigindo que o desenvolvedor tenha uma dose de atenção extra , já que o desenvolvedor tem que definir cada um dos elementos que serão impressos como também a sua posição.Mas particularmente eu prefiro este componente ao Crystal Report, por que o Crystal e sim muito fácil de fazer um relatório com ele, mas até hoje a cada nova instalação na maquina do cliente e uma aventura com os ocx dll e outras coisas que o Crystal depende mesmo no .Net, o que faz o Crystal ser uma opção viável apenas para ambientes muito bem controlados, o que não e muito fácil para quem faz programas comerciais que serão distribuídos para os mais diversos ambientes.

Então vamos lá, Crie um novo projeto Windows Form, de um duplo click no formulário e declare um DataTable global para o form e no evento form_load digite o código abaixo, ele não tem nada haver com a impressão em si, ele e apenas para gera um DataTable com dados fictícios para o exemplo que usaremos na listagem que iremos criar, Em um projeto real ele deve ser substituído por um DataTable, DataSet ou DataReader em fim pela fonte de dados que você estiver usando.

Private tbDados As DataTable

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


Dim Nomes As String() = {"Maria", "João", "José", "Joaquim", "Joana", "Valdete", "Joca", "Rosa"}


Me.tbDados = New DataTable


Me.tbDados.Columns.Add("id")


Me.tbDados.Columns.Add("Nome")


While tbDados.Rows.Count <>


Dim dr As DataRow


dr = tbDados.NewRow


dr("id") = tbDados.Rows.Count + 1


Randomize()


dr("Nome") = Nomes(CInt(Int((8 * Rnd())))) & " " & Nomes(CInt(Int((8 * Rnd()))))


tbDados.Rows.Add(dr)


End While


End Sub

Agora sim, arraste da toolbox o PrintDocument para o seu projeto. E de um duplo click nele, se abrira para você o evento PrintPage, e aqui que se concentra a parte mais importante do codigo, na verdade toda a pagina sera construida aqui, este evento e disparado a cada nova pagina a ser construida no relatorio.

Antes de proceguir vamos ver este evento mas de perto, o evente args dele e o que vamos manipular para dizer ao componente o que queremos que apareça na pagina.O principal metodo que iremos usar e o Graphics.DrawString que tem a seguinte estrutura

e.Graphics.DrawString(<texto que sera impresso><Posição vertical do texto><posição horizontal do texto>)

Outra metodo importante e o HasMorePages, nele informas se haverar mais paginas ou não, se verdadeiro ele disparar novamente o evento para imprimirmos a proxima pagina, se falso ele saira do evento e ira inserar o relatorio.

Temos mas alguns metodos mas acredito que poderemos falar deles no momento em que forem usados.

Bem, creio que você notou que temos que controlar qual e a posição do relatorio que estamos, então para isto iremos criar duas propriedades X e Y no nosso formulario.

Private _X As Single


Private _Y As Single

Public Property X() As Single

Get

Return _X

End Get

Set(ByVal value As Single)

_X = value

End Set

End Property

Public Property Y() As Single

Get

Return _Y

End Get

Set(ByVal value As Single)

_Y = value

End Set

End Property

Só que tem um detalhe a nossa impressão começa a partir da margem superior, para não termos que ficar calculando esta distancia a toda hora vamos fazer a nossa propriedade Y já trazer esta valor calculado, para isto vamos criar mas uma propriedade que vai quardar a margem superior e fazermos uma pequena modificação na propriedade Y.

Private _MargemSuperior As Single

Property MargemSuperior() As Single

Get

Return _MargemSuperior

End Get

Set(ByVal value As Single)

_MargemSuperior = value

End Set

End Property

'Propriedade Y Rescrita

Public Property Y() As Single

Get

Return _Y + Me.MargemSuperior

End Get

Set(ByVal value As Single)

_Y = value - Me.MargemSuperior

End Set

End Property

O que fizemos aqui e quarda o valor da posição de Y onde estamos, na leirura que acrescento o valor da margem superior e na leitura eu disconto este valor, por que na maioria das vezes a posição Y vai ser acrescida do proprio valor, assim mantemos o valor correto.

Agora temos que ter uma maneira de sabermos em que registro estamos,como já que o evento sera disparado a cada pagina, você pode usar BindingContext para navegar os registros, mas optei por criar uma propriedade para guardar esta posição por achar mas facil de entender, então vamos criar a propriedade registro.

Private _Registro As Integer

Private Property Registro() As Integer

Get

Return _Registro

End Get

Set(ByVal value As Integer)

_Registro = value

End Set

End Property

Ainda temos a fonte do padrão do relatorio, vou criar mais uma propriedade para a fonte, que sera usada no relatorio.

Private _Fonte As System.Drawing.Font

Public Property Fonte() As System.Drawing.Font

Get


If IsNothing(_Fonte) Then


_Fonte = New System.Drawing.Font("Verdana", 10)


End If


Return _Fonte


End Get


Set(ByVal value As System.Drawing.Font)


_Fonte = value


End Set


End Property


Nada Imprede você de mudar de fonte, o codigo acho que alto explicativo, a fonte e solicitada se não estiver estanciada estanciamos uma fonte verdade de tamanho 10, bem simples.

Agora escreva este codigo no evento PrintPage


Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage


Me.MargemSuperior = e.MarginBounds.Top


Me.X = e.MarginBounds.Left


While Me.Registro <= Me.tbDados.Rows.Count - 1


e.Graphics.DrawString(Me.tbDados.Rows(Me.Registro)("ID") & " - " & Me.tbDados.Rows(Me.Registro)("NOME"), Me.Fonte, New System.Drawing.SolidBrush(Color.Black), Me.X, Me.Y)


Me.Y += Me.Fonte.Height


Me.Registro += 1


If Me.Y > (e.MarginBounds.Bottom - (Me.Fonte.Height + 3)) AndAlso (Me.Registro <= Me.tbDados.Rows.Count - 1) Then


Me.Y = Me.MargemSuperior


e.HasMorePages = True


Exit Sub


End If


End While


e.HasMorePages = False

End Sub

Com este codigo você já vai poder criar a listagem, deixa eu distacar algumas partes.

Neste trecho estamos pegando a margem superior do relatorio,e passando para a propriedade MargemSuperior, assim não teremos que ficar escrevendo toda hora o codigo de Y+margem , atraves do metodo MarginBounds do EventArgs temos acesso as informações das area util da pagina, ou seja o tamanho da pagina discontada as margens, e as proprias margens.

Me.MargemSuperior = e.MarginBounds.Top

E neste pegamos a posição inicial de X, que e a posição vertical de impresão do relatorio, pense como colunas.

Me.X = e.MarginBounds.Left

Então finalmente fazemos um loop nos registros do DataTable, para escrever a linha.A parte que efetivamente imprime a linha no relatorio e esta aqui.

e.Graphics.DrawString(Me.tbDados.Rows(Me.Registro)("ID") & " - " & Me.tbDados.Rows(Me.Registro)("NOME"), Me.Fonte, New System.Drawing.SolidBrush(Color.Black), Me.X, Me.Y)

Como mostrado no começo este metodo recebeu o texto, a fonte que usamos no relarorio um objeto SolidBrush para definir a cor, no caso usei a preta, e a posição X e Y em que queremos que o texto seja impresso. Depois acrescentamos a posição Y com a altura usada pela fonte do ultimo texto que escrevemos, assim na proxima passagem o texto ficara emediatamente a abaxo, e ascrecentamos também a propriedade registro para lermos o proximo registro.



Me.Y += Me.Fonte.Height


Me.Registro += 1



O If que vem em seguida determina se chegamos ao final da pagina ou não, se a posição Y for maior que a margem bottom no fim da pagina e não tiver acabado os registro então e porque temos mas paginas a imprimir


Então renicializamos o valor da propriedade Y para o inicio da pagina que e a margem superior e passamos o valor Verdadeiro para HasMorePages sinalizando para o aplicativo que temos mais paginas a imprimir e saimos da procedure, que sera disparada novamente para a impressão da proxima pagina,porem se a margem não for maior então e por que ainda não chegamos ao fim da pagina, e o processamente vai se dar até os registros acabarem, que sera quando a propriedade HasMorePages do eventargs receberar o valor falso sinalizando o fim do relatorio o Me.Fonte.Height + 3, entrou na conta so pra termos uma folga do rodapé.


While Me.Registro <= Me.tbDados.Rows.Count - 1


e.Graphics.DrawString(Me.tbDados.Rows(Me.Registro)("ID") & " - " & Me.tbDados.Rows(Me.Registro)("NOME"), Me.Fonte, New System.Drawing.SolidBrush(Color.Black), Me.X, Me.Y)


Me.Y += Me.Fonte.Height


Me.Registro += 1


If Me.Y > (e.MarginBounds.Bottom - (Me.Fonte.Height + 3)) AndAlso (Me.Registro <= Me.tbDados.Rows.Count - 1) Then


Me.Y = Me.MargemSuperior


e.HasMorePages = True


Exit Sub


End If


End While


e.HasMorePages = False

Neste estante mas um componente vai entrar em cena,e o PrintPreviewDialog ele e o responsavel pela visualização do relatorio, então arraste um botão para o seu formulario e um PrintPreviewDialog e de duplo click no botão e no evento onClick do botão escrava o seguinte codigo.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Me.Registro = 0


Me.PrintPreviewDialog1.Document = Me.PrintDocument1


Me.PrintPreviewDialog1.ShowDialog()


End Sub

O que fizemos aqui e inicializar a propriedade registro como 0, para que a cada novo relatorio a contagem seja reiniciada, depois informamos ao PrintPreviewDialog qual o documento que sera vizualizado por ele, e finalmente mostramos.

O resulta esta bem feio, mas calma, ainda vamos fazer mas algumas coisas.




Todo relatorio,tem que ter cabeçalho e rodape, então agora que já vimos o coração do codigo vamos infeitar um pouco,vamos começar pelo cabeçalho.

Vamos criar uma procedure, para o cabeçalho para o codigo não ficar muito bagunçado, passando o eventargs como parametro.Vou colocar o codigo e vou comentando os trechos


Private Sub Cabecalho(ByVal e As System.Drawing.Printing.PrintPageEventArgs)


e.Graphics.DrawRectangle(Pens.Red, Me.X, Me.Y, e.MarginBounds.Width, 60)


Me.Y += 0.5


e.Graphics.DrawImage(Image.FromFile("c:\divtopfundo.jpg"), Me.X, Me.Y, e.MarginBounds.Width, 60)


e.Graphics.DrawString(Now.ToString, New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.White), e.MarginBounds.Width - 80, Me.Y)


Me.Y += 70


Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}


e.Graphics.DrawLines(Pens.Black, pontos)


Me.Y += 0.5


e.Graphics.DrawString("Id: Nome:", New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.Blue), Me.X, Me.Y)


Me.Y += 20


pontos = New Point() {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}


e.Graphics.DrawLines(Pens.Black, pontos)


Me.Y += 10


Me.AlturaDoCabecalho = Me.Y


End Sub




A primeira coisa que fezemos e imprimir um retangulo, com a cor vermelha,o codigo e bem tranguilo, e dizer onde começa o retangulo passando a posição X e Y, e depois dizemos o comprimente que no caso passei o comprimento da area util da pagina e depois a altura do retangulo.E como vamos escrever dentro do retangulo fiz um pegueno ascrecimo na propriedade Y para escrevemos logo a baixo da borda do retangulo

e.Graphics.DrawRectangle(Pens.Red, Me.X, Me.Y, e.MarginBounds.Width, 60)

Me.Y += 0.5


Depois vamos colocar uma imagem no cabeçalho, esta imagem poder ser carregada a partir de um arquivo ou de um streem de memoria no caso da imagem vir do banco,aqui eu fiz direto de um arguivo para o exemplo, no mais ele e igual ao retangulo, passamos X e Y e a area que a imagem ira ocupar, o ideal para que não deforme e que seja o tamanho certo da imagem.

e.Graphics.DrawImage(Image.FromFile("c:\divtopfundo.jpg"), Me.X, Me.Y, e.MarginBounds.Width, 60)

Depois da imagem escrevemos a data corrente, ai não tem novidades a não ser o deslocamento do ponto X.

Depois deslocamos o eixo Y, para depois do quadrados e vamos desenhar uma linha.Aqui tem alguns detalhes, que podem parecer estranha, num primeiro momento, mas que faz muito sentido na verdade.O comando para desenhar linha e e.Graphics.DrawLines(Pens.Black, pontos), como você pode ver, so passamos o labis de cor preta, e os pontos da reta, os pontos de reta nada são do que um array dizendo onde termina e começa cada ponto, isto mesmo pontos de reta igual as aulas de matematica do primario.você pode passar quantos pontos quiser se quiser fazer varias retas que formem uma outra imagem, você pode até mesmo fazer um quandrado,no nosso caso só temos dois pontos de reta o que tem a mesma altura que e eixo Y e que estão em estremidades diferentes que e o eixo X, Então vazemos isto, passamos Y para os dois eixos que e igual, e o eixo X para um e a largura da pagina para o outro. O codigo esta abaixo.

Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)


Depois escrevemos os titulos das colunas, como fizemos para escrever os dados, sem novidades aqui também.E escrevemos uma segundo linha,lembrando de acrescentar a cada linha escrita a propriedade Y, para escrever uma linha embaixo da outra.

Volta ao evento PrintPage e logo acima do while faça a chamada do metodo que gera o cabeçalho

Me.Cabecalho(e)

Agora você já deve poder rodar e gerar o relatorio.que ficara assim.






Esta faltando o Rodapé do relatorio,o codigo agora depois de tudo que já vimos e facil, a única novidade e o calculo do numero de paginas, para calcular isto temos que saber quantos registros nos temos e qual o espaço que eles ocupam, e quanto de espaço sobra na pagina discontando o rodape e o cabeçalho. Falando assim parece complicado mas não e, mas antes vamos fazer alguns ajustes, primeiro vamos criar uma propriedade para quardar o valor do cabeçalho,e quardaremos a posição de Y no momento que o cabeçalho foi impresso, assim se almentarmos o numero de linhas do cabelho ou dimuirmos não teremos problemas com o restante do codigo.

Private _AlturaDoCabecalho As Single


Property AlturaDoCabecalho() As Single


Get


Return _AlturaDoCabecalho


End Get


Set(ByVal value As Single)


_AlturaDoCabecalho = value


End Set


End Property


Declarada a propriedade de Altura do Cabeçalho vamos tabem fazer a propriedade da Pagina Atual, para usarmos no rodape.


Private _PaginaAtual As Integer


Public Property PaginaAtual() As Integer


Get


Return _PaginaAtual


End Get


Set(ByVal value As Integer)


_PaginaAtual = value


End Set


End Property

Agora vamos criar o metodo Rodape, como a maior parte do codigo já e conhecido vou postar direto e comentar.

Private Sub Rodape(ByVal e As System.Drawing.Printing.PrintPageEventArgs)

Me.Y = e.MarginBounds.Bottom - (Me.Fonte.Height + 1)

Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 0.5

Dim TotPag As Integer

TotPag = Math.Ceiling(((Me.Fonte.Height * tbDados.Rows.Count) / (e.MarginBounds.Bottom - Me.AlturaDoCabecalho)))

e.Graphics.DrawString("Pagina " & Me.PaginaAtual & " de " & TotPag.ToString, New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.Blue), Me.X, Me.Y)

Me.Y += Me.Fonte.Height + 0.5

pontos = New Point() {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)


End Sub


A primeira linha do codigo posicionamos o eixo Y na posição do rodape, isto faro com que o rodape fique no fim da pagina mesmo que os registros termiem no meio dela no casso e a margem inferior menos a linha que foi gasta com o texto somada ao espaço ocupado pelas duas linhas que seram impressas, este codigo deveria ser parametrizado, mas ficaria muito complicado para explicar.Mas acredito que você não tera dificuldades de fazer isto.


Depois vem o nosso velho conhecido codigo de desenhar linha, Agora sim vamos ao calculo do total de paginas.Aqui multiplicamos a altura da fonte que estamos usando pelo numero de registros do banco, assim temos o total de espaço que sera usado pelos registros, depois pegamos a margem de fundo da pagina e tiramos o espaço ocupado pelo cabeçalho, e teremos o espaço que ainda esta sobrando da area util da pagina, dividindo o espaço a ser ocupado pelos registros pelo o que ainda temos para usar na pagina, E passamos este valor para a função Ceiling do namespace Math,para arredondar o decimal sempre para cima, por que se tiver decimal já e sinal que teremos uma outra pagina que não esta completa, e o comportamento natural do inteiro ao receber um numero decimal e arredondar para o inteiro mas proximo, e o que queremos e arredondar sempre para cima.

Feito isto agora e fazer alguns acertos que são os seguintes.volte ao metodo cabecalho e no final atribua o valor de Y para alturacabecalho, para sabermos o tamanho do cabeçalho.


Private Sub Cabecalho(ByVal e As System.Drawing.Printing.PrintPageEventArgs)

e.Graphics.DrawRectangle(Pens.Red, Me.X, Me.Y, e.MarginBounds.Width, 60)

Me.Y += 0.5

e.Graphics.DrawImage(Image.FromFile("c:\divtopfundo.jpg"), Me.X, Me.Y, e.MarginBounds.Width, 60)

e.Graphics.DrawString(Now.ToString, New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.White), e.MarginBounds.Width - 80, Me.Y)

Me.Y += 70

Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 0.5

e.Graphics.DrawString("Id: Nome:", New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.Blue), Me.X, Me.Y)

Me.Y += 20

pontos = New Point() {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 10

Me.AlturaDoCabecalho = Me.Y 'Acrecente este codigo

end Sub

Depois no PrintPage acrescente a chamada do rodape, antes das saidas do metodo

Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

Me.MargemSuperior = e.MarginBounds.Top

Me.X = e.MarginBounds.Left

Me.PaginaAtual += 1

Me.Cabecalho(e)

While Me.Registro <= Me.tbDados.Rows.Count - 1

e.Graphics.DrawString(Me.tbDados.Rows(Me.Registro)("ID") &amp;amp;amp;amp; " - " & Me.tbDados.Rows(Me.Registro)("NOME"), Me.Fonte, New System.Drawing.SolidBrush(Color.Black), Me.X, Me.Y)

Me.Y += Me.Fonte.Height

Me.Registro += 1

If Me.Y > (e.MarginBounds.Bottom - (Me.Fonte.Height + 3)) AndAlso (Me.Registro <= Me.tbDados.Rows.Count - 1) Then

Me.Rodape(e) 'Acrecente este codigo

Me.Y = Me.MargemSuperior

e.HasMorePages = True

Exit Sub

End If

End While


Me.Rodape(e) 'Acrescente este codigo


e.HasMorePages = False


End Sub




E por ultimo no botão em em que iremos clicar para chamar nosso relatorio, inicialize a pagina atual


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click


Me.Registro = 0


Me.PaginaAtual = 0 'Acrecente este codigo


Me.PrintPreviewDialog1.Document = Me.PrintDocument1


Me.PrintPreviewDialog1.ShowDialog()


End Sub


Tai o codigo para gerar relatorio, como falei e um pouco braçal.O melhor mesmo e vocÊ criar classes que herdem deste componente e criar codigos mas eficientes para o controle de colunas margens e tudo mais, para não ter que ficar sempre escrevendo este codigo.Acredito que com o que foi visto aqui da pra dar inicio aos relatorios e evitar os eternos problemas de dll e ocx do Crystal Report. Valeu e até mais o codigo completo esta em outro post.



Codigo para criação de relatórios com PrintDocument

Public Class Form2

Private _PaginaAtual As Integer

Private _X As Single

Private _Y As Single

Private _MargemSuperior As Single

Private _Registro As Integer

Private _Fonte As System.Drawing.Font

Private tbDados As DataTable

Private _AlturaDoCabecalho As Single

Property AlturaDoCabecalho() As Single

Get

Return _AlturaDoCabecalho

End Get

Set(ByVal value As Single)

_AlturaDoCabecalho = value

End Set

End Property

Private Property Registro() As Integer

Get

Return _Registro

End Get

Set(ByVal value As Integer)

_Registro = value

End Set

End Property

Property MargemSuperior() As Single

Get

Return _MargemSuperior

End Get

Set(ByVal value As Single)

_MargemSuperior = value

End Set

End Property

Public Property X() As Single

Get

Return _X

End Get

Set(ByVal value As Single)

_X = value

End Set

End Property

Public Property Y() As Single

Get

Return _Y + Me.MargemSuperior

End Get

Set(ByVal value As Single)

_Y = value - Me.MargemSuperior

End Set

End Property

Public Property PaginaAtual() As Integer

Get

Return _PaginaAtual

End Get

Set(ByVal value As Integer)

_PaginaAtual = value

End Set

End Property

Public Property Fonte() As System.Drawing.Font

Get

If IsNothing(_Fonte) Then

_Fonte = New System.Drawing.Font("Verdana", 10)

End If

Return _Fonte

End Get

Set(ByVal value As System.Drawing.Font)

_Fonte = value

End Set

End Property

Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

Me.MargemSuperior = e.MarginBounds.Top

Me.X = e.MarginBounds.Left

Me.PaginaAtual += 1

Me.Cabecalho(e)

While Me.Registro <= Me.tbDados.Rows.Count - 1

e.Graphics.DrawString(Me.tbDados.Rows(Me.Registro)("ID") & " - " & Me.tbDados.Rows(Me.Registro)("NOME"), Me.Fonte, New System.Drawing.SolidBrush(Color.Black), Me.X, Me.Y)

Me.Y += Me.Fonte.Height

Me.Registro += 1

If Me.Y > (e.MarginBounds.Bottom - (Me.Fonte.Height + 3)) AndAlso (Me.Registro <= Me.tbDados.Rows.Count - 1) Then

Me.Rodape(e)

Me.Y = Me.MargemSuperior

e.HasMorePages = True

Exit Sub

End If

End While

Me.Rodape(e)

e.HasMorePages = False

End Sub

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

Dim Nomes As String() = {"Maria", "João", "José", "Joaquim", "Joana", "Valdete", "Joca", "Rosa"}

Me.tbDados = New DataTable

Me.tbDados.Columns.Add("id")

Me.tbDados.Columns.Add("Nome")

While tbDados.Rows.Count <>

Dim dr As DataRow

dr = tbDados.NewRow

dr("id") = tbDados.Rows.Count + 1

Randomize()

dr("Nome") = Nomes(CInt(Int((8 * Rnd())))) & " " & Nomes(CInt(Int((8 * Rnd()))))

tbDados.Rows.Add(dr)

End While

End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Me.Registro = 0

Me.PaginaAtual = 0

Me.PrintPreviewDialog1.Document = Me.PrintDocument1

Me.PrintPreviewDialog1.ShowDialog()

End Sub

Private Sub Cabecalho(ByVal e As System.Drawing.Printing.PrintPageEventArgs)

e.Graphics.DrawRectangle(Pens.Red, Me.X, Me.Y, e.MarginBounds.Width, 60)

Me.Y += 0.5

e.Graphics.DrawImage(Image.FromFile("c:\divtopfundo.jpg"), Me.X, Me.Y, e.MarginBounds.Width, 60)

e.Graphics.DrawString(Now.ToString, New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.White), e.MarginBounds.Width - 80, Me.Y)

Me.Y += 70

Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 0.5

e.Graphics.DrawString("Id: Nome:", New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.Blue), Me.X, Me.Y)

Me.Y += 20

pontos = New Point() {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 10

Me.AlturaDoCabecalho = Me.Y 'Acrecente este codigo

End Sub

Private Sub Rodape(ByVal e As System.Drawing.Printing.PrintPageEventArgs)

Me.Y = e.MarginBounds.Bottom - (Me.Fonte.Height + 1)

Dim pontos As Point() = {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

Me.Y += 0.5

Dim TotPag As Integer

TotPag = Math.Ceiling(((Me.Fonte.Height * tbDados.Rows.Count) / (e.MarginBounds.Bottom - Me.AlturaDoCabecalho)))

e.Graphics.DrawString("Pagina " & Me.PaginaAtual & " de " & TotPag.ToString, New Font("Verdana", 10, FontStyle.Bold), New System.Drawing.SolidBrush(Color.Blue), Me.X, Me.Y)

Me.Y += Me.Fonte.Height + 0.5

pontos = New Point() {New Point(Me.X, Me.Y), New Point(e.MarginBounds.Width, Me.Y)}

e.Graphics.DrawLines(Pens.Black, pontos)

End Sub

End Class

quinta-feira, 7 de junho de 2007

Master Pages Tipadas

Bem hoje vou falar de uma coisa muito simples, tão simples que nem sei por que já não vem habilitada por padrão, por ser extramente útil para quem usa master pages, e acredito que seja praticamente a totalidade de quem desenvolve paginas ASP.Net 2.0

O assunto em questão são máster pages tipadas, e para que elas servem, servem para que você possa acessar diretamente os métodos da master page a partir da pagina onde ela foi aplicada.Sempre que se usa uma master page, por melhor que seja o projeto do layout quase sempre tem alguma coisa que você gostaria de poder definir na master page e mudar na pagina em que você a esta usando. Por exemplo digamos que você tenha uma label com um titulo em todas as paginas do seu site, no mesmo lugar mesma fonte mas o texto diferente, algo como “PAGINA DE ARTIGOS” e “PAGINA DE CADASTRO”, e assim por diante em cada uma das 100 paginas do seu site,Pois temos duas maneiras de fazer isto.

Toda pagina ASP.Net 2.0 tem uma propriedade Master, que nada mas e do que uma estância da pagina master page que esta associada a ela, e por esta propriedade podemos acessar a master page sem o menor problema. Uma das maneiras e por cast do tipo, você pode usar um comando assim.

CType(CType(Me.Master,MasterPage).FindControl("lbltitulo"), Label).Text = "Titula desta pagina"

Legal você mudou o label, maa as custas de dois casts e uma busca na coleção de componentes da pagina, o cast e uma coisa que deve ser evitada o máximo possível por degradar a performance, então a sugestão e tipa a master page da pagina, e isto e uma coisa muito simples. Vamos ver como se faz isto, crie um novo projeto web, adicione um novo item master page, e a configure normalmente, como a sua criatividade permitir.

Cria uma segunda pagina e aplique a sua recém criada master page a ela.Agora acesse o código html da sua pagina clicando em cima da guia souce que fica no canto inferior da tela, repare no inicio da pagina a tag de diretiva de configuração,que é a primeira tag, onde tem a informação da linguagem usada, qual a herança da pagina a master page e outras. Vamos inserir uma linha de código logo abaixo da tag page,e então teremos a nossa pagina tipada.Escreva o seguinte código.

<%@ MasterType VirtualPath="~/MasterPage.master" %>

O que fizemos aqui e informar a nossa pagina qual e o tipo da master page que ela esta usando,o parametro VirualPath recebe o caminho onde estar a master page, caso você tenha mudado o diretorio ou o nome padrão de sua master page, informe o caminho e o nome que você deu a ela

Agora vocÊ poderarar acessar o label da master sem a nescidade de cast, para isto vamos criar uma propriedade publica em nossa master page. Vá a até a master page e cria a seguinte propriedade

Property TituloDaPagina() As String

Get

Return Me.lblTitulo.Text

End Get

Set(ByVal value As String)

Me.lblTitulo.Text = value

Me.Page.Title = value

End Set

End Property

Aqui nos estamos atribuindo o valor da propriedade diretamente ao text do label e ao title da pagina para que atualize também a barra de titulo do navegador.

Voltando a pagina que esta usando a nossa master page agora você pode digitar o seguinte código.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Me.Master.TituloDaPagina = "Primeiro exemplo da Pagina"

End Sub

Veja que o codigo e muito mas limpo facil e sem o indesejavel cast., agora você pode criar qualquer propriedade procedure ou função que julgar necessário na sua master page, e expor-las nas paginas que “herdam” de sua master page.

Valeu e até, e um abraço.