Split image across pages to print - vb.net

I am trying to print an image in vb.net. The size of the image is dynamic, and is derived from a panel, so can span more than 1 page. For this, I am cropping the image and printing the first part, then recursively calling the procedure to print the next section. The first page prints okay, but the subsequent pages are blank, as is the image that is supposed to be on them.
800 is height of page, 1100 is width. All the save images are to pinpoint the problem: restimg.bmp comes up as blank, so the problem seems to be in the second using statement. I know very little about image manipulation, so simple terms and example please.
This is the code.
Sub recersive_print(ByVal WholeImg As Bitmap)
If WholeImg.Height > 800 Then
Dim CropRect As New Rectangle(0, 0, 1100, 800)
Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
Dim restofimg = New Bitmap(1100, WholeImg.Height - 800)
Dim restofingrect As New Rectangle(0, 0, restofimg.Height, restofimg.Width)
Using grp = Graphics.FromImage(CropImage)
grp.DrawImage(WholeImg, New Rectangle(0, 0, CropRect.Width, CropRect.Height), CropRect, GraphicsUnit.Pixel)
End Using
CropImage.Save("E:\cropped.bmp")
Using grp = Graphics.FromImage(restofimg)
grp.DrawImage(WholeImg, New Rectangle(0, CropRect.Height, restofimg.Width, restofimg.Height), restofingrect, GraphicsUnit.Pixel)
End Using
'img_filepath = Application.StartupPath & "\out" & Val(img_filepath) + 1 & ".bmp"
img_to_print = CropImage
'CropImage.Save(img_filepath)
PrintDocument1.Print()
'WholeImg.Dispose()
restofimg.Save("E:\Rest.bmp")
recersive_print(restofimg)
Else
img_to_print = WholeImg
img_to_print.Save("E:\out.bmp")
PrintDocument1.Print()
End If
End Sub
Thanks
EDIT: img_to_print is used in the following way
Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
Dim g As Graphics = e.Graphics
g.DrawImage(img_to_print, 5, 5)
End Sub

800 is width of page, 1100 is height. i believe you mean 1100 width and 800 height.
Sub recersive_print(ByVal WholeImg As Bitmap)
Static i As Integer = 0
i += 1
If WholeImg.Height > 800 Then
Dim CropRect As New Rectangle(0, 0, 1100, 800)
Dim CropImage = New Bitmap(CropRect.Width, CropRect.Height)
Dim restofimg = New Bitmap(1100, WholeImg.Height - 800)
Dim restofingrect As New Rectangle(0, 0, restofimg.Width, restofimg.Height)
Using grp = Graphics.FromImage(CropImage)
grp.DrawImage(WholeImg, CropRect, CropRect, GraphicsUnit.Pixel)
End Using
CropImage.Save("E:\cropped" & i.ToString ".bmp")
Using grp = Graphics.FromImage(restofimg)
grp.DrawImage(WholeImg, restofingrect, New Rectangle(0, CropRect.Height, restofimg.Width, restofimg.Height), GraphicsUnit.Pixel)
End Using
restofimg.Save("E:\Rest" & i.ToString ".bmp")
recersive_print(restofimg)
Else
WholeImg.Save("E:\out.bmp")
End If
End Sub
valter

Related

How to print datagridview data in horizontal line with printdocument?

I have a query in the SqlServer database, the result of that query is played inside a Datagridview, the result can contain from 0 to 100 data or even more if you doubt it.
I'm trying to print the "Identification" column of Datagridview horizontally, but without success. I have researched in several places and nothing too.
screenshot of form with DataGridView
Follow the form code:
Imports System.ComponentModel
Imports System.Data.SqlClient
Imports System.Drawing.Printing
Public Class frm_relatorio_entregas
' Variables used in the module
Dim RelatorioTitulo As String ' Report title
Dim paginaatual As Integer ' Page number being printed
Dim LinhaAtual As Integer ' Current line number being printed
Dim LinhasporPagina As Integer ' Number of lines per page
Dim PosicaoDaLinha As Single ' Position of the line being printed
Dim registro As Integer ' Record being printed
Private Sub Imprimir()
RelatorioTitulo = "Delivery Report"
Dim doc As PrintDocument = New PrintDocument
AddHandler doc.PrintPage, New Printing.PrintPageEventHandler(AddressOf Me.pdRelatorios_Printpage)
AddHandler doc.BeginPrint, New Printing.PrintEventHandler(AddressOf Me.Begin_Print)
Dim dialogo As PrintDialog = New PrintDialog
'dialogo.Document = doc
' If (dialogo.ShowDialog = DialogResult.OK) Then
Dim preview As PrintPreviewDialog = New PrintPreviewDialog()
preview.Document = doc
preview.WindowState = FormWindowState.Maximized
preview.PrintPreviewControl.Zoom = 1.0
preview.ShowDialog()
'End If
End Sub
Private Sub Begin_Print(ByVal sender As Object, ByVal e As Printing.PrintEventArgs)
' Assigning values ​​to variables at the start of printing
LinhaAtual = 0
paginaatual = 1
PosicaoDaLinha = 0
registro = 0
End Sub
Private Sub pdRelatorios_Printpage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
' Margin variables (e.MarginBounds obtain the rectangular area that represents the part of the page within the margins.)
Dim MargemEsquerda As Single = e.MarginBounds.Left
Dim MargemDireita As Single = e.MarginBounds.Right
Dim MargemSuperior As Single = e.MarginBounds.Top
Dim MargemInferios As Single = e.MarginBounds.Bottom
'Pen class defines an object used to define lines and curves
Dim CanetaDaImpressora As Pen = New Pen(Color.Black, 1)
'Variables of the fonts used (the font class defines a specific format for text, including font face, size and style attributes.
Dim FonteNegrito As Font
Dim FonteTitulo As Font
Dim FonteSubTitulo As Font
Dim FonteRodape As Font
Dim FonteNormal As Font
Dim font1 As Font
'Define effects on fonts used
FonteNegrito = New Font("Arial", 9, FontStyle.Bold)
FonteTitulo = New Font("Arial", 13, FontStyle.Bold)
FonteSubTitulo = New Font("Arial", 9, FontStyle.Bold)
FonteRodape = New Font("Arial", 8)
FonteNormal = New Font("Calibri", 9)
font1 = New Font("Segoe UI", 8, FontStyle.Bold)
'Print the title of the report
e.Graphics.DrawString(RelatorioTitulo, FonteTitulo, Brushes.Black, MargemEsquerda + 200, 30, New StringFormat)
If DateTimePicker1.Value = DateTimePicker1.Value Then
e.Graphics.DrawString(DateTimePicker1.Value, FonteSubTitulo, Brushes.Black, MargemEsquerda + 900, 30, New StringFormat)
Else
e.Graphics.DrawString(DateTimePicker1.Value & " - " & DateTimePicker1.Value, FonteSubTitulo, Brushes.Black, MargemEsquerda + 900, 30, New StringFormat)
End If
'Define the number of lines per page
LinhasporPagina = CInt(e.MarginBounds.Height / New Font("Calibri", 18).GetHeight(e.Graphics) - 2)
While (LinhaAtual <= LinhasporPagina AndAlso registro <= DataGridView1.Rows.Count - 1)
PosicaoDaLinha = MargemSuperior + (LinhaAtual * New Font("Calibri", 18).GetHeight(e.Graphics) + 30)
e.Graphics.DrawString(DataGridView1.Rows(registro).Cells(0).Value.ToString, New Font("Calibri", 8, FontStyle.Bold), Brushes.Black, 30, PosicaoDaLinha, New StringFormat())
'Increment record
registro += 1
'increment line
LinhaAtual += 1
End While
'baseboard
e.Graphics.DrawLine(CanetaDaImpressora, 20, MargemInferios + 15, 1150, MargemInferios + 15)
e.Graphics.DrawString(System.DateTime.Now.ToString(), FonteRodape, Brushes.Black, 30, MargemInferios + 15, New StringFormat())
e.Graphics.DrawString("Página : " & paginaatual, FonteRodape, Brushes.Black, 1070, MargemInferios + 15, New StringFormat)
'Increase the page number
paginaatual += 1
'Here check if you are going to open a new page
If (LinhaAtual > LinhasporPagina) Then
' When you open a new page, you have to reset LlinhaAtual
e.HasMorePages = True
LinhaAtual = 0
Else
e.HasMorePages = False
End If
End Sub
Private Sub frm_relatorio_entregas_Load(sender As Object, e As EventArgs) Handles MyBase.Load
txtcod.Clear()
txtrazao.Clear()
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
DateTimePicker1.ResetText()
DateTimePicker2.ResetText()
ComboBox1.SelectedIndex = 0
txtcod.Select()
End Sub
Private Sub txtcod_KeyUp(sender As Object, e As KeyEventArgs) Handles txtcod.KeyUp
If e.KeyCode = Keys.F2 Then
frm_consulta_cliente_cadastro.ShowDialog()
End If
End Sub
Private Sub consulta()
DataGridView1.Rows.Clear()
Cursor.Current = Cursors.WaitCursor
Dim consultando As New frm_aguarde_consultando
consultando.Show()
' Set cursor as hourglass
Application.DoEvents()
Dim ano, mes, dia As Integer
Dim var1data, var2data As Date
Dim dinicio, dfim As String
var1data = DateTimePicker1.Value '.ToString.Substring(0, 10)
dia = var1data.Day
mes = var1data.Month
ano = var1data.Year
dinicio = ano & "-" & mes & "-" & dia
var2data = DateTimePicker2.Value
dia = var2data.Day
mes = var2data.Month
ano = var2data.Year
dfim = ano & "-" & mes & "-" & dia
Using sqlcoon As SqlConnection = GetConnectionsql()
Dim READER As SqlDataReader
Try
sqlcoon.Open()
Dim Query As String
Query = "select MOV_IDENTIFICACAO,MOV_PROTOCOLO,MOV_DATADOC,MOV_SITUACAO,MOV_DATAENTREGA,MOV_HORAENTREGA,MOV_SITEND_CODIGO
from movimento where MOV_DATADOC = '" & dinicio & "'
AND MOV_CLI_CODIGO = '" & txtcod.Text & "' AND MOV_SITUACAO = '" & "E" & "'
AND CAST(MOV_DATAENTREGA AS DATE) = '" & dfim & "' "
Dim COMMAND As SqlCommand = New SqlCommand(Query, sqlcoon)
READER = COMMAND.ExecuteReader
While READER.Read
Dim MOV_IDENTIFICACAO = READER("MOV_IDENTIFICACAO")
Dim MOV_DATADOC = READER("MOV_DATADOC")
Dim MOV_DATAENTREGA = READER("MOV_DATAENTREGA")
Dim MOV_PROTOCOLO = READER("MOV_PROTOCOLO")
Dim MOV_SITUACAO = READER("MOV_SITUACAO")
Dim MOV_SITEND_CODIGO = READER("MOV_SITEND_CODIGO")
DataGridView1.Rows.Add(MOV_IDENTIFICACAO, MOV_PROTOCOLO, MOV_DATADOC, MOV_SITUACAO, MOV_DATAENTREGA, MOV_SITEND_CODIGO)
End While
READER.Close()
sqlcoon.Close()
''--------------'''''''''
Label9.Text = DataGridView1.Rows.Count
For Each linha In DataGridView1.Rows
Dim altura As Integer = 17
linha.height = altura
Next
If DataGridView1.Rows.Count >= 0 Then
' Set cursor as default arrow
Cursor.Current = Cursors.Default
' Hide the please wait form
consultando.Hide()
End If
Catch ex As SqlException
MessageBox.Show(ex.Message)
Finally
' sqlcoon.Dispose()
End Try
sqlcoon.Open()
Try
For r As Integer = 0 To DataGridView1.Rows.Count - 1
Dim COMMAND3 As SqlCommand
Dim READER3 As SqlDataReader
Dim Query_3 As String
Query_3 = "select IMOV_CODIGORECBTO from imovimento where IMOV_MOV_IDENTIFICACAO ='" & DataGridView1.Rows(r).Cells(0).Value.ToString & "'"
COMMAND3 = New SqlCommand(Query_3, sqlcoon)
READER3 = COMMAND3.ExecuteReader
While READER3.Read
Dim IMOV_CODIGORECBTO = READER3("IMOV_CODIGORECBTO")
'DataGridView1.Columns(6).HeaderCell.Value = "ID"
DataGridView1.Rows(r).Cells(6).Value = IMOV_CODIGORECBTO
End While
READER3.Close()
Next
DataGridView1.Sort(DataGridView1.Columns(6), ListSortDirection.Ascending)
sqlcoon.Close()
Catch ex As SqlException
MsgBox(ex.Message)
End Try
End Using
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
consulta()
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Imprimir()
End Sub
End Class
screenshot of what is currently being printed
screenshot of I would like the result to be:
Would anyone have any ideas, tools or means to help me? I thank.
An alternative would be to:
Get just the column values that you want.
Join the values from #1 using a delimiter, such as a tab.
Use the Graphics.DrawString method (documentation) for the value from #2.
There will need to be some considerations:
You will need to measure the string from #3 to see if it exceeds the bounds of your print document
If so, then you will need to print multiple pages, picking up where you left off.
Update
Per the OP's request, here is an example. Keep in mind that it doesn't account for the considerations listed above. That will take a bit of effort in debugging to account for those points:
Private Sub pdRelatorios_PrintPage(sender As Object, e As PrintPageEventArgs) Handles pdRelatorios.PrintPage
Dim title = "Relatorio de Entregas"
Dim titleFont = New Font(Font.FontFamily, Convert.ToSingle(Font.Size * 1.5), FontStyle.Bold)
Dim titlePosition = New PointF(Convert.ToSingle(e.MarginBounds.X * 2 - e.PageBounds.Width / 2), e.MarginBounds.Y)
Dim titleSize = e.Graphics.MeasureString(title, titleFont)
e.Graphics.DrawString(title, titleFont, SystemBrushes.ControlText, titlePosition)
Dim cellValues = DataGridView1.Rows.Cast(Of DataGridViewRow).Select(Function(row) row.Cells(0)?.Value?.ToString())
Dim joinedCellValues = String.Join(Constants.vbTab, cellValues)
Dim bodyBounds = New RectangleF(e.MarginBounds.X, Convert.ToSingle(titleSize.Height + e.MarginBounds.Y), e.PageBounds.Width - e.MarginBounds.X * 2, e.PageBounds.Height - e.MarginBounds.Y * 2)
e.Graphics.DrawString(joinedCellValues, Font, SystemBrushes.ControlText, bodyBounds)
End Sub

How to print loop result in next blank page if Current Page is Full

I have have the following codes to print the voucher, but I face problem in loop section while printing the SQL query result if the page is full it reprints using same page.
enter image description here
Private Sub ATATprint_PrintPage(sender As Object, e As PrintPageEventArgs) Handles ATATprint.PrintPage
Dim Brush1 As New SolidBrush(Color.Black)
Dim ValueBrush As New SolidBrush(Color.DarkGreen)
Dim lblFont As Font = New Font("B Yekan", 10, FontStyle.Regular)
Dim ValueFont As Font = New Font("Agency FB", 10, FontStyle.Bold)
Dim ypos As Integer = 300
Dim pn As Integer = 1
Dim str(6) As String
str(0) = TrnAccountType
str(1) = TrnAccountNo
str(2) = TrnAccountName
str(3) = TrnCurrecy
str(4) = TrnExRate
str(5) = TrnAmount
str(6) = TrnNarration
Try
Dim adapter As New SqlDataAdapter("select case when trd_DrCr = 'Dr' then 'Debit' else 'Credit' end,
isnull(acc_Ccy, '')+'-'+Convert(nvarchar,trd_Account), acc_Name, trd_ccy, format(trd_ExRate,'#,###,###.0000'), format(trd_Amount, '#,###,###.00'), trd_Narration
from TransactionDetails join Accounts on Accounts.acc_Number = TransactionDetails.trd_Account where trd_TrnRef = '" & fncTrnReference.Text & "'", connection)
Dim table As New DataTable
adapter.Fill(table)
For row As Integer = 0 To table.Rows.Count - 1
For col As Integer = 0 To table.Columns.Count - 1
e.Graphics.DrawString(str(col), lblFont, Brush1, 100, ypos)
e.Graphics.DrawString(table.Rows(row)(col).ToString, ValueFont, ValueBrush, 200, ypos)
ypos += 15
Next
ypos += 30
If ypos > 900 Then
ypos = 200
e.HasMorePages = True
End If
Next
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
e.HasMorePages = False
End Sub
You need to fill the adapter outside of the PrintPage method and also keep track of the value of the row variable from your for loop. When the PrintPage method is called again, start your For loop from that point.
Also, you don't need to create new SolidColorBrushes if you are using the standard colors.
Finally, be sure to dispose your Fonts when finished. I did this in the Finally block of the code below.
This code is not tested so it may require some changes to meet your needs, but it should give you some ideas.
'These variables are declared outside the PrintPage method so they retain their values between calls.
Private adapter As SqlDataAdapter = Nothing
Private savedRowValue As Integer = 0
Private table As DataTable = Nothing
Private Sub ATATprint_PrintPage(sender As Object, e As PrintPageEventArgs) Handles ATATprint.PrintPage
Dim Brush1 As Brushes.Black 'Use the standard brushes here.
Dim ValueBrush Brushes.DarkGreen
Dim lblFont As Font = New Font("B Yekan", 10, FontStyle.Regular)
Dim ValueFont As Font = New Font("Agency FB", 10, FontStyle.Bold)
Dim ypos As Integer = 300
Dim pn As Integer = 1
Dim str(6) As String
str(0) = TrnAccountType
str(1) = TrnAccountNo
str(2) = TrnAccountName
str(3) = TrnCurrecy
str(4) = TrnExRate
str(5) = TrnAmount
str(6) = TrnNarration
Try
'Create the data adapter and fill the DataTable only on the first time the PrintPage method is called
If adapter Is Nothing Then
adapter As New SqlDataAdapter("select case when trd_DrCr = 'Dr' then 'Debit' else 'Credit' end,
isnull(acc_Ccy, '')+'-'+Convert(nvarchar,trd_Account), acc_Name, trd_ccy, format(trd_ExRate,'#,###,###.0000'), format(trd_Amount, '#,###,###.00'), trd_Narration
from TransactionDetails join Accounts on Accounts.acc_Number = TransactionDetails.trd_Account where trd_TrnRef = '" & fncTrnReference.Text & "'", connection)
table = New DataTable
adapter.Fill(table)
End If
'Start the For loop at the saved value instead of at 0
For row As Integer = savedRowValue To table.Rows.Count - 1
For col As Integer = 0 To table.Columns.Count - 1
e.Graphics.DrawString(str(col), lblFont, Brush1, 100, ypos)
e.Graphics.DrawString(table.Rows(row)(col).ToString, ValueFont, ValueBrush, 200, ypos)
ypos += 15
Next
ypos += 30
If ypos > 900 Then
ypos = 200
e.HasMorePages = True
savedRowValue = row + 1 'Save the value of the current row
Exit Sub
End If
Next
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
lblFont.Dispose() 'Dispose of your fonts here
ValueFont.Dispose()
End Try
e.HasMorePages = False
End Sub

Display an image from datagridview to a picturebox

Can anyone help me with this one? The scenario is that when I click any columns in a datagridview it will display the image to a picturebox
Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
If e.RowIndex >= 0 Then
Dim row As DataGridViewRow
row = Me.DataGridView1.Rows(e.RowIndex)
lblProperty.Text = row.Cells("Column1").Value.ToString
txtType.Text = row.Cells("Column2").Value.ToString
txtPrice.Text = row.Cells("Column3").Value.ToString
txtBed.Text = row.Cells("Column4").Value.ToString
txtBath.Text = row.Cells("Column5").Value.ToString
txtFootages.Text = row.Cells("Column6").Value.ToString
txtStatus.Text = row.Cells("Column7").Value.ToString
txtYear.Text = row.Cells("Column8").Value.ToString
txtDesc.Text = row.Cells("Column9").Value.ToString
Dim bytes As [Byte]() = row.Cells("Column10").Value
Dim ms As New MemoryStream(bytes)
pbImage.Image = Image.FromStream(ms)
txtDate.Text = row.Cells("Column11").Value.ToString
txtAddress.Text = row.Cells("Column12").Value.ToString
txtStories.Text = row.Cells("Column13").Value.ToString
End If
End Sub
It has an error Unable to cast object of type 'System.String' to type 'System.Byte[]'.
Dim bytes As Byte() = Datagridview1.CurrentRow.Cells(5).Value
Using ms As New MemoryStream(bytes)
Picturebox1.Image = Image.FromStream(ms)
End Using
Maybe you are over thinking it, there is a function to easily draw bitmaps.
I would suggest using the DrawToBitmap on the DataGridView by using the ColumnBounds
example this:
Dim GridBitmap as new Bitmap(DataGridView1.GetColumnDisplayRectangle(row.Cells("Column10").ColumnIndex,false).width,DataGridView1.GetColumnDisplayRectangle(row.Cells("Column10").ColumnIndex,false).height)
DataGridView1.DrawToBitmap(GridBitmap,DataGridView1.GetColumnDisplayRectangle(row.Cells("Column10").ColumnIndex,false))
pbImage.Image = GridBitmap
It might need to be adapted to your DataGridView.
Note, Instead of this:
Dim bytes As [Byte]() = row.Cells("Column10").Value
Dim ms As New MemoryStream(bytes)
pbImage.Image = Image.FromStream(ms)
Still having problem? Try this:
New project.
Add a DataGridView (DataGridView1) to your form.
Add some random columns to your gridview.
Add a Picture Box to your form (PictureBox1)
Make sure the Picture box property "SizeMode" is set to auto size.
Add an event forDataGridView1.MouseDown.
Add this code to the event.
Dim GridBitmap As New Bitmap(DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Width + DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Left, DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Height)
Dim GridColRectangle As New Rectangle(0, 0, DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Width + DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Left, DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Height + DataGridView1.GetColumnDisplayRectangle(e.ColumnIndex, True).Top)
DataGridView1.DrawToBitmap(GridBitmap, GridColRectangle)
PictureBox1.Image = GridBitmap
You may need to adjust it to your own application.

how to create jpg file from given string

I have converted given string to JPG file format. But i need those image string in center. Iam getting output from leftside of rectangle.
Dim stext As String = "Testing"
Dim format As StringFormat = New StringFormat()
Dim MyRect As Rectangle = New Rectangle(0, 0, 400, 800)
Dim MyGraphics As Graphics = Me.CreateGraphics()
Dim MyImg As Image = New Bitmap(391, 132, MyGraphics)
Dim imageGraphics As Graphics = Graphics.FromImage(MyImg)
imageGraphics.FillRectangle(Brushes.White, MyRect)
format.Alignment = StringAlignment.Center
format.LineAli`enter code here`gnment = StringAlignment.Center
imageGraphics.DrawString("Testing", New Font("Times New Roman", 30, Drawing.FontStyle.Bold), Brushes.Black, RectangleF.op_Implicit(MyRect))
MyGraphics.DrawImage(MyImg, MyRect)
MyImg.Save(Destfilename & "/" & "test.jpg")
I have given this code output. But i need this string to in center position. How to do, Kindly help me through.
Output: https://drive.google.com/file/d/0B_nzYHWVJJ7Ka3N0V2NmRnl3UFk/view?usp=sharing
I hope this example could help you
Private Sub CenterTextAt(ByVal gr As Graphics, ByVal txt As _
String, ByVal x As Single, ByVal y As Single)
' Mark the center for debugging.
gr.DrawLine(Pens.Red, x - 10, y, x + 10, y)
gr.DrawLine(Pens.Red, x, y - 10, x, y + 10)
' Make a StringFormat object that centers.
Dim sf As New StringFormat
sf.LineAlignment = StringAlignment.Center
sf.Alignment = StringAlignment.Center
' Draw the text.
gr.DrawString(txt, Me.Font, Brushes.Black, x, y, sf)
sf.Dispose()
End Sub

Unable To Override OnDrawItem Correctly With OwnerDrawn Listview Using LargeIcon View

I am trying to finalize my custom Listview control with that uses LargeIcon view. I am trying to custom draw the Item within the OnDrawItem event.
So far I have the following code:
Protected Overrides Sub OnDrawItem(e As DrawListViewItemEventArgs)
Dim flags As TextFormatFlags
Dim subColour As Color = Color.Black
Dim subBackColour As Color = Color.Empty
Try
If Not (e.State And ListViewItemStates.Selected) = 0 Then
'Draw the background for a selected item.
e.Graphics.FillRectangle(System.Drawing.SystemBrushes.Highlight, e.Bounds)
e.DrawFocusRectangle()
Else
'Draw the background for an unselected item.
e.Graphics.FillRectangle(System.Drawing.SystemBrushes.Control, e.Bounds)
End If
e.DrawBackground()
'Draw the Icons
e.Graphics.SmoothingMode = SmoothingMode.HighQuality
e.Item.ImageList.Draw(e.Graphics, New Point(20, 22), 0)
e.Graphics.ResetTransform()
e.DrawFocusRectangle()
'Draw the Text
flags = TextFormatFlags.HorizontalCenter Or TextFormatFlags.Bottom
Dim rec As New Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width + 10, e.Bounds.Height + 10)
TextRenderer.DrawText(e.Graphics, e.Item.Text, Me.Font, rec, subColour, subBackColour, flags)
MyBase.OnDrawItem(e)
Catch ex As Exception
ErrorTrap(ex, "ListView_Stores: OnDrawItem()")
End Try
End Sub
However, when I run my code, it draws my Text and Icon correctly but I can't seem to get the Item Highlighting correct as per my picture below:
It's not highlighting correctly with any colour (just a dotted square) and it's not even highlighting the entire bounds of the object - it chops half way through the text.
Wondering if someone could assist or at least point me in the right direction.
Thanks
Okay, with a LOT of research and trial and error I have managed to achieve exactly what I was after by overriding the OnDraw event of the ListItem in my custom class. I'm unsure if it's the correct (or preferred) method, but I am happy with the outcome.
I ended up making use of the ColorMatrix method to overlay a 'blue' highlight color to my selected item. Then when unselected I simply set my ColorMatrix to nothing
My new revised code:
Protected Overrides Sub OnDrawItem(e As DrawListViewItemEventArgs)
Dim storeName_flags As New StringFormat
Dim storeCode_flags As New StringFormat
Dim matrixItems As Single()() = { _
New Single() {0, 0, 0, 0, 0}, _
New Single() {0, 0.6F, 0, 0, 0}, _
New Single() {0, 0, 3, 0, 0}, _
New Single() {0, 0, 0, 1, 0}, _
New Single() {0, 0, 0, 0, 1}}
Dim colorMatrix As ColorMatrix = New ColorMatrix(matrixItems)
Dim imgattr As ImageAttributes = New ImageAttributes
Dim bmp As Bitmap = New Bitmap(My.Resources.Store_Good)
Try
'Get StoreName and StoreNum from original e.Item.Text
Dim StoreDetail As String() = e.Item.Text.Split(New Char() {"|"c})
Dim StoreName As String = StoreDetail(0)
Dim StoreNum As String = StoreDetail(1)
'Declare Image Rectangle as the max size of the bitmap
Dim Image_Width As Integer = bmp.Width
Dim Image_Height As Integer = bmp.Height
Dim imgRect As New Rectangle(e.Bounds.X + ((e.Bounds.Width - Image_Width) / 2), e.Bounds.Y, Image_Width, Image_Height)
'Declare Text Rectangle
Dim textSize As SizeF = New SizeF(e.Graphics.MeasureString(StoreName, Me.Font, 100))
Dim textRect As New Rectangle(e.Bounds.X + ((e.Bounds.Width - textSize.Width) / 2), e.Bounds.Bottom - textSize.Height, textSize.Width + 1, textSize.Height)
e.Graphics.SmoothingMode = SmoothingMode.HighQuality
If e.Item.Selected Then
'Set the Image to use the 'blue' color matrix and highlight the text
imgattr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
e.Graphics.FillRectangle(System.Drawing.SystemBrushes.Highlight, textRect)
Else
'Turn off the color matrix and draw the default background
imgattr = Nothing
e.DrawBackground()
End If
'Draw the Image
e.Graphics.DrawImage(bmp, imgRect, 0, 0, Image_Width, Image_Height, GraphicsUnit.Pixel, imgattr)
storeCode_flags.Alignment = StringAlignment.Center
e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
Dim rect2 As New Rectangle(e.Bounds.X + ((e.Bounds.Width - Image_Width) / 2) + 1, e.Bounds.Y + 15, Image_Width, Image_Height)
e.Graphics.DrawString(StoreNum, New Font(CustomFnt.Families(0), 24, FontStyle.Bold, GraphicsUnit.Pixel), Brushes.Black, rect2, storeCode_flags)
'Draw the Text
storeName_flags.Alignment = StringAlignment.Center
storeName_flags.LineAlignment = StringAlignment.Far
storeName_flags.FormatFlags = StringFormatFlags.FitBlackBox
e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
e.Graphics.DrawString(StoreName, Me.Font, Brushes.Black, textRect, storeName_flags)
bmp.Dispose()
MyBase.OnDrawItem(e)
Catch ex As Exception
ErrorTrap(ex, "ListView_Stores: OnDrawItem()")
End Try
End Sub
Now instead of this:
I get this: