При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Работа с печатью и изображениями
Тема печати в Windows традиционно относится к одной из самых сложных: наличие огромного количества параметров, свойств и методов делает эту довольно простую вещь сильно запутанной. В этой лекции мы рассмотрим такие несложные задачи, как настройка параметров страницы, предварительный просмотр, печать содержимого элемента RichTextBox, а также преобразование форматов и печать рисунков, помещенных в PictureBox. Элемент PictureBox поддерживает различные режимы просмотра изображений – мы применим их всех и создадим простое приложение, позволяющее просматривать фотографии. Мы также еще раз создадим пользовательский элемент управления с собственными свойствами.
Печать содержимого RichTextBox. Элементы управления PrintDocument, PageSetupDialog, PrintPreviewDialog, PrintDialog
В качестве исходного приложения, в котором будем реализовывать возможность печати, возьмем проект TextEditor, созданный нами во второй лекции. Как мы знаем, максимальный объем элемента TextBox ограничивается 64 Кб, для работы с многостраничными документами этого явно мало. Поэтому удаляем с формы элемент txtBox и на его место помещаем элемент RichTextBox, которому задаем имя rtbText. Свойству Dock устанавливаем значение Fill, кроме того, удаляем значение свойства Text. Изменяем и добавляем следующие пункты главного меню (рис. 6.1):
Name | Text | Shortcut |
---|---|---|
mnuFile | &Файл | |
mnuOpen | &Открыть | CtrlO |
mnuSave | &Сохранить | CtrlS |
menuItem1 | - | |
mnuPageSetup | Пара&метры страницы | |
mnuPrintPreview | Пред&варительный просмотр | |
mnuPrint | &Печать | CtrlP |
Перетаскиваем на форму из окна ToolBox элементы управления: PrintDocument, PageSetupDialog, PrintPreviewDialog и PrintDialog. Подобно другим элементам диалогов, они отображаются на панели компонент в среде Visual Studio .NET. При печати формируется одна или несколько страниц, за которые отвечает объект PrintDocument. Элементы управления PageSetupDialog, PrintPreviewDialog и PrintDialog представляют собой диалоговые окна параметров страниц, предварительного просмотра и печати соответственно. Настройка свойств этих объектов представляет собой столь гибкую и широкую задачу, решаемую в коде приложения, что, в отличие, например, от элементов OpenFileDialog или SaveFileDialog, имеет смысл создавать их программно, в классе формы:
// PrintDocument printDocument1 = new PrintDocument(); // PageSetupDialog pageSetupDialog1 = new PageSetupDialog(); // PrintPreviewDialog printPreviewDialog1 = new PrintPreviewDialog(); // PrintDialog printDialog1 = new PrintDialog();
Этот фрагмент полностью аналогичен добавлению на форму элементов. Мы, однако, будем использовать визуальный режим.
Свойству Document элементов управления PageSetupDialog и PrintPreviewDialog устанавливаем значение свойства Name элемента PrintDocument — printDocument1. Тем самым мы связываем объект printDocument1, отвечающий за формирование страниц печати, с диалоговыми окнами. Устнавливаем следующие свойства элемента PrintDialog:
printDialog1, свойство | Значение | Описание |
---|---|---|
AllowSelection | True | Разрешение на печать выделенного фрагмента документа |
AllowSomePages | True | Разрешение на печать нескольких страниц |
PrintDocument | printDocument1 | Связывание с экземпляром объекта PrintDocument |
На информационной панели окна Properties при выборе свойств AllowSelection или AllowSomePages можно заметить сообщение – Enables and disables the Selection/Pages radio button (Включает или отключает доступность радиопереключателей "Выделенный фрагмент/Страницы"). Само по себе определение этих свойств еще не включает соответствующую функциональность — еще одна причина, по которой конфигурировать объекты печати лучше программно:
InitializeComponent(); // pageSetupDialog1.Document = printDocument1; // printPreviewDialog1.Document = printDocument1; // printDialog1.Document = printDocument1; // printDialog1.AllowSomePages = true; // printDialog1.AllowSelection = true;
Переходим в код формы. Для работы с печатью в библиотеке .NET Framework применяется класс System.Drawing.Printing, поэтому его нужно подключить в самом начале работы:
using System.Drawing.Printing;
В классе формы объявляем следующие переменные:
//Переменная для хранения текста для печати. //В нее мы будем помещать текст из RichTextBox string stringPrintText; //Переменная, определяющая номер страницы, с которой нужно начать печать int StartPage; //Переменная, определяющая количество страниц для печати: int NumPages; //Переменная, определяющая номер текущей страницы: int PageNumber;Листинг 6.1.
Следует обратить внимание на переменную stringPrintText — именно она будет отвечать за передачу текста в объект печати. Поскольку мы определили для нее строковый тип, это означает, что мы не сможем вывести на печать рисунки в тексте — как мы знаем, элемент RichTextBox поддерживает их наличие (в этом можно убедиться, скопировав содержимое документа Microsoft Word в наше приложение). Определение полной печати будет более сложным, что, впрочем, не является существенным для понимания самой концепции печати 11За ответом на этот вопрос я отправляю читателя к библиотеке MSDN. Если вы поймете общую идею, то без труда сможете нарастить дополнительные возможности на приложение для печати..
В конструкторе форме определяем диапазон страниц для печати:
public Form1() { InitializeComponent(); … //Определяем номер страницы, с которой следует начать печать printDialog1.PrinterSettings.FromPage = 1; //Определяем максимальный номер печатаемой страницы. printDialog1.PrinterSettings.ToPage = printDialog1.PrinterSettings.MaximumPage; }Листинг 6.2.
Максимальное количество страниц в диапазоне — 9999;, очевидно, что его вполне хватит для распечатки любого документа. Основным событием, в обработчике которого практически и будет осуществляться вся печать, будет событие PrintPage элемента printDocument1. Переключаемся в режим дизайна, выделяем элемент и, переключившись в окне Properties на вкладку событий, дважды щелкаем в поле этого события:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { }Листинг 6.3.
Далее все описываемые фрагменты кода будут относиться к этому обработчику.
Класс Graphics, принадлежащий пространству имен System.Drawing, — один из важнейших классов, отвечающих за рисование графики и вывода текста на форме. При создании новой формы пространство имен System.Drawing по умолчанию включается в шаблон. Для формирования страницы печати создаем экземпляр этого класса:
Graphics graph = e.Graphics; //Создаем объект font, которому устанавливаем // шрифт элемента rtbText Font font = rtbText.Font; //Получаем значение межстрочного интервала — высоту шрифта float HeightFont = font.GetHeight(graph); //Создаем экземпляр strfmt класса StringFormat для определения //дополнительных параметров форматирования текста. StringFormat stringformat = new StringFormat();
Страница представляет собой прямоугольный объект, параметры которого следует определить. Для этого используется класс RectangleF (буква F указывает на использование типа данных с плавающей точкой float ):
//Создаем экземляры rectanglefFull и rectfText класса RectangleF для //определения областей печати и текста. RectangleF rectanglefFull, rectfText; //Создаем переменные для подсчета числа символов и строк. int NumberSymbols, NumberLines;
Страница, формируемая для печати, состоит из трех областей (рис. 6.2).