Printing from Xojo Application

I have read a few posts about being able to print simply from a Xojo application. The consensus seems to be that the inbuilt report handler is not very good (I agree with this).

I have created a class that operates in a similar fashion to a Listbox. You drop it on to your window, set its basic properties and then populate it in code - exactly like you would a listbox component. In the zip file is a Xojo project which has the class embedded. Just copy the whole sbPrint folder to your project.

I am releasing it to the community in the link that follows this post. Questions and enhancements would be readily received. This is a licence free class as I have received so much help from this forum that I would like to give back.

Download: https://dl.dropboxusercontent.com/u/10747116/RealStudioClasses/sabPrinting.zip

App Open event: This item does not exist: SetRegister

Remove the line and it runs fine.

Thanks! :wink:

Thank You , very fine

I wrote a method to print a ListBox, detects If Portrait or landscape needed

2 problems I have

HeaderTextsize is not working (IDE and with code)
missing lines on the right side and on the first row

Sub PrintListBox(source as listbox, mysp as sabPrint, margins as string)
  mysp.DeleteAllRows
  mysp.ColumnCount = source.ColumnCount
  dim box As structBox
  
  ////// column headers
  dim lbheaders as string
  if source.HasHeading = false then
    for l as integer = 0 to source.ColumnCount-1
      lbheaders = lbheaders + EndOfLine + str(l + 1)
    next
    lbheaders = lbheaders.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",")
    mysp.InitialValue = lbheaders
  else
    mysp.InitialValue = source.Heading(-1).ReplaceAll(chr(9),",")
  end if
  ////// end column headers
  
  //// columnwidths
  dim lbwidth, lbwidthAll as integer
  dim lbcolwidths as string
  for l as integer = 0 to source.ColumnCount-1
    lbwidthAll = lbwidthAll + source.Column(l).WidthActual
  next
  for l as integer = 0 to source.ColumnCount-1
    lbwidth = round(source.Column(l).WidthActual) / round(lbwidthAll) * 100
    lbcolwidths =  lbcolwidths + EndOfLine + str(lbwidth)
  next
  lbcolwidths = lbcolwidths.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",")
  // MsgBox lbcolwidths
  mysp.ColumnWidthsPerCent = lbcolwidths
  //// end columnwidths
  
  ///// PageOrientation
  if lbwidthAll > 750 then 
    mysp.PageOrientation = 1
  else
    mysp.PageOrientation = 0
  end if
  ///// end PageOrientation
  
  ////// textsize
  if source.TextSize = 0 then source.TextSize = 10
  dim mytextsize as integer = source.TextSize
  dim z as double = 750 / lbwidthAll
  if z < 1  then
    mytextsize = round(source.TextSize * z)
  end if
  mysp.TextSize = mytextsize
  mysp.HeaderTextSize = mytextsize
  mysp.HeaderTextFont = source.TextFont
  mysp.TextFont = source.TextFont
  ////// end textsize 
  
  /////// column alignment
  dim colalign as integer
  dim colalignstring as String
  for cal as integer = 0 to source.ColumnCount-1
    colalign = source.ColumnAlignment(cal)
    colalignstring = colalignstring + EndOfLine + str(colalign)
  next
  colalignstring = colalignstring.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",") _
  .ReplaceAll("0","l").ReplaceAll("1","l").ReplaceAll("2","c") _
  .ReplaceAll("3","r").ReplaceAll("4","l")
  mysp.Alignment = colalignstring
  mysp.HeaderAlignment = colalignstring
  /////// end column alignment
  
  ////// add ListBox Content
  for row as integer = 0 to source.ListCount-1
    mysp.AddRow
    for j as integer = 0 to source.ColumnCount
      mysp.Cell(row, j) = source.Cell(row, j)
      mysp.CellBox(mysp.LastIndex, j) = box
    next
    box.Bottom = True
    box.Left = True
    box.Right = True
    box.Top = False
  next
  mysp.Margins = margins
  ////// end add ListBox Content
  mysp.Print
End Sub

[quote=183800:@Markus Winter]App Open event: This item does not exist: SetRegister

Remove the line and it runs fine.

