How To ... Print Using DotNet

How To > Use DotNet > Print Using DotNet

The following tutorials shows the basics of displaying and printing a text file using DotNet.

Related topics:

DotNet In MAXScript

dotNetObject

MaxCustomControls Assembly

NATURAL LANGUAGE

Define a MaxForm using the MaxCustomControls assembly containing a text box and two buttons.

Implement a function to print a text file to be called by the Print button's event handler.

Implement a function to open a text file when the event handler of the Open File button is called.

Register the event handlers.

Ensure the event handlers will not be garbage collected.

EXAMPLE

--Example code provided by Artur Leão
(
dotNet.loadAssembly "MaxCustomControls.dll"
local filetoprint = ""
local printFont = dotnetobject "System.Drawing.Font" "Arial" 10
local streamReader
 
hForm = dotNetObject "MaxCustomControls.MaxForm"
hForm.Size = dotNetObject "System.Drawing.Size" 640 480
hForm.FormBorderStyle = (dotnetclass "System.Windows.Forms.FormBorderStyle").FixedToolWindow
hForm.Text = "Printing with dotNet"
hForm.ShowInTaskbar = False
 
txtTextBox = dotNetObject "System.Windows.Forms.TextBox"
txtTextBox.MultiLine = True
txtTextBox.WordWrap = False
txtTextBox.Size = dotNetObject "System.Drawing.Size" 500 430
txtTextBox.Location = dotNetObject "System.Drawing.Point" 10 10
txtTextBox.ScrollBars = txtTextBox.ScrollBars.Both
 
btnOpenFile = dotNetObject "System.Windows.Forms.Button"
btnOpenFile.Location = dotNetObject "System.Drawing.Point" 520 10
btnOpenFile.Text = "Open File"
 
btnPrint = dotNetObject "System.Windows.Forms.Button"
btnPrint.Location = dotNetObject "System.Drawing.Point" 520 40
btnPrint.Text = "Print"
 
frmOpenFileDialog = dotNetObject "System.Windows.Forms.OpenFileDialog"
frmOpenFileDialog.Filter = "txt files (*.txt)|*.txt|Script Files (*.ms)|*.ms|ini files (*.ini)|*.ini|XML files (*.xml)|*.xml|All files (*.*)|*.*"
 
fn btnOpenFile_Click = (
  dlgResult = dotNetClass "System.Windows.Forms.DialogResult"
  if frmOpenFileDialog.ShowDialog() == dlgResult.OK do (
    IOFile = dotNetClass "System.IO.File"
    txtTextBox.Text = IOFile.ReadAllText frmOpenFileDialog.Filename
    filetoprint = frmOpenFileDialog.Filename
  )--end if
)--end fn
 
fn pd_PrintPage sender ev = (
  count = 0
  linesperpage = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics)
  yPos = 0
  leftMargin = ev.MarginBounds.Left
  topMargin = ev.MarginBounds.Top
  currentline = ""
  brush = dotnetobject "System.Drawing.Solidbrush" (dotnetclass "System.Drawing.Color").black
  stringformat = dotnetobject "System.Drawing.StringFormat"
  while count < linesperpage do(
    currentline = streamReader.ReadLine()
    if currentline == undefined do exit
    yPos = topMargin + count * printFont.GetHeight(ev.Graphics)
    ev.Graphics.DrawString currentline printFont brush leftMargin yPos stringformat
    count += 1
  )--end while loop
  if currentline != undefined then
    ev.HasMorePages = true
  else
    ev.HasMorePages = false
)--end fn
 
fn btnPrint_Click = (
  if filetoprint != "" then (
    try (
      streamReader = dotnetobject "System.IO.Streamreader" filetoprint
      pd = dotnetobject "System.Drawing.Printing.PrintDocument"
      dotnet.addeventhandler pd "PrintPage" pd_PrintPage
      pd.Print()
      streamReader.Close()
    )catch print (getCurrentException())
  )--end if
)--end fn
 
dotNet.AddEventHandler btnOpenFile "Click" btnOpenFile_Click
dotNet.AddEventHandler btnPrint "Click" btnPrint_Click
 
hForm.Controls.Add(txtTextBox)
hForm.Controls.Add(btnOpenFile)
hForm.Controls.Add(btnPrint)
 
dotnet.setLifetimeControl btnOpenFile #dotnet
dotnet.setLifetimeControl btnPrint #dotnet
 
hForm.ShowModeless()
)

Step-By-Step

(
dotNet.loadAssembly "MaxCustomControls.dll"

We load a custom assembly containing 3ds Max specific controls.

local filetoprint = ""

This variable will store the name of the file to print.

local printFont = dotnetobject "System.Drawing.Font" "Arial" 10

We create an object instance of the System.Drawing.Font class with font name Arial and size of 10 and store the object in the user variable for later use.

local streamReader

This user variable will hold the stream reader.

hForm = dotNetObject "MaxCustomControls.MaxForm"

Let's create our main form.

hForm.Size = dotNetObject "System.Drawing.Size" 640 480

We set the size of the form to 640 x 480 using the System.Drawing.Size object.

hForm.FormBorderStyle = (dotnetclass "System.Windows.Forms.FormBorderStyle").FixedToolWindow

We also set the border style of the form to the predefined value FixedToolWindow which is exposed by the System.Windows.Forms.FormBorderStyle class.

Alternatively, we could use the fact that the left-hand-side hForm.FormBorderStyle value is the same class we are assigning from at the right-hand-size, so we could say

hForm.FormBorderStyle = hForm.FormBorderStyle.FixedToolWindow
hForm.Text = "Printing with dotNet"

We set the title of the form to the string...

hForm.ShowInTaskbar = False

...and we make sure the new form does not appear in the Windows Taskbar.

txtTextBox = dotNetObject "System.Windows.Forms.TextBox"

Now we need a textbox to display the content of the file to print.

txtTextBox.MultiLine = True
txtTextBox.WordWrap = False

The textbox will allow multiple lines of text and no word wrapping.

txtTextBox.Size = dotNetObject "System.Drawing.Size" 500 430
txtTextBox.Location = dotNetObject "System.Drawing.Point" 10 10

The textbox will have a size of 500x430 pixels and its upper left corner will be placed at coordinates 10,10 relatively to the upper left corner of the form.

txtTextBox.ScrollBars = txtTextBox.ScrollBars.Both

We will allow both horizontal and vertical scrollbars.

btnOpenFile = dotNetObject "System.Windows.Forms.Button"
btnOpenFile.Location = dotNetObject "System.Drawing.Point" 520 10
btnOpenFile.Text = "Open File"

We will need a button to open a file to print.

We create a new button object and set its position to coordinates 520,10 inside the form. Its caption will read "Open File".

btnPrint = dotNetObject "System.Windows.Forms.Button"
btnPrint.Location = dotNetObject "System.Drawing.Point" 520 40
btnPrint.Text = "Print"

We will need another button to start printing.

We create a new button object and set its position to coordinates 520,40 inside the form. Its caption will read "Print".

frmOpenFileDialog = dotNetObject "System.Windows.Forms.OpenFileDialog"
frmOpenFileDialog.Filter = "txt files (*.txt)|*.txt|Script Files (*.ms)|*.ms|ini files (*.ini)|*.ini|XML files (*.xml)|*.xml|All files (*.*)|*.*"

In order to select a file for printing, we will need an OpenFileDialog object set to filter the typical file types.

fn btnOpenFile_Click = (

We will need an even handler function to be called when the Open File button is pressed.

dlgResult = dotNetClass "System.Windows.Forms.DialogResult"

We will be using the OpenFileDialog we pre-defined earlier, so we need a user variable containing the class DialogResult to compare the result of the dialog with the predefined results.

if frmOpenFileDialog.ShowDialog() == dlgResult.OK do (

We call the ShowDialog() method of the OpenFileDialog object which displays the dialog and lets the user pick a file. Once it returns, we compare the return value of the dialog with the OK value of the DialogResult class to see if the operation finished successfully (as opposed to being canceled by the user).

IOFile = dotNetClass "System.IO.File"
txtTextBox.Text = IOFile.ReadAllText frmOpenFileDialog.Filename

If a valid file was picked, we will use the System.IO.File class to read the file and assign its content to the textbox in the form. We simply call the method ReadAllText in the System.IO.File class and pass the filename returned by the OpenFileDialog as argument. The result from this method gets assigned to the text of the textBox.

filetoprint = frmOpenFileDialog.Filename

At this point, we can store the selected filename in the local variable filetoprint so we can use it when printing the file.

)--end if
)--end fn

fn pd_PrintPage sender ev = (

Before we define an even handler function for the Print button, will will need a function to perform the whole printing operation. It gets two arguments - the sender and the event.

local count = 0

We will need a count variable to increment the line count as we process the text.

linesperpage = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics) 

To calculate the number of lines per page, we have to divide the height of the printed document page between the margins by the height of the printing font.

yPos = 0

This variable will be used to calculate and store the vertical position to print at.

leftMargin = ev.MarginBounds.Left
topMargin = ev.MarginBounds.Top

We grab the left and top margins into user variables, too.

currentline = ""

This variable will hold the current line.

brush = dotnetobject "System.Drawing.Solidbrush" (dotnetclass "System.Drawing.Color").black

We create a solid brush object with black color.

stringformat = dotnetobject "System.Drawing.StringFormat"

This variable will contain an object required by the DrawString method later on.

while count < linesperpage do (

While the counter is less than the number of lines per page, we repeat the following

currentline = streamReader.ReadLine()

We read a line from the streamreader - the variable was initialized in the beginning and will be assigned in the event handler of the Print button.

if currentline == undefined do exit

If the currentline variable contains undefined (which means we read past the end of the file), we exit the loop.

yPos = topMargin + count * printFont.GetHeight(ev.Graphics)

We now calculate the vertical position to print at by adding the height of the font multiplier by the current line counter to the top margin.

ev.Graphics.DrawString currentline printFont brush leftMargin yPos stringformat

Finally, we can draw the string of the current line using the current font and brush at horizontal position equal to the left margin, vertical position according to the yPos value calculated in the previous step and format based on the string format object.

count += 1

We increment the counter by one and the loop repeats unless we have reached the end of the page or the end of the file.

)--end while loop
if currentline != undefined then ev.HasMorePages = true else ev.HasMorePages = false 

We check to see why the loop exited - if the current line is not undefined, then there are more pages to print and we set the corresponding property of the PrintDocument object to notify it whether it should keep on printing or not.

Note that the same can be written shorter as

ev.HasMorePages = currentline != undefined
)--end fn

fn btnPrint_Click = (

Now we can define the event handler of the Print button.

if filetoprint != "" do (
try (

If the file name selected to print is not an empty string, we try to perform the following:

streamReader = dotnetobject "System.IO.Streamreader" filetoprint

We set the streamReader variable initialized in the beginning of the script to a dotNetObject instance of the class System.IO.Streamreader. It is similar to the fileStream values of MAXScript and provides file IO access to files on disk.

pd = dotnetobject "System.Drawing.Printing.PrintDocument"

We also create a PrintDocument object which will handle the actual printing.

dotnet.addeventhandler pd "PrintPage" pd_PrintPage

We add an event handler to the PrintDocument object which will call the MAXScript function we defined previously.

pd.Print()

Now we can call the Print() method in the PrintDocument object to start printing. This will call out print event handler function once for each page until the hasMorePages property of the PrintDocument is set to false at which point it will return here.

streamReader.Close()

Once we are back, we close the stream.

) catch print (getCurrentException())

If there was any problem within the printing code, we catch the error and print the last exception.

)--end if
)--end fn

dotNet.AddEventHandler btnOpenFile "Click" btnOpenFile_Click
dotNet.AddEventHandler btnPrint "Click" btnPrint_Click

Now we can add the two event handler functions we defined previously to the two buttons.

hForm.Controls.Add(txtTextBox)
hForm.Controls.Add(btnOpenFile)
hForm.Controls.Add(btnPrint)

We can also add the textbox and the two buttons to the form.

dotnet.setLifetimeControl btnOpenFile #dotnet
dotnet.setLifetimeControl btnPrint #dotnet

Since we are adding scripted event handlers to the DotNet controls and placing those controls in a form whose lifetime is longer than the MAXScript values wrapping the controls, we set the lifetime control of the buttons to be controlled by DotNet

You might ask:

Is there any reason this isn’t the default behavior when creating event handlers?

Yes, the above approach is only applicable to DotNet objects that are placed in other DotNet objects.

An example of when you have an event handler and you do not want the object’s lifetime control to change is a timer object. You want the timer object to be deleted when the MAXScript value wrapping the object is deleted. If you set the timer object’s lifetime control to #dotnet, then at some random point in the future the timer object will be deleted by the DotNet garbage collector since no DotNet object holds a reference to the timer object.

hForm.ShowModeless()

Finally, we show the form.

)--end script

Result

See Also