Программирование >>  Вывод графики 

1 ... 11 12 13 [ 14 ] 15 16 17 ... 19


Определения обработчиков достаточно стандартны:

protected void OpenFileDialog FileOk(object Sender, CancelEventArgs e)

LoadFile(fileOpenDialog.FileName);

protected void menuFileOpen Click(object sender, EventArgs e) fileOpenDialog.ShowDialog();

protected void menuFileExit Click(object sender, EventArgs e) Close();

Далее рассмотрим метод LoadFile(). Он обрабатывает открытие и чтение файла (а также обеспечивает возбуждение события Paint для принудительной перерисовки с новым файлом).

private void LoadFile(string FileName)

StreamReader sr = new StreamReader(FileName); string nextLine; documentLines.Clear(); nLines = 0;

TextLineInformation nextLineInfo; while ( (nextLine = sr.ReadLine()) != null)

nextLineInfo = new TextLineInformation(); nextLineInfo.Text = nextLine; documentLines.Add(nextLineInfo); ++nLines;

sr.Close();

documentHasData = (nLines>0) ? true : false; CalculateLineWidths();

CalculateDocumentSize();

Text = standardTitle + - + FileName;

Invalidate();

Большая часть этой функции представляет собой просто стандартный код чтения файла (см. главу 25). Отметим, что после того, как файл открыт, мы последовательно добавляем его строки в ArrayList по имени documentLines, поэтому в итоге этот массив содержит все строки файла в том порядке, как они были прочитаны. После считывания файла устанавливается флаг documentHasData, который говорит о том, есть ли в документе содержимое, подлежащее отображению. Следующая задача - вычислить, где именно все должно отображаться, а, покончив с этим, потребуется определить, какой размер клиентской области понадобится для отображения всего файла - т.е. размер документа, необходимый для настройки линеек прокрутки. И, наконец, устанавливается текст заголовка и вызывается Invalidate(). Это один из важнейших методов, предоставленных Microsoft, поэтому в следующем разделе мы первым делом поговорим о нем, прежде чем рассматривать код методов CalculateLineWidths() и CalculateDocumentSize().



Invalidate()

Invalidate() - член класса System.Windows.Forms.Form. Он помечает клиентскую область окна как недействительную, а потому подлежащую перерисовке, и затем убеждается в том, что было возбуждено событие Paint. Метод Invalidate() имеет пару перегрузок: можно передать ему прямоугольник, указывающий точно (в страничных координатах) позицию и размер перерисовываемой области, или же, если не передавать никаких параметров, то недействительной помечается вся клиентская область.

Если нам известно, что какая-то часть требует перерисовки, почему нельзя просто вызвать OnPaint() либо какой-то другой метод, который выполнит ее непосредственно? Ответ состоит в том, что вообще прямой вызов процедур рисования считается дурным тоном программирования. Если ваш код решает что-то перерисовать, он должен вызвать Invalidate(). Ниже описаны причины.

□ Перерисовка - это почти всегда наиболее ресурсоемкая задача приложения GDI+, а потому выполнение ее посреди другой работы задерживает ее ход. В данном примере, если бы мы напрямую вызвали метод, выполняющий перерисовку из LoadFile(), это означало бы, что LoadFile() не вернет управление до тех пор, пока не завершится рисование. В это время наше приложение не могло бы реагировать ни на какие другие события. С другой стороны, вызов Invalidate() просто заставляет Windows возбудить событие Paint перед тем, как немедленно вернуть управление из LoadFile(). После этого Windows может свободно просматривать события, подлежащие обработке. Внутреннее устройство этого механизма таково, что события в виде сообщений находятся в специальной очереди сообщений. Windows периодически просматривает эту очередь, и если в ней есть сообщения, выбирает одно из них и вызывает соответствующий обработчик события. Хотя событие Paint может быть единственным, находящимся в очереди (поэтому OnPaint() будет вызван немедленно), в более сложных приложениях очередь может содержать и другие события, чей приоритет выше, чем у нашего события Paint. В частности, если пользователь решит выйти из приложения, то это будет помечено как сообщение, известное под именем WM QUIT.

□ Если у вас есть более сложное, многопоточное приложение, вероятно, вы решите, чтобы только один его поток обрабатывал рисование. Применение Invalidate() для маршрутизации всех событий рисования через очередь сообщений обеспечивает хорошую возможность гарантировать, что один и тот же поток выполнит все рисование, независимо то того, какой поток его потребовал. (Это будет любой поток, отвечающий за очередь сообщений - тот, который запустил Application.Run().)

□ Существует еще одна причина, связанная с производительностью. Предположим, что есть два разных запроса на рисование части экрана, которые поступают почти одновременно. Может быть, код только что модифицировал документ и желает гарантировать его отображение, в то время как пользователь только что переместил другое окно, которое накрывало часть клиентской области нашего окна. За счет вызова Invalidate() мы предоставляем для Windows шанс узнать об этом. Затем Windows может объединить эти два события Paint, комбинируя недействительные области, чтобы собственно рисование произошло только один раз.



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

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

В очень сложных приложениях вы можете даже написать целый класс, который отвечает за рисование экрана. Несколько лет назад, когда MFC б1ла стандартной технологией разработки GDI-ориентированных приложений, библиотека MFC следовала именно такой модели - с применением класса C++ C<ИмяПриложения>View, отвечавшего за перерисовку. Однако даже в этом случае класс имел одну функцию-член OnDraw(), которая служила точкой входа для большинства запросов рисования.

Вычисление размеров элементов и размеров документа

В этом разделе мы вернемся к примеру CapsEditor и рассмотрим методы CalculateLineWidths() и CalculateDocumentSize(), вызываемые из LoadFile().

private void CalculateLineWidths() {

Graphics dc = this.CreateGraphics();

foreach (TextLineInformation nextLine in documentLines)

nextLine.Width = (uint)dc.MeasureString(nextLine.Text, mainFont).Width;

Этот метод просто проходит по каждой прочитанной строке и использует метод Graphics.MeasureString() для вычисления того, сколько горизонтального пространства экрана ей требуется. Это значение сохраняется для последующего применения, потому что MeasureString() - дорогостоящая по вычислительным ресурсам операция. Если пример CapsEditor недостаточно прост, чтобы легко обработать высоту и местоположение каждого элемента, этот метод почти наверняка придется реализовать, чтобы также вычислить все эти величины.

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

Ниже приведен код метода, вычисляющего размер документа.

private void CalculateDocumentSize()

if (!documentHasData)

documentSize = new Size(100, 200);



1 ... 11 12 13 [ 14 ] 15 16 17 ... 19

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика