שתף באמצעות


PrintPreviewDialog - How can I add a button to select a printer

Question

Friday, December 4, 2009 4:44 AM | 1 vote

I'm using the PrintPreviewDialog.  Works great, but I really need to allow the user to select a printer instead of just having the print go directly to the default printer.  Any ideas on this?  Can't believe it isn't default functionality in the control.

All replies (21)

Friday, December 11, 2009 12:54 PM ✅Answered | 2 votes

Here is what I did to fix the problem.

I created a class called PrintPreviewDialogSelectPrinter.  In it I have two subs.  Basically, the class inherits the PrintPreviewDialog, adds a button that functions as I want, and removes the button that doesn't.  I finally found a sample online that I could understand.  I'm sure there are better ways of doing this, but it works very well.  Call it just like PrintPreviewDialog.

Imports System.Drawing.Printing
Public Class PrintPreviewDialogSelectPrinter
    Inherits PrintPreviewDialog

    Private Sub myPrintPreview_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        'Get the toolstrip from the base control
        Dim ts As ToolStrip = CType(Me.Controls(1), ToolStrip)
        'Get the print button from the toolstrip
        Dim printItem As ToolStripItem = ts.Items("printToolStripButton")

        'Add a new button 
        With printItem

            Dim myPrintItem As ToolStripItem
            myPrintItem = ts.Items.Add(.Text, .Image, New EventHandler(AddressOf MyPrintItemClicked))

            myPrintItem.DisplayStyle = ToolStripItemDisplayStyle.Image
            'Relocate the item to the beginning of the toolstrip
            ts.Items.Insert(0, myPrintItem)
        End With

        'Remove the orginal button
        ts.Items.Remove(printItem)
    End Sub
    Private Sub MyPrintItemClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim dlgPrint As New PrintDialog
        Try
            With dlgPrint
                .AllowSelection = True
                .ShowNetwork = True
                .Document = Me.Document
            End With
            If dlgPrint.ShowDialog = Windows.Forms.DialogResult.OK Then
                Me.Document.Print()
            End If
        Catch ex As Exception
            MsgBox("Print Error: " & ex.Message)
        End Try
    End Sub
End Class

Friday, December 4, 2009 5:48 AM

you can use a PrintDialog to select the printer

http://msdn.microsoft.com/en-us/library/aa983680(VS.71).aspx


Friday, December 4, 2009 9:03 AM

Override the PrintPreviewDialog and add a button to the toolstrip.  Show a control of your choosing on the click of the button.  See the PrintRegister program at www.johnweinhold.com for an example.


Friday, December 4, 2009 4:36 PM

I'm using the PrintPreviewDialog control.  The sample is using the Print Preview Control.

Is there a way to make the default print button show the printer dialog for selecting a printer instead of going straight to print?


Friday, December 4, 2009 4:47 PM

My sample uses the PrintPreviewDialog.  Try it.  It shows you how to use the control's events also.


Friday, December 4, 2009 4:49 PM

My sample uses the PrintPreviewDialog.  Try it.  It shows you how to use the control's events also.

I missed it.  Can you be more specific as to where I should look?

Here is what I am doing:

ppdForm.Document = .PreparePrintDocument()

 

ppdControl.UseAntiAlias = True

ppdControl.WindowState = FormWindowState.Maximized

ppdControl.ShowDialog()


Friday, December 4, 2009 4:55 PM

BTW, I'm looking at the PrintCheckRegister example.


Friday, December 4, 2009 5:08 PM

BTW, I'm looking at the PrintCheckRegister example.

Yes that's the right example.  Study it.  It shows how to do what you asked.  Override the print button OnClick and show the printdialog.


Friday, December 4, 2009 5:14 PM

Well, I don't see it.

Anyone else have any thoughts?


Wednesday, December 9, 2009 7:49 AM

Hello, Allen
Please follow jwavila's advice, PrintDialog is the right Component.
Thanks
Chao


Wednesday, December 9, 2009 3:11 PM

Hello, Allen
Please follow jwavila's advice, PrintDialog is the right Component.
Thanks
Chao

Not really.  I know the PrintDialog will do it, but the question is how to override the PrintPreviewDialog.


Wednesday, December 9, 2009 6:07 PM

Add a PrintDocument, PrintPreviewDialog, PrintDialog and a Button to a Windows Form.  Replace the Form1 code with the code below.  Press F5 and then the ToolStripButton labeled "Printer".

Public Class Form1

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

    PrintPreviewDialog1.ShowDialog()

  End Sub

  Dim WithEvents PDB As New ToolStripButton("Printer")

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load

    PrintPreviewDialog1.Document = PrintDocument1

    PrintDialog1.Document = PrintDocument1

    CType(PrintPreviewDialog1.Controls(1), ToolStrip).Items.Add(PDB)

  End Sub

  Private Sub PDB_Click1(ByVal sender As Object, ByVal e As EventArgs) Handles PDB.Click

    PrintDialog1.ShowDialog()

  End Sub

End Class


Wednesday, December 9, 2009 6:32 PM

Because the layout in the print preview is depended on the printer and the settings for that printer you should set the printer
using the print dialog prior to viewing it in the print preview control.

Another tactic (the one I use since I don’t like the print preview control) is to build one.
Basically the same code that prints to the printer can draw on any control. I use a panel myself.
Create a class that inherits the PrintDocument and Move the drawing code out of the print doc’s
Print_Page event into a public function that you can pass the graphics objects from a print
document or a control For example if you start a new windows forms project and add a class called
AdressLabels.
 
Paste the below code into the AdressLabels code window

Namespace AddressLabels
    Public Enum TypeOfPrintJob
        Display
        Paper
    End Enum

    Public Class Document
        Inherits Printing.PrintDocument

#Region "   String Formats"
        Private strfmtCenter As New StringFormat(StringFormatFlags.NoWrap) With {.Alignment = StringAlignment.Center, .Trimming = StringTrimming.EllipsisCharacter, .LineAlignment = StringAlignment.Center}
        Private strfmtLeft As New StringFormat(StringFormatFlags.NoWrap) With {.Alignment = StringAlignment.Near, .Trimming = StringTrimming.EllipsisCharacter, .LineAlignment = StringAlignment.Center}
        Private strfmtRight As New StringFormat(StringFormatFlags.NoWrap) With {.Alignment = StringAlignment.Far, .Trimming = StringTrimming.EllipsisCharacter, .LineAlignment = StringAlignment.Center}
        Private strfmtWrap As New StringFormat() With {.Alignment = StringAlignment.Near, .Trimming = StringTrimming.None, .LineAlignment = StringAlignment.Near}
#End Region

        'What it is being printed on
        Private mPrintFor As TypeOfPrintJob
        Public Property PrintFor() As TypeOfPrintJob
            Get
                Return mPrintFor
            End Get
            Set(ByVal value As TypeOfPrintJob)
                mPrintFor = value
            End Set
        End Property

        'Does it have more labels to print
        Private mHasMoreLabels As Boolean
        Public ReadOnly Property HasMoreLabels() As Boolean
            Get
                Return mHasMoreLabels
            End Get
        End Property

        'collection of addresses
        Private mAddresses As List(Of Address)
        Public Property Addresses() As List(Of Address)
            Get
                If mAddresses Is Nothing Then
                    mAddresses = New List(Of Address)
                End If

                Return mAddresses
            End Get
            Set(ByVal value As List(Of Address))
                mAddresses = CType(value, List(Of Address))
            End Set
        End Property

        'font used
        Private mLabelFont As Font
        Public Property LabelFont() As Font
            Get
                If mLabelFont Is Nothing Then
                    mLabelFont = New Font("Arial", 10, FontStyle.Regular, GraphicsUnit.Pixel)
                End If

                Return mLabelFont
            End Get
            Set(ByVal value As Font)
                mLabelFont = CType(value, Font)
            End Set
        End Property

        '
        Private mLayout As LabelLayout
        Public Property Layout() As LabelLayout
            Get
                If mLayout Is Nothing Then
                    mLayout = New LabelLayout
                End If

                Return mLayout
            End Get
            Set(ByVal value As LabelLayout)
                mLayout = CType(value, LabelLayout)
            End Set
        End Property

        Private Sub AdressLabels_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint
            Dim PrintAddress As Address

            'make sure printed flag is cleared 
            'before starting print job
            For Each PrintAddress In Addresses
                PrintAddress.Printed = False
            Next
        End Sub

        Private Sub Print_Page(ByVal sender As Object, ByVal e As Printing.PrintPageEventArgs) Handles MyBase.PrintPage
            'draw the labels on the printers graphics
            DrawLabels(e.Graphics, e.MarginBounds, e.MarginBounds)

            'let the print doc know if there is
            'more printing to do
            e.HasMorePages = HasMoreLabels
        End Sub

        'All the printing is done here
        Public Function DrawLabels(ByVal OnGraphics As Graphics, ByVal PrintArea As Rectangle, ByVal ClipTo As Rectangle) As Point
            Dim PrintAddress As Address
            Dim CityStateZip As String
            Dim BottomRight As New Point(PrintArea.Width, 0)

            With Layout
                'starting location is upperleft corner of the pages margin
                .LabelLocation = PrintArea.Location
                .FontHeight = LabelFont.Height
            End With

            For Each PrintAddress In Addresses
                'if past bottom of page bail-out to start new page
                If PrintFor = TypeOfPrintJob.Paper AndAlso Layout.LabelBounds.Bottom > PrintArea.Bottom Then
                    'let document know there are more pages
                    mHasMoreLabels = True

                    Return BottomRight
                End If

                'only print addresses that were not
                'printed on any previous pages
                If Not PrintAddress.Printed Then
                    CityStateZip = String.Format("{0}, {1} {2}", PrintAddress.City, PrintAddress.State, PrintAddress.Zip)

                    'Print label bounds to make it easier to adjust label size
                    'after you have the size you want comment out
                    OnGraphics.DrawRectangle(Pens.Blue, Layout.LabelBounds)

                    '                                                                             (alignment what-not)
                    '                        Text              Font         Brush       Rectangle     StringFormat
                    OnGraphics.DrawString(PrintAddress.Name, LabelFont, Brushes.Black, Layout.Name, strfmtCenter)
                    OnGraphics.DrawString(PrintAddress.Address, LabelFont, Brushes.Black, Layout.Address, strfmtCenter)
                    OnGraphics.DrawString(CityStateZip, LabelFont, Brushes.Black, Layout.CityStaeZip, strfmtCenter)

                    'check to see if another label will fit on the line
                    If Layout.LabelBounds.Right + Layout.LabelBounds.Width < PrintArea.Right Then
                        Layout.LabelLocation = New Point(Layout.LabelBounds.Right, Layout.LabelBounds.Top)
                    Else
                        'start new line of labels
                        Layout.LabelLocation = New Point(PrintArea.Left, Layout.LabelBounds.Bottom)

                        BottomRight.Y += Layout.LabelBounds.Height
                    End If

                    'mark is address as printed
                    PrintAddress.Printed = True
                End If
            Next

            'clear printed flags because all have printed
            For Each PrintAddress In Addresses
                PrintAddress.Printed = False
            Next

            mHasMoreLabels = False

            Return BottomRight
        End Function
    End Class

    'a single address
    Public Class Address

        Private mName As String
        Public Property Name() As String
            Get
                Return mName
            End Get
            Set(ByVal value As String)
                mName = value
            End Set
        End Property

        Private mAddress As String
        Public Property Address() As String
            Get
                Return mAddress
            End Get
            Set(ByVal value As String)
                mAddress = value
            End Set
        End Property

        Private mCity As String
        Public Property City() As String
            Get
                Return mCity
            End Get
            Set(ByVal value As String)
                mCity = value
            End Set
        End Property

        Private mState As String
        Public Property State() As String
            Get
                Return mState
            End Get
            Set(ByVal value As String)
                mState = value
            End Set
        End Property

        Private mZip As String
        Public Property Zip() As String
            Get
                Return mZip
            End Get
            Set(ByVal value As String)
                mZip = value
            End Set
        End Property

        Private mPrinted As Boolean
        Public Property Printed() As Boolean
            Get
                Return mPrinted
            End Get
            Set(ByVal value As Boolean)
                mPrinted = value
            End Set
        End Property

    End Class

    'Generates layout rectangles for labels
    Public Class LabelLayout
        Private Const Left_Padding As Integer = 3
        Private TopPadding As Integer

        Private mLabelLocation As Point
        Public Property LabelLocation() As Point
            Get
                Return mLabelLocation
            End Get
            Set(ByVal value As Point)
                mLabelLocation = value
            End Set
        End Property

        Private mLabelSize As Size
        Public Property LabelSize() As Size
            Get
                Return mLabelSize
            End Get
            Set(ByVal value As Size)
                mLabelSize = value

                'vert center address in label
                TopPadding = Math.Round((mLabelSize.Height - FontHeight * 3) * 0.5)
            End Set
        End Property

        Private mFontHeight As Double
        Public Property FontHeight() As Double
            Get
                Return mFontHeight
            End Get
            Set(ByVal value As Double)
                mFontHeight = value

                'vert center address in label
                TopPadding = Math.Round((mLabelSize.Height - mFontHeight * 3) * 0.5)
            End Set
        End Property

        'rectangles for formating the labels layout based on the size and location
        Public ReadOnly Property LabelBounds() As Rectangle
            Get
                Return New Rectangle(mLabelLocation, mLabelSize)
            End Get
        End Property

        Public ReadOnly Property Name() As Rectangle
            Get
                Return New Rectangle(LabelBounds.Left + Left_Padding, LabelBounds.Top + TopPadding, LabelBounds.Width - Left_Padding * 2, FontHeight)
            End Get
        End Property

        Public ReadOnly Property Address() As Rectangle
            Get
                Return New Rectangle(Name.Left, Name.Bottom, Name.Width, FontHeight)
            End Get
        End Property

        Public ReadOnly Property CityStaeZip() As Rectangle
            Get
                Return New Rectangle(Name.Left, Address.Bottom, Name.Width, FontHeight)
            End Get
        End Property
    End Class
End Namespace

Then paste this code over the form1’s code

Public Class Form1
    'Label Print document
    Private mLabelDoc As AddressLabels.Document
    Public Property LabelDoc() As AddressLabels.Document
        Get
            If mLabelDoc Is Nothing Then
                mLabelDoc = New AddressLabels.Document
            End If

            Return mLabelDoc
        End Get
        Set(ByVal value As AddressLabels.Document)
            mLabelDoc = CType(value, AddressLabels.Document)
        End Set
    End Property

    Private Sub PrintBut_Click(ByVal sender As Object, ByVal e As EventArgs)
        Dim dlgPrint As New PrintDialog

        'display print dialog to print labels
        If dlgPrint.ShowDialog() = Windows.Forms.DialogResult.OK Then
            'tell label print document that we are printing on paper
            'i.e to printer not to a form
            LabelDoc.PrintFor = AddressLabels.TypeOfPrintJob.Paper

            'pass the dialogs printer settings to the print doc
            LabelDoc.PrinterSettings = dlgPrint.PrinterSettings

            'start printing 
            LabelDoc.Print()
        End If
    End Sub

    Private Sub panPreview_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs)
        'panel to paint the labels on
        Dim PreviewOn As Panel = CType(sender, Panel)
        Dim BottomRight As Point

        'get the scroll offsets
        Dim TopLeft As New Point(PreviewOn.HorizontalScroll.Value * -1, PreviewOn.VerticalScroll.Value * -1)

        'tell the label print doc that you are printing 
        'for display i.e. on the panel
        LabelDoc.PrintFor = AddressLabels.TypeOfPrintJob.Display

        'call the drawlabels sub and pass the graphics from the panel
        BottomRight = LabelDoc.DrawLabels(e.Graphics, New Rectangle(TopLeft, New Size(800, 600)), e.ClipRectangle)

        'set the min scroll
        PreviewOn.AutoScrollMinSize = New Size(BottomRight)
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim RandAddress As New Random

        'Sets size for each label
        LabelDoc.Layout.LabelSize = New Size(200, 75)
        LabelDoc.LabelFont = New Font("Garamond", 14, FontStyle.Regular, GraphicsUnit.Pixel)

        'Add some addresses
        Dim newAddress As AddressLabels.Address

        For I As Integer = 0 To 45
            newAddress = New AddressLabels.Address

            With newAddress
                .Name = String.Format("Else, Someone {0}", I.ToString)
                .Address = String.Format("{0} Some Other Ave", RandAddress.Next(1001, 10000).ToString)
                .City = "Some Place"
                .State = "CA"
                .Zip = "93257"
            End With

            LabelDoc.Addresses.Add(newAddress)
        Next

        'add print button
        Dim butPrint As New Button

        With butPrint
            .Location = New Point(5, 5)
            .Text = "Print"
        End With

        AddHandler butPrint.Click, AddressOf PrintBut_Click

        'add panel to hold print button
        Dim TopPanel As New Panel

        With TopPanel
            .Dock = DockStyle.Top
            .Height = butPrint.Bottom + 5
            .Controls.Add(butPrint)
        End With

        'add panel to use for print
        'preview
        Dim panPreview As New Panel

        With panPreview
            .Dock = DockStyle.Fill
            .BackColor = SystemColors.Window
            .BorderStyle = BorderStyle.Fixed3D
            .Height = Me.Height - butPrint.Bottom + 20
            .AutoScroll = True
        End With

        AddHandler panPreview.Paint, AddressOf panPreview_Paint

        Me.Controls.Add(panPreview)
        Me.Controls.Add(TopPanel)

    End Sub

    
