Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

288 C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

public int Offset {

get {return offset;} set {offset = value;}

}

public TextDocument(string[] text) { this.Text = text;

}

}

Depending on the type of material you are printing, you might want to modify this class. For example, you could store an array of image data, some content that should be used as a header or footer on each page, font information, or even the name of a file from which you want to read the information. Encapsulating the information in a single class makes it easier to print more than one document at the same time. This is especially important because the printing process runs in a new dedicated thread. As a consequence, the user is able to keep working in the application and therefore update your data while the pages are printing. So, this dedicated class should contain a copy of the data to print to avoid any concurrency problems.

The code that initiates printing is the same as in recipe 8-14, only now it creates a TextDocument instance instead of a PrintDocument instance. The PrintPage event handler keeps track of the current line and checks whether the page has space before attempting to print the next line. If a new page is needed, the HasMorePages property is set to true and the PrintPage event fires again for the next page. If not, the print operation is deemed complete. This simple code sample also takes into account whether a line fits into the width of the page; refer to recipe 8-16 for a solution to this problem.

The full form code is as follows:

using System;

using System.Drawing; using System.Windows.Forms;

using System.Drawing.Printing;

namespace Apress.VisualCSharpRecipes.Chapter08

{

public partial class Recipe08_15 : Form

{

public Recipe08_15()

{

InitializeComponent();

}

private void cmdPrint_Click(object sender, EventArgs e)

{

// Create a document with 100 lines. string[] printText = new string[101]; for (int i = 0; i < 101; i++)

{

printText[i] = i.ToString(); printText[i] +=

": The quick brown fox jumps over the lazy dog.";

}

PrintDocument doc = new TextDocument(printText); doc.PrintPage += this.Doc_PrintPage;

PrintDialog dlgSettings = new PrintDialog(); dlgSettings.Document = doc;

C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

289

// If the user clicked OK, print the document. if (dlgSettings.ShowDialog() == DialogResult.OK)

{

doc.Print();

}

}

private void Doc_PrintPage(object sender, PrintPageEventArgs e)

{

//Retrieve the document that sent this event. TextDocument doc = (TextDocument)sender;

//Define the font and determine the line height. using (Font font = new Font("Arial", 10))

{

float lineHeight = font.GetHeight(e.Graphics);

//Create variables to hold position on page. float x = e.MarginBounds.Left;

float y = e.MarginBounds.Top;

//Increment the page counter (to reflect the page that

//is about to be printed).

doc.PageNumber += 1;

//Print all the information that can fit on the page.

//This loop ends when the next line would go over the

//margin bounds, or there are no more lines to print.

while ((y + lineHeight) < e.MarginBounds.Bottom && doc.Offset <= doc.Text.GetUpperBound(0))

{

e.Graphics.DrawString(doc.Text[doc.Offset], font, Brushes.Black, x, y);

//Move to the next line of data. doc.Offset += 1;

//Move the equivalent of one line down the page. y += lineHeight;

}

if (doc.Offset < doc.Text.GetUpperBound(0))

{

//There is still at least one more page.

//Signal this event to fire again. e.HasMorePages = true;

}

else

{

// Printing is complete. doc.Offset = 0;

}

}

}

}

}

290 C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

8-16. Print Wrapped Text

Problem

You need to parse a large block of text into distinct lines that fit on one page.

Solution

Use the Graphics.DrawString method overload that accepts a bounding rectangle.

How It Works

Often, you will need to break a large block of text into separate lines that can be printed individually on a page. The .NET Framework can perform this task automatically, provided you use a version of the Graphics.DrawString method that accepts a bounding rectangle. You specify a rectangle that represents where you want the text to be displayed. The text is then wrapped automatically to fit within those confines.

The Code

The following code demonstrates this approach, using the bounding rectangle that represents the printable portion of the page. It prints a large block of text from a textbox on the form.

using System;

using System.Drawing; using System.Windows.Forms;

using System.Drawing.Printing;

namespace Apress.VisualCSharpRecipes.Chapter08

{

public partial class Recipe08_16 : Form

{

public Recipe08_16()

{

InitializeComponent();

}

private void cmdPrint_Click(object sender, EventArgs e)

{

// Create the document and attach an event handler.

string text = "Windows Server 2003 builds on the core strengths " + "of the Windows family of operating systems--security, " + "manageability, reliability, availability, and scalability. " + "Windows Server 2003 provides an application environment to " + "build, deploy, manage, and run XML Web services. " + "Additionally, advances in Windows Server 2003 provide many " + "benefits for developing applications.";

PrintDocument doc = new ParagraphDocument(text); doc.PrintPage += this.Doc_PrintPage;

C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

291

//Allow the user to choose a printer and specify other settings. PrintDialog dlgSettings = new PrintDialog(); dlgSettings.Document = doc;

//If the user clicked OK, print the document.

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

{

doc.Print();

}

}

private void Doc_PrintPage(object sender, PrintPageEventArgs e)

{

//Retrieve the document that sent this event. ParagraphDocument doc = (ParagraphDocument)sender;

//Define the font and text.

using (Font font = new Font("Arial", 15))

{

e.Graphics.DrawString(doc.Text, font, Brushes.Black, e.MarginBounds, StringFormat.GenericDefault);

}

}

}

public class ParagraphDocument : PrintDocument

{

private string text; public string Text

{

get { return text; } set { text = value; }

}

public ParagraphDocument(string text)

{

this.Text = text;

}

}

}

Figure 8-10 shows the wrapped text.

292 C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

Figure 8-10. The printed document with wrapping

8-17. Show a Dynamic Print Preview

Problem

You need to use an on-screen preview that shows how a printed document will look.

Solution

Use PrintPreviewDialog or PrintPreviewControl (both of which are found in the System.Windows.Forms namespace).

How It Works

.NET provides two elements of user interface that can take a PrintDocument instance, run your printing code, and use it to generate a graphical on-screen preview:

The PrintPreviewDialog, which shows a preview in a stand-alone form

The PrintPreviewControl, which shows a preview in a control that can be embedded in one of your own custom forms

To use a stand-alone print preview form, you simply create a PrintPreviewDialog object, assign its Document property, and call the Show method:

PrintPreviewDialog dlgPreview = new PrintPreviewDialog(); dlgPreview.Document = doc;

dlgPreview.Show();

The Print Preview window (shown in Figure 8-11) provides all the controls the user needs to move from page to page, zoom in, and so on. The window even provides a print button that allows

C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

293

the user to send the document directly to the printer. You can tailor the window to some extent by modifying the PrintPreviewDialog properties.

Figure 8-11. Using the PrintPreviewDialog control

You can also add a PrintPreviewControl control to any of your forms to show a preview alongside other information. In this case, you do not need to call the Show method. As soon as you set the PrintPreviewControl.Document property, the preview is generated. To clear the preview, set the Document property to null, and to refresh the preview, simply reassign the Document property. PrintPreviewControl shows only the preview pages, not any additional controls. However, you can add your own controls for zooming, tiling multiple pages, and so on. You simply need to adjust the PrintPreviewControl properties accordingly.

The Code

For example, consider the form shown in Figure 8-12. It incorporates a PrintPreviewControl and allows the user to select a zoom setting.

Figure 8-12. Using the PrintPreviewControl in a custom window

294 C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

Here is the complete form code:

using System;

using System.Drawing; using System.Windows.Forms;

using System.Drawing.Printing;

namespace Apress.VisualCSharpRecipes.Chapter08

{

public partial class Recipe08_17 : Form

{

public Recipe08_17()

{

InitializeComponent();

}

private PrintDocument doc;

//(PrintDocument.PrintPage event handler code omitted.

//See code in recipe 8-15.)

private void Recipe08_17_Load(object sender, EventArgs e)

{

//Set the allowed zoom settings. for (int i = 1; i <= 10; i++)

{

lstZoom.Items.Add((i * 10).ToString());

}

//Create a document with 100 lines. string[] printText = new string[100]; for (int i = 0; i < 100; i++)

{

printText[i] = i.ToString();

printText[i] += ": The quick brown fox jumps over the lazy dog.";

}

doc = new TextDocument(printText); doc.PrintPage += this.Doc_PrintPage;

lstZoom.Text = "100"; printPreviewControl.Zoom = 1; printPreviewControl.Document = doc; printPreviewControl.Rows = 2;

}

private void cmdPrint_Click(object sender, EventArgs e)

{

// Set the zoom.

printPreviewControl.Zoom = Single.Parse(lstZoom.Text) / 100;

//Show the full two pages, one above the other. printPreviewControl.Rows = 2;

//Rebind the PrintDocument to refresh the preview. printPreviewControl.Document = doc;

}

}

C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

295

// (TextDocument class code omitted. See recipe 8-15.)

}

8-18. Manage Print Jobs

Problem

You need to pause or resume a print job or a print queue.

Solution

Use WMI. You can retrieve information from the print queue using a query with the Win32_PrintJob class, and you can use the Pause and Resume methods of the WMI Win32_PrintJob and Win32_Printer classes to manage the queue.

How It Works

WMI allows you to retrieve a vast amount of system information using a querylike syntax. One of the tasks you can perform with WMI is to retrieve a list of outstanding print jobs, along with information about each one. You can also perform operations such as printing and resuming a job or all the jobs for a printer. To use WMI, you need to add a reference to the System.Management.dll assembly.

The Code

The following code shows a Windows application that interacts with the print queue. It performs a WMI query to get a list of all the outstanding jobs on the computer and displays the job ID for each one in a list box. When the user selects the item, a more complete WMI query is performed, and additional details about the print job are displayed in a textbox. Finally, the user can click the Pause and Resume button after selecting a job to change its status.

Remember that Windows permissions might prevent you from pausing or removing a print job created by another user. In fact, permissions might even prevent you from retrieving status information and could cause a security exception to be thrown.

using System;

using System.Drawing; using System.Windows.Forms; using System.Management; using System.Collections; using System.Text;

namespace Apress.VisualCSharpRecipes.Chapter08

{

public partial class Recipe08_18 : Form

{

public PrintQueueTest()

{

InitializeComponent();

}

private void cmdRefresh_Click(object sender, EventArgs e)

{

// Select all the outstanding print jobs.

296 C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

string query = "SELECT * FROM Win32_PrintJob"; using (ManagementObjectSearcher jobQuery =

new ManagementObjectSearcher(query))

{

using (ManagementObjectCollection jobs = jobQuery.Get())

{

// Add the jobs in the queue to the list box. lstJobs.Items.Clear();

txtJobInfo.Text = "";

foreach (ManagementObject job in jobs)

{

lstJobs.Items.Add(job["JobID"]);

}

}

}

}

private void Recipe08_18_Load(object sender, EventArgs e)

{

cmdRefresh_Click(null, null);

}

//This helper method performs a WMI query and returns the

//WMI job for the currently selected list box item. private ManagementObject GetSelectedJob()

{

try

{

//Select the matching print job.

string query = "SELECT * FROM Win32_PrintJob " + "WHERE JobID='" + lstJobs.Text + "'";

ManagementObject job = null;

using (ManagementObjectSearcher jobQuery = new ManagementObjectSearcher(query))

{

ManagementObjectCollection jobs = jobQuery.Get(); IEnumerator enumerator = jobs.GetEnumerator(); enumerator.MoveNext();

job = (ManagementObject)enumerator.Current;

}

return job;

}

catch (InvalidOperationException)

{

// The Current property of the enumerator is invalid return null;

}

}

private void lstJobs_SelectedIndexChanged(object sender, EventArgs e)

{

ManagementObject job = GetSelectedJob(); if (job == null)

{

txtJobInfo.Text = ""; return;

}

C H A P T E R 8 G R A P H I C S, M U LT I M E D I A , A N D P R I N T I N G

297

// Display job information.

StringBuilder jobInfo = new StringBuilder(); jobInfo.AppendFormat("Document: {0}", job["Document"].ToString()); jobInfo.Append(Environment.NewLine); jobInfo.AppendFormat("DriverName: {0}", job["DriverName"].ToString()); jobInfo.Append(Environment.NewLine);

jobInfo.AppendFormat("Status: {0}", job["Status"].ToString()); jobInfo.Append(Environment.NewLine); jobInfo.AppendFormat("Owner: {0}", job["Owner"].ToString()); jobInfo.Append(Environment.NewLine);

jobInfo.AppendFormat("PagesPrinted: {0}", job["PagesPrinted"].ToString()); jobInfo.Append(Environment.NewLine);

jobInfo.AppendFormat("TotalPages: {0}", job["TotalPages"].ToString());

if (job["JobStatus"] != null)

{

txtJobInfo.Text += Environment.NewLine;

txtJobInfo.Text += "JobStatus: " + job["JobStatus"].ToString();

}

if (job["StartTime"] != null)

{

jobInfo.Append(Environment.NewLine); jobInfo.AppendFormat("StartTime: {0}", job["StartTime"].ToString());

}

txtJobInfo.Text = jobInfo.ToString();

}

private void cmdPause_Click(object sender, EventArgs e)

{

if (lstJobs.SelectedIndex == -1) return; ManagementObject job = GetSelectedJob(); if (job == null) return;

//Attempt to pause the job. int returnValue = Int32.Parse(

job.InvokeMethod("Pause", null).ToString());

//Display information about the return value. if (returnValue == 0)

{

MessageBox.Show("Successfully paused job.");

}

else

{

MessageBox.Show("Unrecognized return value when pausing job.");

}

}

private void cmdResume_Click(object sender, EventArgs e)

{

if (lstJobs.SelectedIndex == -1) return; ManagementObject job = GetSelectedJob(); if (job == null) return;

Соседние файлы в предмете Программирование на C++