Thụ lý sự kiện PrintDocument.PrintPage, và sử dụng các phương thức DrawString và DrawImage của lớp Graphics để in dữ liệu ra trang.. Để in một văn bản, bạn cần tạo một đối tượng Syst
Trang 113. In văn b n đ n gi n In văn b n đ n gi n ả ơ ả ả ơ ả
Bạn cần in text hoặc hình.
Thụ lý sự kiện PrintDocument.PrintPage, và sử dụng các phương thức DrawString
và DrawImage của lớp Graphics để in dữ liệu ra trang.
.NET sử dụng mô hình in dựa-trên-sự-kiện bất đồng bộ (asynchronous event-based printing
model) Để in một văn bản, bạn cần tạo một đối tượng
System.Drawing.Printing.PrintDocument, cấu hình các thuộc tính của nó, và rồi gọi phương thức Print để thực hiện tác vụ in Bộ thực thi sẽ phát sinh các sự kiện BeginPrint, PrintPage,
và EndPrint của lớp PrintDocument trên một tiểu trình mới Bạn thụ lý các sự kiện này và sử dụng đối tượng System.Drawing.Graphics để xuất dữ liệu ra trang Hình và text được ghi ra
trang theo cùng cách như bạn vẽ lên cửa sổ bằng GDI+ Tuy nhiên, bạn có thể cần phải theo
vết vị trí của bạn trên trang, vì mọi phương thức của lớp Graphics đều yêu cầu tọa độ chỉ định nơi cần vẽ.
Các thiết lập cho máy in được cấu hình thông qua các thuộc tính
PrintDocument.PrinterSettings và PrintDocument.DefaultPageSettings Thuộc tính
PrinterSettings trả về một đối tượng PrinterSettings (đã được mô tả trong mục 8.12) cho biết máy in sẽ được sử dụng Thuộc tính DefaultPageSettings cung cấp đối tượng
PageSettings cho biết độ phân giải (resolution), lề (margin), hướng trang (orientation) Bạn
có thể cấu hình các thuộc tính này trong mã lệnh, hoặc bạn có thể sử dụng lớp
System.Windows.Forms.PrintDialog để người dùng thực hiện các thay đổi thông qua hộp
thoại in chuẩn của Windows (xem hình 8.9) Trong hộp thoại in, người dùng có thể chọn một máy in và chọn số lượng bản in (number of copies) Người dùng cũng có thể nhắp vào nút
Properties để cấu hình các thiết lập nâng cao như cách bố trí trang (layout) và độ phân giải
máy in (resolution) Cuối cùng, người dùng có thể chấp thuận hoặc hủy bỏ thao tác in bằng cách nhắp OK hoặc Cancel.
Hình 8.9 Hộp thoại Print
Trang 2Trước khi sử dụng lớp PrintDialog, bạn phải gắn nó vào đối tượng PrintDocument bằng cách thiết lập thuộc tính PrintDialog.Document Theo đó, bất kỳ sự thay đổi nào do người dùng thực hiện trong hộp thoại in sẽ tự động được áp dụng vào đối tượng PrintDocument.
Ví dụ dưới đây là một form chỉ chứa một nút lệnh Khi người dùng nhắp vào nút này, ứng dụng sẽ tạo một đối tượng PrintDocument mới, cho phép người dùng cấu hình các thiết lập in,
và rồi bắt đầu thao tác in bất đồng bộ Phương thức thụ lý sự kiện đáp ứng cho sự kiện
PrintPage sẽ ghi ra nhiều dòng text và một bức hình.
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Printing;
public class SimplePrint : System.Windows.Forms.Form {
private System.Windows.Forms.Button cmdPrint;
// (Bỏ qua phần mã designer.)
private void cmdPrint_Click(object sender, System.EventArgs e) {
// Tạo một văn bản và gắn vào phương thức thụ lý sự kiện
PrintDocument doc = new PrintDocument();
doc.PrintPage += new PrintPageEventHandler(this.Doc_PrintPage);
// Cho phép người dùng chọn một máy in
// và chỉ định các thiết lập khác
PrintDialog dlgSettings = new PrintDialog();
dlgSettings.Document = doc;
// Nếu người dùng nhắp OK thì in văn bản
if (dlgSettings.ShowDialog() == DialogResult.OK) {
// Phương thức này trả về tức thì, trước khi tác vụ in
// bắt đầu Sự kiện PrintPage sẽ phát sinh bất đồng bộ
doc.Print();
}
Trang 3private void Doc_PrintPage(object sender, PrintPageEventArgs e) {
// Định nghĩa font
Font font = new Font("Tahoma", 30);
// Xác định vị trí trên trang
// Trong trường hợp này, chúng ta đọc các thiết lập lề
// (mặc dù không có gì ngăn chúng ta vượt qua biên lề)
float x = e.MarginBounds.Left;
float y = e.MarginBounds.Top;
// Xác định chiều cao của một dòng (dựa trên font được sử dụng)
float lineHeight = font.GetHeight(e.Graphics);
// In năm dòng text
for (int i=0; i < 5; i++) {
// Vẽ text với bút vẽ đen, sử dụng font và
// tọa độ mà chúng ta đã xác định
e.Graphics.DrawString("Thập Diện Mai Phục " + i.ToString(),
font, Brushes.Black, x, y);
// Dịch xuống một dòng
y += lineHeight;
}
y += lineHeight;
// Vẽ hình
e.Graphics.DrawImage(Image.FromFile(Application.StartupPath +
"\\test.bmp"), x, y);
}
}
Ví dụ này có một hạn chế là nó chỉ in một trang đơn Để in các văn bản phức tạp và nhiều trang hơn, bạn cần phải tạo một lớp chuyên biệt để đóng gói các thông tin về văn bản, trang hiện hành Kỹ thuật này sẽ được trình bày trong mục 8.14.
Trang 4Hình 8-10 Văn bản đã được in (sử dụng máy in Adobe PDF)
14. In văn b n có nhi u trang In văn b n có nhi u trang ả ả ề ề
Bạn cần in các văn bản phức tạp gồm nhiều trang và in nhiều văn bản khác nhau
cùng một lúc.
Đặt thông tin muốn in vào một lớp tùy biến dẫn xuất từ PrintDocument, và thiết
lập thuộc tính PrintPageEventArgs.HasMorePages là true trong khi vẫn còn trang
để in.
Sự kiện PrintDocument.PrintPage cho phép bạn chỉ in một trang đơn Nếu muốn in nhiều trang hơn, bạn cần thiết lập thuộc tính PrintPageEventArgs.HasMorePages là true trong phương thức thụ lý sự kiện PrintPage Trong khi HasMorePages là true, lớp PrintDocument
vẫn tiếp tục phát sinh các sự kiện PrintPage (một sự kiện cho một trang) Tuy nhiên, bạn cần biết là đang in đến trang thứ mấy, dữ liệu gì sẽ được in trên mỗi trang Để thực hiện điều này, cách hay nhất là tạo một lớp tùy biến.
Lớp TextDocument dưới đây thừa kế từ PrintDocument và thêm ba thuộc tính: Text lưu trữ một mảng các dòng text, PageNumber cho biết trang vừa được in, và Offset cho biết dòng vừa được
in (trong mảng Text).
public class TextDocument : PrintDocument {
private string[] text;
Trang 5private int offset;
public string[] Text {
get {return text;}
set {text = value;}
}
public int PageNumber {
get {return pageNumber;}
set {pageNumber = value;}
}
public int Offset {
get {return offset;}
set {offset = value;}
}
public TextDocument(string[] text) {
this.Text = text;
}
}
Tùy thuộc vào kiểu tài liệu muốn in, bạn có thể chỉnh sửa lớp này Ví dụ, bạn có thể lưu trữ một mảng gồm các dữ liệu hình, một vài nội dung sẽ được sử dụng làm header hoặc footer trên mỗi trang, thông tin về font, hoặc tên của file mà bạn muốn đọc thông tin từ nó Gói các thông tin này vào một lớp đơn sẽ khiến cho việc in nhiều văn bản cùng một lúc dễ dàng hơn Phần mã khởi đầu cũng giống như mục 8.13, chỉ khác là bây giờ tạo đối tượng TextDocument
(thay vì tạo đối tượng PrintDocument) Phương thức thụ lý sự kiện PrintPage giữ vết của dòng hiện hành và kiểm tra có còn chỗ trống trên trang hay không trước khi thực hiện in dòng kế tiếp Nếu cần trang mới, thuộc tính HasMorePages được thiết lập là true và sự kiện PrintPage
phát sinh lần nữa cho trang kế tiếp Nếu không, thao tác in xem như hoàn tất
Phần mã đầy đủ cho form được trình bày dưới đây:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Printing;
public class MultiPagePrint : System.Windows.Forms.Form {
Trang 6private System.Windows.Forms.Button cmdPrint;
// (Bỏ qua phần mã designer.)
private void cmdPrint_Click(object sender, System.EventArgs e) {
// Tạo văn bản gồm 100 dòng
string[] printText = new string[100];
for (int i=0; i < 100; i++) {
printText[i] = i.ToString();
printText[i] +=
": Thập Diện Mai Phục (House of Flying Daggers)";
}
PrintDocument doc = new TextDocument(printText);
doc.PrintPage += new PrintPageEventHandler(this.Doc_PrintPage);
PrintDialog dlgSettings = new PrintDialog();
dlgSettings.Document = doc;
// Nếu người dùng nhắp OK thì in văn bản
if (dlgSettings.ShowDialog() == DialogResult.OK) {
doc.Print();
}
}
private void Doc_PrintPage(object sender, PrintPageEventArgs e) {
// Thu lấy văn bản đã gửi sự kiện này
TextDocument doc = (TextDocument)sender;
// Định nghĩa font và xác định chiều cao
Font font = new Font("Tahoma", 10);
float lineHeight = font.GetHeight(e.Graphics);
Trang 7float x = e.MarginBounds.Left;
float y = e.MarginBounds.Top;
// Tăng biến đếm cho trang (cho biết số trang đã được in)
doc.PageNumber += 1;
// In tất cả thông tin vừa khít trên trang
// Vòng lặp này kết thúc khi dòng kế tiếp vượt quá biên lề,
// hoặc không còn dòng nào để in
while ((y + lineHeight) < e.MarginBounds.Bottom &&
doc.Offset <= doc.Text.GetUpperBound(0)) {
e.Graphics.DrawString(doc.Text[doc.Offset], font,
Brushes.Black, x, y);
// Dịch đến dòng dữ liệu kế tiếp
doc.Offset += 1;
// Dịch xuống một dòng trên trang
y += lineHeight;
}
if (doc.Offset < doc.Text.GetUpperBound(0)) {
// Vẫn còn ít nhất một trang nữa
// Cho biết sự kiện này sẽ phát sinh lần nữa
e.HasMorePages = true;
}else {
// Thao tác in đã hoàn tất
doc.Offset = 0;
}
}
}
Trang 815. In text d ng wrapping In text d ng wrapping ạ ạ
Bạn cần phân tích một khối text lớn thành các dòng riêng biệt sao cho vừa khít
trên một trang.
Sử dụng phương thức nạp chồng Graphics.DrawString (phương thức này nhận
vào một hình chữ nhật biên).
Thông thường, bạn sẽ cần phá một khối text lớn thành các dòng riêng biệt để có thể in lên
trang từng dòng một .NET Framework có thể thực hiện công việc này một cách tự động bằng
một phiên bản của phương thức Graphics.DrawString (nhận vào một hình chữ nhật biên) Bạn cần chỉ định hình chữ nhật mô tả nơi bạn muốn text sẽ hiển thị Theo đó, text sẽ được wrap một cách tự động cho vừa khít bên trong đường biên này.
Đoạn mã dưới đây thực hiện cách tiếp cận này:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Printing;
public class WrappedPrint : System.Windows.Forms.Form {
private System.Windows.Forms.Button cmdPrint;
// (Bỏ qua phần mã designer.)
private void cmdPrint_Click(object sender, System.EventArgs e) {
// Tạo một văn bản và gắn nó vào phương thức thụ lý sự kiện
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 += new PrintPageEventHandler(this.Doc_PrintPage);
Trang 9// Cho phép người dùng chọn một máy in
// và chỉ định các thiết lập khác
PrintDialog dlgSettings = new PrintDialog();
dlgSettings.Document = doc;
// Nếu người dùng nhắp OK thì in văn bản
if (dlgSettings.ShowDialog() == DialogResult.OK) {
doc.Print();
}
}
private void Doc_PrintPage(object sender, PrintPageEventArgs e) {
// Thu lấy văn bản đã gửi sự kiện này
ParagraphDocument doc = (ParagraphDocument)sender;
// Định nghĩa font và text
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;
}
}
Trang 10Hình 8.11 Văn bản đã được in (sử dụng máy in Adobe PDF)
16. Hi n th print-preview Hi n th print-preview ể ể ị ị
Bạn cần sử dụng print-preview để biết được văn bản khi được in ra sẽ trông như
thế nào.
Sử dụng PrintPreviewDialog hoặc PrintPreviewControl (cả hai đều thuộc không
gian tên System.Windows.Forms).
.NET cung cấp hai điều kiểm có thể nhận vào một đối tượng PrintDocument, chạy đoạn mã thực hiện thao tác in, và sử dụng nó để tạo print-preview trên màn hình Hai điều kiểm này là:
• PrintPreviewDialog—hiển thị print-preview trong một cửa sổ độc lập.
• PrintPreviewControl—hiển thị print-preview trong một form tùy biến.
Để sử dụng cửa sổ print-preview độc lập, bạn cần tạo đối tượng PrintPrevewDialog, ấn định văn bản, và gọi phương thức PrintPreviewDialog.Show.
PrintPreviewDialog dlgPreview = new PrintPreviewDialog();
dlgPreview.Document = doc;
dlgPreview.Show();
Cửa sổ print-preview (xem hình 8.12) cung cấp tất cả các điều khiển cần thiết để di chuyển từ
trang này sang trang khác, thu phóng trang Cửa sổ này cũng cung cấp nút Print cho phép
người dùng gửi trực tiếp văn bản đến máy in Bạn có thể biến đổi cửa sổ này bằng cách chỉnh sửa các thuộc tính của PrintPrevewDialog.
Bạn cũng có thể thêm PrintPreviewControl vào bất kỳ form nào để hiển thị print-preview kế bên các thông tin khác Trong trường hợp này, bạn không cần gọi phương thức Show Ngay khi bạn thiết lập thuộc tính PrintPreviewControl.Document thì preview được tạo ra Để xóa preview, cần thiết lập thuộc tính Document là null, và để làm tươi preview, cần gán lại thuộc tính Document PrintPreviewControl chỉ hiển thị các trang preview, không có thêm điều khiển nào khác Tuy nhiên, bạn có thể thêm các điều kiểm để thực hiện thu phóng trang, lát nhiều trang Bạn chỉ cần điều chỉnh các thuộc tính của PrintPreviewControl cho phù hợp.