End Class

Friday, December 11, 2009 2:04 PM

"I finally found a sample online that I could understand"

Please post a link to your source.


Friday, December 11, 2009 2:20 PM

"I finally found a sample online that I could understand"

Please post a link to your source.

Good idea:
http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_22108522.html


Monday, October 4, 2010 1:55 PM

Hello Allen,

thank you for your code. I use it in my C# application:

 

public class PrintPreviewDialogSelectPrinter : PrintPreviewDialog

  {

    public PrintPreviewDialogSelectPrinter()

    {

      Shown += myPrintPreview_Shown;

    }



    private void myPrintPreview_Shown(object sender, System.EventArgs e)

    {

      //Get the toolstrip from the base control

      ToolStrip ts = (ToolStrip)this.Controls[1];

      //Get the print button from the toolstrip

      ToolStripItem printItem = ts.Items[0]; //"printToolStripButton"



      ToolStripItem myPrintItem;

      myPrintItem = ts.Items.Add(printItem.Text, printItem.Image, new EventHandler(MyPrintItemClicked));



      myPrintItem.DisplayStyle = ToolStripItemDisplayStyle.Image;

      //Relocate the item to the beginning of the toolstrip

      ts.Items.Insert(0, myPrintItem);



      //Remove the orginal button

      ts.Items.Remove(printItem);

    }

    private void MyPrintItemClicked(object sender, EventArgs e)

    {

      PrintDialog dlgPrint = new PrintDialog();

      try

      {

        dlgPrint.AllowSelection = true;

        dlgPrint.ShowNetwork = true;

        dlgPrint.Document = this.Document;

        if (dlgPrint.ShowDialog() == DialogResult.OK)

        {

          this.Document.Print();

        }

      }

      catch (Exception ex)

      {

        MessageBox.Show("Print Error: " + ex.Message);

      }

    } 

  

  }

 


Friday, September 23, 2011 12:55 AM

thanks, it works like a charm. :)

you solved to code this solution, and solved my problem.

 


Friday, March 1, 2013 8:56 AM | 1 vote

I had the same problem after changing my print method to PrintPreviewDialog.  To coorect this I added the line:

pdiag.ShowDialog(); // pdiag previously defined as a PrintDialog

Before my PrintPreviewDialog ...  line.

This sets the printer.  The print button in the Print Preview tool bar uses it as requested.  So the trick is to set it before the PrintPreviewDialogue is executed.

Web Developer


Wednesday, September 17, 2014 2:37 AM

Nice and quick solution (even after 5 years). I needed this for C# and it works still very well.


Monday, December 12, 2016 11:02 AM

Here is what I did to fix the problem.

I created a class called PrintPreviewDialogSelectPrinter.  In it I have two subs.  Basically, the class inherits the PrintPreviewDialog, adds a button that functions as I want, and removes the button that doesn't.  I finally found a sample online that I could understand.  I'm sure there are better ways of doing this, but it works very well.  Call it just like PrintPreviewDialog.

Imports System.Drawing.Printing
Public Class PrintPreviewDialogSelectPrinter
    Inherits PrintPreviewDialog

    Private Sub myPrintPreview_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        'Get the toolstrip from the base control
        Dim ts As ToolStrip = CType(Me.Controls(1), ToolStrip)
        'Get the print button from the toolstrip
        Dim printItem As ToolStripItem = ts.Items("printToolStripButton")

        'Add a new button 
        With printItem

            Dim myPrintItem As ToolStripItem
            myPrintItem = ts.Items.Add(.Text, .Image, New EventHandler(AddressOf MyPrintItemClicked))

            myPrintItem.DisplayStyle = ToolStripItemDisplayStyle.Image
            'Relocate the item to the beginning of the toolstrip
            ts.Items.Insert(0, myPrintItem)
        End With

        'Remove the orginal button
        ts.Items.Remove(printItem)
    End Sub
    Private Sub MyPrintItemClicked(ByVal sender As Object, ByVal e As EventArgs)
        Dim dlgPrint As New PrintDialog
        Try
            With dlgPrint
                .AllowSelection = True
                .ShowNetwork = True
                .Document = Me.Document
            End With
            If dlgPrint.ShowDialog = Windows.Forms.DialogResult.OK Then
                Me.Document.Print()
            End If
        Catch ex As Exception
            MsgBox("Print Error: " & ex.Message)
        End Try
    End Sub
End Class

Very good solution. Thanks a lot.


Sunday, January 22, 2017 3:10 PM

I allow myself some remarks to the code:

  1. Moving the initialising code to sub New() prevents errors at second call
  2. It is enough, to add a handler to the existing button. One may not exchange it.
  3. It is usefull, to close the dialog, wenn job is done.

So that is what I use:

 Friend Class PrintPreviewDialogWithPrinter
      Inherits PrintPreviewDialog
     
      Public Sub New()
         'Get the toolstrip from the base control
         Dim ts As ToolStrip = CType(Me.Controls("toolStrip1"), ToolStrip)
         'Get the print button from the toolstrip
         Dim PrintButton As ToolStripItem = ts.Items("printToolStripButton")
         
            AddHandler PrintButton.Click, AddressOf MyPrintItemClicked
      End Sub

      Private Sub myPrintPreview_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
         'if you like:
         Me.Height = FrmMain.Height
         Me.Width = FrmMain.Width
       End Sub

      Private Sub MyPrintItemClicked(ByVal sender As Object, ByVal e As EventArgs)
         
         Dim dlgPrint As New PrintDialog
         Try
            With dlgPrint
               .AllowSelection = True
               .ShowNetwork = True
               .Document = Me.Document 'me verweist offenbar schon auf das Doc im Dialog.
            End With
            If dlgPrint.ShowDialog = Windows.Forms.DialogResult.OK Then
               Me.Document.Print()
            End If
         Catch ex As Exception
            MsgBox("Print Error: " & ex.Message)
         End Try
         Me.Close()  'to make it more convenient
      End Sub