Thanks! ;-)[/quote]
Fixed now.

I’m amazed at the speed that you have made this function!

[quote=183850:@Axel Schneider]////// add ListBox Content
for row as integer = 0 to source.ListCount-1
mysp.AddRow
for j as integer = 0 to source.ColumnCount
mysp.Cell(row, j) = source.Cell(row, j)
mysp.CellBox(mysp.LastIndex, j) = box
next
box.Bottom = True
box.Left = True
box.Right = True
box.Top = False
next
mysp.Margins = margins
////// end add ListBox Content
mysp.Print
[/quote]
You should set the ‘box’ before calling it.

for row as integer = 0 to source.ListCount-1 mysp.AddRow box.Bottom = True box.Left = True box.Right = True box.Top = False for j as integer = 0 to source.ColumnCount mysp.Cell(row, j) = source.Cell(row, j) mysp.CellBox(mysp.LastIndex, j) = box next next
I would also use

mysp.Cell(mysp.LastIndex, j)

instead of

mysp.Cell(row, j)

I can’t replicate the HeaderTextSize issue. Whenever I change it in my development environment it works fine! Can you send to me your test project?

Thanks for the feedback, much appreciated.

Sorry, forgot about the missing lines.

The first line is solved with the previous code. Your ‘box’ setting was after the print line and should have been before.

The missing right line on the last column is probably due to your internal calculations of the five columns in percentage terms. You should place a check that they all add up to EXACTLY 100. I cannot guarantee that this will be the case when calculated. I will look at this tomorrow and see if I can fix it and post again.

Thanks again.

the calculation result was 97, I changed the line to

lbwidth = round((source.Column(l).WidthActual) / lbwidthAll * 100)

now I get always 101
maybe my calculation is wrong?

Axel

I have changed your print routine to the following:

[code]Sub PrintListBox(extends source as listbox, mysp as sabPrint, optional DrawBox As Boolean = False)
’ Simon Berridge Changes
’ 01/05/2015 Changed parameter Listbox to ‘extends’
’ 01/05/2015 Removed parameter Margins as set in the sabPrint class
’ 01/05/2015 Removed the reference to ‘margins’ in the code
’ 01/05/2015 Added the Optional parameter DrawBoxes to allow switch for drawing the cell outlines
’ 01/05/2015 Now calculates the exact column width percentages
’ 01/05/2015 The Header Text Size and Alignment are set in the sabPrint class so removed those references

mysp.DeleteAllRows
mysp.ColumnCount = source.ColumnCount
dim box As structBox

////// column headers
dim lbheaders as string
if source.HasHeading = false then
for l as integer = 0 to source.ColumnCount-1
lbheaders = lbheaders + EndOfLine + str(l + 1)
next
lbheaders = lbheaders.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",")
mysp.InitialValue = lbheaders
else
mysp.InitialValue = source.Heading(-1).ReplaceAll(chr(9),",")
end if
////// end column headers

//// columnwidths
dim lbwidth, lbwidthAll as integer
dim lbwidth2(-1) As integer ’ Added to calculate EXACT column widths
dim lbcolwidths as string
for l as integer = 0 to source.ColumnCount-1
lbwidthAll = lbwidthAll + source.Column(l).WidthActual
next

’ -----------------------------------------------------------------------
’ 01/05/2015 Simon Berridge
’ Now calculate the exact column widths
for l as integer = 0 to source.ColumnCount-1
lbwidth = round(source.Column(l).WidthActual) / round(lbwidthAll) * 100
lbwidth2.Append lbwidth
next
dim w As Integer = 100
for l as integer = 0 to source.ColumnCount - 2
w = w - lbwidth2(l)
next
lbwidth2(lbwidth2.Ubound) = w
for l as integer = 0 to source.ColumnCount-1
lbcolwidths = lbcolwidths + EndOfLine + str(lbwidth2(l))
next
’ End of added code
’ -----------------------------------------------------------------------

lbcolwidths = lbcolwidths.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",")
// MsgBox lbcolwidths
mysp.ColumnWidthsPerCent = lbcolwidths
//// end columnwidths

///// PageOrientation
if lbwidthAll > 750 then
mysp.PageOrientation = 1
else
mysp.PageOrientation = 0
end if
///// end PageOrientation

////// textsize
if source.TextSize = 0 then source.TextSize = 10
dim mytextsize as integer = source.TextSize
dim z as double = 750 / lbwidthAll
if z < 1 then
mytextsize = round(source.TextSize * z)
end if
mysp.TextSize = mytextsize

’ 01/05/2015 Simon Berridge
’ Commented out these two lines as they should be set in the sabPrint class dropped on the Window.
’ mysp.HeaderTextSize = mytextsize
’ mysp.HeaderTextFont = source.TextFont
mysp.TextFont = source.TextFont
////// end textsize

/////// column alignment
dim colalign as integer
dim colalignstring as String
for cal as integer = 0 to source.ColumnCount-1
colalign = source.ColumnAlignment(cal)
colalignstring = colalignstring + EndOfLine + str(colalign)
next
colalignstring = colalignstring.Replace(EndOfLine,"").ReplaceAll(EndOfLine,",") _
.ReplaceAll(“0”,“l”).ReplaceAll(“1”,“l”).ReplaceAll(“2”,“c”) _
.ReplaceAll(“3”,“r”).ReplaceAll(“4”,“l”)
mysp.Alignment = colalignstring

’ 01/05/2015 Simon Berridge
’ Commented out this line as the alignment is set in the class on the Window…
’ mysp.HeaderAlignment = colalignstring
/////// end column alignment

////// add ListBox Content
for row as integer = 0 to source.ListCount-1
mysp.AddRow
box.Bottom = True
box.Left = True
box.Right = True
box.Top = False
for j as integer = 0 to source.ColumnCount
mysp.Cell(row, j) = source.Cell(row, j)
’ 01/05/2015 Simon Berridge
'Added for new parameter
if DrawBox then
mysp.CellBox(mysp.LastIndex, j) = box
end if
next
next
’ 01/05/2015 Simon Berridge
’ Commented out this line as set in the class dropped on the window.
’ mysp.Margins = margins
////// end add ListBox Content
mysp.Print
End Sub
[/code]
As you are passing a sabPrint instance into your routine there is no need to set margins and the header text stuff. See the comments in the code.

I have added your listbox print routine to the sabPrintCommon module so that it is now globally available.

I have also fixed the right hand line printing.

The latest version is here:
https://dl.dropboxusercontent.com/u/10747116/RealStudioClasses/sabPrinting.zip

Thanks for your input, much appreciated.

Simon.

Any chance of multiline printing?

Funny you should ask

Cells spanning pages not implemented.

[quote=183963:@Peter Fargo]Funny you should ask

Cells spanning pages not implemented.[/quote]
Peter

Please would you share?

Simon.

It’s just some old reporting code I duct taped onto your code. There are some things that should be changed to make it fit your code better but I’ll leave that up to you.

modified project

Note: I recommend letting Simon rework the attached code before using it. It works fine but was thrown together as a proof of concept. i.e.: some hard coded fudge factors, etc.

Very strange. I had the project in version control format and saved it to binary and all the colors changed?

I have heard of that happening before with one of the 2014 IDEs. You may want to bug it and attach both formats for review.

I found #27366 which appears to be the same. I’ve updated the case and attached samples. @Simon Berridge normally I would have asked first but where you have posted the unencrypted code I didn’t think it would be an issue.

to add vertical lines in the Table Header you can add this at the end of PrintTableHeader

  dim x as integer = - 1
  for rx as integer = 0 to (ColumnCount-1)
    g.ForeColor = TableHeaderOutlineColor
    g.DrawLine(x, 0, x, g.Height)
    x = x + pc(rx)
  next

Peter and Axel

Thank you.

Will work on it now.

Simon.

Now done.

Table column lines added.
Multi-line capability now added.

You can download and try it again.

Thanks for all input. much appreciated.

Simon.

Sorry, forgot the link.

https://dl.dropboxusercontent.com/u/10747116/RealStudioClasses/sabPrinting.zip

Latest additions:

CellAlignment - allows individual cells to be aligned Left, Centre or Right.