Рисующее приложение в версии .NET, страница 8

Одним из способов повышения эффективности процесса перерисовки является отказ от стирания фона окна и перерисовка только той части экрана, которая действительно изменилась. Добавляя в рисунок новую линию, мы изображаем только ее и не трогаем того, что уже есть в окне. В приложении с MDI-интерфейсом одновременно могут существовать несколько окон (видов) с одним и тем же рисунком. Все рисунки-близнецы того вида, который в данный момент изменяется пользователем, должны отслеживать его изменения. Очевидно, что при перерисовке этих окон не следует стирать фон, да и перерисовывать следует только те части, которые изменились. Для этой цели в класс Line вводится поле данных rect, которое соответствует минимальному прямоугольнику, ограничивающему линию. Вычисление прямоугольника следует производить в момент, когда новая линия только что создана и готова попасть в контейнер линий рисунка. Эту задачу выполняет метод CalcRect, реализация которого приведена выше. Приведенная версия навеяна упомянутым примером Sсribble. Она использует метод FromLTRB — генерации нового прямоугольника по координатам двух его вершин (Left-Top и Right-Bottom). Коды метода легко читаются, но они, пожалуй, излишне расточительны, так как для каждой линии прямоугольник rect создается вновь, несмотря на то, что он уже существует. Вызовите справку и убедитесь, что Rectangle — это структура, то есть rect имеет тип value, поэтому память для него уже отведена компилятором. Обратите внимание также на то, что для каждой точки линии создается временный прямоугольник, который затем копируется в новый rect.

Старый прямоугольник при этом оставляется (в области managed heap) для сборщика мусора, видимо, для того, чтобы система всегда выглядела занятой. Мы уже привыкли к тому, что Windows — это чрезвычайно занятая система. Чем выше ее версия, тем больше у нее неотложных дел, тем медленнее работает Windows Explorer и тем мощнее нужен процессор, чтобы работать в привычном темпе.

Приведем более экономную версию, в которой уже существующий прямоугольник корректируется только при необходимости.

public void CalcRect()

{

rect.Size = Size.Empty;

if (points.Count > 0)

{

rect.Location = (Point)points[0];

foreach (Point pt in points)

{

if (pt.X < rect.Left)

rect.X = pt.X;

if (pt.X > rect.Right)

rect.Width = pt.X - rect.X;

if (pt.Y < rect.Top)

rect.Y = pt.Y;

if (pt.Y > rect.Bottom)

rect.Height = pt.Y - rect.Y;

}

rect.Inflate ((int)penWidth, (int)penWidth);

}

}

В Win32 используется понятие invalidated rectangle (поврежденный прямоугольник). Так называется часть окна, которая нуждается в перерисовке. Она указывается в методе Invalidate, унаследованного классом Form, а, следовательно, и окном вида нашего документа (классом DocView) от класса Control. Эта функция провоцирует посылку окну сообщения WM_PAINT. Класс DrawView для управления видом мы создадим позже, но сейчас важно понять, что при его перерисовке понадобится прямоугольник, обрамляющий линию. Только эту часть вида (view) и следует перерисовывать. Например:

view.Invalidate (line.Rect);

Такой вызов приводит к перерисовке только той части окна, которую занимает прямоугольник, обрамляющий линию line.

Класс Документа

Настала пора создать класс документа, структуру данных которого мы обсудили выше в разделе "Архитектура Документ-Вид". Вставьте коды класса DrawDoc внутрь блока namespace Draw файла DrawDoc.cs вслед за кодами класса Line.

public class DrawDoc

{

public int docID;    // Индекс документа

public Pen curPen;   // Текущее перо, задаваемое пользователем

public ArrayList lineList = new ArrayList(); // Список линий рисунка

public ArrayList viewList = new ArrayList(); // Список видов данного документа

public bool isDirty; // Признак того, что файл изменен

public DrawDoc (MainWindow wnd) // Конструктор

{

isDirty = false;

curPen = new Pen (Color.Blue, 2.0f); // Создаем перо, действующее по умолчанию

docID = MainWindow.docCount + 1; // Обращаемся к статической переменной главного окна и узнаем свой индекс