Чтение онлайн

ЖАНРЫ

C# для профессионалов. Том II

Ватсон Карли

Шрифт:

Для примера

CapsEditor
все сделано очень простым, по сути нет никаких комбинаций для рассмотрения. Необходимо ответить только на двойной щелчок пользователя мышью, и в этом случае строку текста, над которой находится курсор мыши, необходимо перевести в символы верхнего регистра.

Это несложная задача, но имеется одна заминка. Необходимо перехватить событие

Doubleclick
, но приведенная выше таблица показывает, что это событие имеет параметр
EventArgs
, а не параметр
MouseEventArgs
. Проблема в том, что необходимо знать, где находится мышь, когда пользователь делает двойной щелчок, если требуется правильно определить строку текста, которая будет переводиться в верхний регистр, и для этого требуется параметр
MouseEventArgs
. Существует два способа обойти эту проблему. Один состоит в использовании статического метода
Control.MousePosition
,
который реализован объектом
Form1
, чтобы найти положение мыши, как в следующем коде:

protected override void OnDoubleClick(EventArgs e) {

 Point MouseLocation = Control.MousePosition;

 // обработать двойной щелчок

В большинстве случаев это срабатывает. Однако могут быть сложности, если приложение (или даже некоторое другое приложение с более высоким приоритетом) выполняет некоторые интенсивные вычисления в момент двойного щелчка пользователя. В таком случае возможно, что обработчик событий

OnDoubleClick
не будет вызван в течение полсекунды. Задержки такого рода вряд ли желательны, так как они начинают раздражать пользователей достаточно быстро. Половины секунды вполне достаточно для перемещения мыши на половину экрана, — и в этом случае выполнение
OnDoubleClick
может произойти в совершенно другом месте.

Лучший подход состоит в использовании одного из многих наложений между значениями событий мыши. Первая часть двойного щелчка мыши включает нажатие левой кнопки. Это означает, что если вызывается

OnDoubleClick
, то мы знаем, что также был вызван
OnMouseDown
с курсором мыши в том же месте. Можно использовать перезагружаемую версию
OnMouseDown
для записи положения мыши при использовании в
OnDoubleClick
. Этот подход используется в
CapsEditor
:

protected override void OnMouseDown(MouseEventArgs e) {

 base.OnMouseDown(e);

 this.mouseDoubleClickPosition = new Point(e.X, e.Y);

}

Теперь посмотрим на перезагруженную версию

OnDoubleClick
. Здесь придется выполнить немного больше работы:

protected override void OnDoubleClick(EventArgs e) {

 int i = PageCoordinatesToLineIndex(this.mouseDoubleClickPosition);

 if (i >= 0) {

TextLineInformation lineToBeChanged =

(TextLineInformation) documentLines[i];

lineToBeChanged.Text = lineToBeChanged.Text.ToUpper;

Graphics dc = this.CreateGraphics;

uint newWidth = (uint)dc.MeasureString(lineToBeChanged.Text, mainFont).Width:

if (newWidth > lineToBeChanged.Width) lineToBeChanged.Width = newWidth;

if(newWidth+2*margin > this.documentSize.Width) {

this.documentSize.Width = (int)newWidth;

this.AutoScrollMinSize = this.documentSize;

}

Rectangle changedRectangle =

new Rectangle(

LineIndexToPageCoordinates(i), new Size((int)newWidth, (int)this.lineHeight));

this.Invalidate(changedRectangle);

 }

 base.OnDoubleClick(e);

}

Начнем работу с вызова

PageCoordinatesToLineIndex
для определения, над какой строкой текста находится курсор мыши, когда пользователь делает двойной щелчок. Если этот вызов возвращает -1, то никакого текста под курсом нет, поэтому ничего делать не надо (за исключением, конечно, вызова версии
OnDoubleClick
базового класса, чтобы позволить Windows выполнить обработку по умолчанию. Это никогда не надо забывать делать.).

При условии, что была идентифицирована строка текста, можно воспользоваться методом

string.ToUpper
, чтобы легко преобразовать ее в верхний регистр. Труднее определить, что и где необходимо перерисовать. К счастью, так как пример сделан упрощенным, существует не слишком много комбинаций. Можно предположить для начала, что преобразование в верхний регистр будет всегда либо оставляет ширину строки на экране без изменения, либо увеличивает ее. Заглавные буквы больше строчных, поэтому ширина никогда не уменьшится. Известно, что поскольку мы не переносим строки, строка текста не будет продолжена на следующей строке и не сместит текст ниже. Действие по преобразованию строки в верхний регистр не будет поэтому в действительности
изменять положение ни одного из выводимых элементов. Это существенное упрощение.

Затем код использует

Graphics.MeasureString
для определения новой ширины текста. Здесь имеется две возможности:

□ Первая: новая ширина может сделать строку самой длинной строкой, что приведет к увеличению ширины всего документа. В этом случае необходимо задать для

AutoScrollMinSize
новый размер, чтобы панели прокрутки размещались правильно.

□ Вторая: размер документа может не измениться.

В любом случае необходимо перерисовать изображение на экране, вызывая

Invalidate
. Только одна строка изменилась, поэтому нет необходимости перерисовывать весь документ. Вместо этого надо определить границы прямоугольника, который содержит только измененную строку, чтобы можно было передать этот прямоугольник в метод
Invalidate
для перерисовывания только этой строки текста. Именно так делает представленный выше код. Вызов
Invalidate
приведет к вызову
OnPaint
, когда окончательно закончит работу обработчик события мыши. Вспомните предыдущие замечания в этой главе о трудностях в задании точки прерывания в
OnPaint
. Если при выполнении примера задать точку прерывания в
OnPaint
для перехвата получающегося действия рисования, то обнаружится, что параметр
PaintEventArgs
для
OnPaint
действительно содержит область вырезания, которая соответствует указанному прямоугольнику. И так как метод
OnPaint
был перезагружен, чтобы аккуратно вычленить область вырезания, то будет перерисована только одна требуемая строка текста.

Печать 

В этой главе мы полностью сконцентрировались на рисовании на экране. Часто бывает желательно, чтобы приложение могло создать твердую копию данных. К сожалению, в книге не хватает места, чтобы рассмотреть детали этого процесса, но мы кратко рассмотрим вопросы, которые встретятся при реализации печати документа.

Во многом печать схожа с выводом на экран. Предоставляется контекст устройства (экземпляр

Graphics
) и на этом экземпляре вызываются все обычные команды вывода. Однако имеются некоторые различия: принтеры не могут прокручиваться — они используют страницы. Необходимо убедиться, что найден разумный способ деления документа на страницы, и выводить каждую страницу по запросу. К тому же большинство пользователей ожидают, что вывод на принтер будет выглядеть очень похоже на вывод на экран. Этого очень трудно добиться при использовании координат страницы. Проблема в том, что принтеры имеют другое число точек на дюйм (dpi), чем экран. Дисплейные устройства традиционно поддерживают стандарт около 96 dpi, хотя некоторые новые мониторы имеют более высокое разрешение. Принтеры могут иметь более тысячи dpi. Это означает, например, что при рисовании фигур или выводе изображений, при задании их размеров числом пикселей они будет выглядеть на принтере слишком маленькими. Иногда та же самая проблема может влиять на шрифты текста. К счастью, GDI+ допускает в этих случаях применение координат устройства. Чтобы напечатать документы, почти наверняка придется использовать свойство
Grpahics.PageUnit
для выполнения печати с помощью некоторых физических единиц измерения, таких как дюймы или миллиметры.

.NET имеет большое количество классов, созданных для поддержки процесса печати. Эти классы позволяют контролировать и извлекать различные настройки принтера и находятся в основном в пространстве имен

System.Drawing.Printing
. Существуют также предопределенные диалоговые окна
PrintDialog
и
PrintPreviewDialog
, которые доступны в пространстве имен
System.Windows.Forms
. Процесс печати будет включать вызов метода
Show
на экземпляре одного из этих классов после задания некоторых свойств.

Заключение

В этой главе было рассмотрено рисование на устройстве вывода, реализующиеся в коде приложения, а не с помощью предопределенных элементов управления или диалоговых окон. GDI+ является мощным инструментом, и базовые классы .NET могут помочь при рисовании на устройстве. Мы видели, что этот процесс является в действительности довольно простым, в большинстве случаев можно рисовать текст и сложные фигуры или выводить изображения с помощью пары инструкций C#. Однако управление рисованием — работа, происходящая неявно, включающая определение, что и где нарисовать и нужна ли перерисовка в любой данной ситуации. Это значительно более сложная задача, требующая тщательного проектирования алгоритма. Важно хорошо понимать, как работает GDI+ и какие действия предпринимает Windows для рисования. В частности, с учетом архитектуры Windows важно, чтобы рисование выполнялось с помощью объявления областей окна недействительными, где это возможно, в таком случае система Windows реагирует должным образом на событие Paint.

Поделиться с друзьями: