views:

849

answers:

5

I'm using .net's PrintPreviewDialog and whenever it's generating a preview, it locks up my GUI in the background and makes it look like it has crashed until the preview is finished. Seeing how the .net's page progress window that pops up isn't a dialog, the back ground can be selected which then comes to the front in a half drawn, locked up manner. This also happens when the user clicks the actual "Print" button on the preview dialog, and when I just run PrintDocument.Print(). Is there an easy way to modify the following code to stop the GUI from hanging when the user is waiting for .net to draw the print pages:

//just showing a preview, hangs up background GUI on generating the preview
// and when user prints straight from the preview
this.printPreviewDialog.ShowDialog(this);

//just trying to print a .net PrintDocument class, GUI hangs in background
// when .net is drawing the pages
this.printDocument.Print();
+1  A: 

You should probably call those methods in a different thread if they take so long. Investigate the use of BackgroundWorker to help you.

Also, It could be that it's because of the windows printers and not your code (are you using a network printer? if so, change to a virtual printer and see if that changes anything).

gcores
Yeah, I've been trying to think of a way to do this, but you cannot run this.printPreviewDialog.ShowDialog(this); on a separate thread, since you will get a cross thread Exception for trying to access GUI components.
EMaddox84
Do you know why is it taking so long, is there any method you're calling that's accountable for the time and that could be taken to another thread?
gcores
You need to make sure the this.printPreviewDialog is constructed in a background thread, not just calling ShowDialog from another thread
grepsedawk
Yeah, it's the overridden PrintPage() inside of the PrintDocument class. It does the page drawing. And due to the structure of .net's PrintDocument class, I can't leave that method until all the work is done.
EMaddox84
As to constructing it in a background thread. I don't see how that would amend anything, because the heavy work is occurring when the PrintDocument within the PrintPreviewDialog is drawing pages.
EMaddox84
I guess what I need is to have the PrintPage() method run on a seperate thread, but since it is invoked by the PrintPreviewDialog, I don't think I can.
EMaddox84
It should work if both the PrintDocument and the PrintDialog are created on the same thread. Otherwise, I'm not sure. Have you checked if changing the default printer changes anything?
gcores
Yeah I am printing to a document writer currently, so that shouldn't be the case. I'll try creating and showing the preview dialog on the same thread though. I'll let you know if it works.
EMaddox84
One other thing I can think of, try updating your .net runtime and try on another machine :)
grepsedawk
There is a way for another thread to send updates to the GUI thread in .NET. I don't remember how offhand, but I remember seeing Scott Hanselman using it somewhere. You might want to look at the dnrtv episodes that he did.
Slapout
My bad. It wasn't Scott Hanselman. It was Carl Franklin in dnrtv ep 16.
Slapout
+1  A: 

the ShowDialog method creates a modal window, it blocks the main thread. the Show method creates a nonmodal window, it doesn't block the main thread.

When you call

this.printDocument.Print();

it again does its work in the main thread.

To do that in a background thread, you could try something like (off the top of my head)

ThreadPool.QueueUserWorkItem ( (obj) => this.printDocument.Print() );

it uses a new thread to print the document, instead of the main GUI thread.

If you want to know more, you have to research threading

DonkeyMaster
+1  A: 

Another option would be spinning up a new UI thread:

ThreadStart ts = () =>
{
    printDocument.Print();

    // Start the message loop to prevent the thread from finishing straight away.
    System.Windows.Forms.Application.Run();
};
Thread t = new Thread(ts);
t.SetApartmentState(ApartmentState.STA);
t.Start();

Keep in mind this code isn't tested and may need some tuning (especially the message loop part) - and you might also want to keep in mind you will need to shut down the thread at some time - so perhaps you might want a class to handle the interaction and lifetime management.

Matthew Savage
I have never seen that construct before. Why not use the ThreadPool instead?
foson
A: 

Looks like there is a performance problem in your code to generate the document.

GvS
A: 

Dot Net Rocks TV (dnrtv) covers how to run things in a background thread to keep the GUI thread free in episode 16.

Slapout