Модификация приложения WindowsForms: функциональность векторного редактора

Для начала определим все необходимые для работы приложения класса внутри самого приложения.

Первый класс с именем файла DrawObject.csстанет основным классом для рисования любых объектов. На его основе будут созданы классы для рисования конкретных объектов типа линий или прямоугольников. Класс будет содержать все возможные функции, события и свойства, необходимы для работы с графическим объектом. А именно этот класс будет определять функция рисования объекта (Draw), выделения, перемещения, изменения размеров и определения количества ключевых точек. Что такое ключевая точка? Для линии это точки на концах линии, которые «подсвечиваются» небольшими прямоугольниками. Если выделить линию и навести мышь на такую точку, курсор будет изменён (зависит от типа точки и объекта рисования). Нажатие на ключевую точку обеспечивает операцию изменения размеров объекта либо перемещения (зависит от объекта рисования). Любой объект во время выделения мышью подсвечивается ключевыми точками. Например, нарисованный и выделенный прямоугольник имеет 8 ключевых точек:

Модификация приложения WindowsForms: функциональность векторного редактора - student2.ru

Код файла DrawObject.cs:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 2 (Файлы классов Draw...)].

Класс GraphicsPropertiesнапрямую связан с дочерней формойLWP15Properties. Обработаем форму LWP15Properties и проинициализируем все необходимые свойства и события.

Откроем код формы (выделим LWP15Properies.cs ->нажмём правую кнопку мыши ->Перейти к коду (F7)). Найдём:

public LWP15Properties()

{

InitializeComponent();

}

Добавимпосле:

privateGraphicsProperties properties;

privateconststring undefined = "Незадано";

privateconstint maxWidth = 10;

publicGraphicsProperties Properties

{

get { return properties; }

set { properties = value; }

}

В самое начало формы добавим код:

using System.Globalization;

Найдём:

namespace LWP15Draw

{

public partialclassLWP15Properties : Form

{

Изменимна:

namespace LWP15Draw

{

partialclassLWP15Properties : Form

{

Проинициализируем первое событие формы LWP15Properties: Load со следующим кодом:

privatevoid LWP15Properties_Load(object sender, EventArgs e)

{

InitControls();

SetColor();

SetPenWidth();

}

Вспомогательные функции добавим сразу после предыдущего фрагмента кода:

privatevoid InitControls()

{

for (int i = 1; i <= maxWidth; i++)

{

CB_PenWidth.Items.Add(i.ToString(CultureInfo.InvariantCulture));

}

}

privatevoid SetColor()

{

if (properties.Color.HasValue) L_Color.BackColor = properties.Color.Value;

else L_Color.Text = undefined;

}

privatevoid SetPenWidth()

{

if (properties.PenWidth.HasValue)

{

int penWidth = properties.PenWidth.Value;

if (penWidth < 1) penWidth = 1;

if (penWidth > maxWidth) penWidth = maxWidth;

label2.Text = penWidth.ToString(CultureInfo.InvariantCulture);

CB_PenWidth.SelectedIndex = penWidth - 1;

}

else { label2.Text = undefined; }

}

privatevoid ReadValues()

{

if (CB_PenWidth.Text != undefined) { properties.PenWidth = CB_PenWidth.SelectedIndex + 1; }

if (L_Color.Text.Length == 0) { properties.Color = L_Color.BackColor; }

}

СобытиеSelectedIndexChangedдляComboBoxэтойформы:

privatevoid CB_PenWidth_SelectedIndexChanged(object sender, EventArgs e)

{

int width = CB_PenWidth.SelectedIndex + 1;

L_PenWidth.Text = width.ToString(CultureInfo.InvariantCulture);

}

СобытиеClickнажатиякнопкиB_SelectColor:

privatevoid B_SelectColor_Click(object sender, EventArgs e)

{

ColorDialog dlg = newColorDialog();

dlg.Color = L_Color.BackColor;

if (dlg.ShowDialog(this) == DialogResult.OK)

{

L_Color.BackColor = dlg.Color;

L_Color.Text = "";

}

}

СобытиеClickнажатиякнопкиB_OK:

privatevoid B_OK_Click(object sender, EventArgs e)

{

ReadValues();

this.DialogResult = DialogResult.OK;

}

Форма готова. Если возникнут ошибки, перепроверяем имена элементов управления.

Следующим классом станет класс, отвечающий за работу со списком графических объектов документа. Данный класс будет реализовывать массив всех графических объектов, нарисованных в документе.

Логика работы с объектами после рисования будет такой:

Каждый новый объект помещается вначале Z-порядка (списка расположения «под курсором»). Таким образом, нарисовав в одном и том же месте несколько объектов и нажав инструментов «Выделение» на этом же месте «по умолчанию» будет выделен последний нарисованный объект. Если изменить положение объекта в Z-списке, отправив объект «назад» («Переместить назад»), наверху окажется предыдущий нарисованный объект. Сам же объект, который мы отправили «назад», будет расположен в конце Z-порядка и будет выделен, если удалить из области все другие объекты.

Файл для класса назовём GraphicsList.cs, код файла будет таким:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 3 (Файлы классов Graphics...)].

Игнорируем возникшие ошибки (3 штуки, исправим позже добавлением других классов).

Также добавим вспомогательный класс для передачи параметров: GraphicsProperties(файл GraphicsProperties.cs)со следующим кодом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 3 (Файлы классов Graphics...)].

Добавляем новый класс, являющийся базовым для команд типа «Отменить» и «Вернуть» («Undo» и «Redo»). Имя файла класса будет Command.cs, код файла вставляем следующий:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4 (Файлы классов Command...)].

Следующий класс (CommandChangeState.cs) станет основным классом для поддержки команд перемещений, изменения размеров и изменения параметров объекта рисования для операций «Отменить» и «Вернуть». Код файла будет следующим:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4 (Файлы классов Command...)].

Класс CommandAdd(CommandAdd.cs), отвечающий за команду добавления объекта для операций «Отменить» и «Вернуть»: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4 (Файлы классов Command...)].

Непосредственно реализация команд «Отменить» и «Вернуть» будет выполнена следующим классом UndoManager (файл UndoManager.cs) с кодом:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 5 (Прочие файлы)].

Класс CommandDelete(CommandDelete.cs), отвечающий за команду удаления выделенного объекта для операций «Отменить» и «Вернуть»: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4 (Файлы классов Command...)].

Класс CommandDeleteAll(CommandDeleteAll.cs), отвечающий за команду удаления всех объектов для операций «Отменить» и «Вернуть»: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4 (Файлы классов Command...)].

Теперь перейдём к классам, поддерживающим рисование графических объектов. Первый и самый примитивный графический объект это линия. Класс на основе DrawObject: DrawLine (DrawLine.cs) реализует всё необходимое для поддержки рисования объекта линии, выделения нарисованного объекта, изменения положения и перемещения объекта линии. Линия рисуется через два три события (нажатие левой кнопки мыши, перемещения и отжатия левой кнопки мыши). Объект имеет де ключевые точки (точка начала и точка конца линии). Сериализуется в файл путём указания следующих параметров: точка начала и точка конца линии, а также строки записей для сохранения в файле: Startи End. Перемещение линии обеспечивается созданием для линии небольшой линейной области (толщина в 7 пикселей от центра линии). Нажатие левой кнопки мыши в этой области приведёт к выделению объекта и активации возможности перемещение, изменения. Изменение размеров линии осуществляется в ключевых точках.

Код файла для реализации класса:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 2 (Файлы классов Draw...)].

Объект получает параметры из инструмента «Линия» следующим обрахом:

privatePoint startPoint;

privatePoint endPoint;

public DrawLine(int x1, int y1, int x2, int y2) : base()

{

startPoint.X = x1;

startPoint.Y = y1;

endPoint.X = x2;

endPoint.Y = y2;

Initialize();

}

Рисует линию так:

///<summary>

/// Главная функция рисования линии на форме

///</summary>

publicoverridevoid Draw(Graphics g)

{

g.SmoothingMode = SmoothingMode.AntiAlias;

Pen pen = newPen(Color, PenWidth);

g.DrawLine(pen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);

pen.Dispose();

}

Прямоугольник. Класс на основе DrawObject: DrawRectangle (DrawRectangle.cs) реализует всё необходимое для поддержки рисования объекта прямоугольника, выделения нарисованного объекта, изменения положения и перемещения объекта прямоугольника. Объект имеет воесь ключевых точек (угловые точки, точки в центре линий). Сериализуется в файл путём указания следующих параметров: область прямоугольника, а также строки записей для сохранения в файле: Rect. Прямоугольник является цельным объектом, область для перемещения которого ограничена стронами прямоугольника. Для перемещения, как и в случае с линией нужно выделить объект (в любом месте прямоугольника) и зажать левую кнопку мыши.

Код файла для реализации класса:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 2 (Файлы классов Draw...)].

Нормализация нужна для правильного определения направления рисования (первое нажатие в левом уголу, движение мыши в правый нижний угол и прочее) и получение положительных значений координат для прямоугольника.

Эллипс. Класс на основе DrawRectangle: DrawEllipse (DrawEllipse.cs) реализует всё необходимое для поддержки рисования объекта эллипса, выделения нарисованного объекта, изменения положения и перемещения объекта эллипса. Объект имеет воесь ключевых точек (угловые точки, точки в центре линий). Сериализуется в файл путём указания следующих параметров: область прямоугольника, а также строки записей для сохранения в файле: Rect. Прямоугольник является цельным объектом, область для перемещения которого ограничена стронами прямоугольника. Для перемещения, как и в случае с линией нужно выделить объект (в любом месте прямоугольника) и зажать левую кнопку мыши.

Код файла для реализации класса:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 2 (Файлы классов Draw...)].

Объект получает следующие параметры из инструмента «Эллипс»:

public DrawEllipse(int x, int y, int width, int height) : base()

{

Rectangle = newRectangle(x, y, width, height);

Initialize();

}

Рисование эллипса реализовано так:

///<summary>

/// Главная функция рисования эллипса на форме

///</summary>

///<param name="g"></param>

publicoverridevoid Draw(Graphics g)

{

Pen pen = newPen(Color, PenWidth);

g.DrawEllipse(pen, DrawRectangle.GetNormalizedRectangle(Rectangle));

pen.Dispose();

}

Карандаш: последний объект рисования. Класс на основе DrawLine: DrawPolygon (DrawPolygon.cs) реализует всё необходимое для поддержки рисования объекта непрерывной линии с малым расстоянием между точками, выделения нарисованного объекта, изменения положения и перемещения объекта карандаша. Объект имеет столько ключевых точек, сколько нарисовано «кусков» + 1 точка). Сериализуется в файл путём указания следующих параметров: точкаидлина(для одного участка), а также строки записей для сохранения в файле: Pointи Length. Карандаш не является цельным объектом (даже в случае замыкания линий). Для перемещения, как и в случае с линией нужно выделить объект (в любом месте нарисоанной ломаной) и зажать левую кнопку мыши. Путь для выделения создаётся также как и в случае с линией, но для каждого участка. Параметр расстояния (до ближайшей точки) регулируется так (будущий файл ToolPolygon.cs):

privateconstint minDistance = 15*15; // Дистанция между ключевыми точками

Кусок кода событиея перемещения мыши во время рисования:

Point point = newPoint(e.X, e.Y);

int distance = (e.X - lastX)*(e.X - lastX) + (e.Y - lastY)*(e.Y - lastY);

if (distance < minDistance)

{

// Если расстояние между последними двумя точками меньше минимального -

// перемещаем последнюю точку

newPolygon.MoveHandleTo(point, newPolygon.HandleCount);

}

else

{

// Добавляем новую точку

newPolygon.AddPoint(point);

lastX = e.X;

lastY = e.Y;

}

Код файла для реализации класса:[искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 2 (Файлы классов Draw...)].

Последние 7 классов относятся к классам реализации инструмента рисования, и именно они инициализируются до вызова класса рисования объекта. Экземпляр класса инструмента передаёт в экземпляр класса рисования все параметры. Первый класс из цепочки является абстрактным базовым классом для реализации работы инструментов. Реализует три простых события действия с мышью (нажатие, перемещение и снятие нажатия). Имя: Tool.cs (класс Tool). Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolObject.cs (класс ToolObject). Это базовый класс для реализации остальных объектов инструментов.Реализует свойства курсора для инструмента, а также функцию добавления нового объекта в список объектов документа:

///<summary>

/// Добавление нового объекта в область рисования.

/// Функция вызывается когда пользователь нажимает ЛКМ на области рисования,

/// и один из полученных ToolObject-инструментов активен.

///</summary>

///<param name="drawArea"></param>

///<param name="o"></param>

protectedvoid AddNewObject(DrawArea drawArea, DrawObject o)

{

drawArea.GraphicsList.UnselectAll();

o.Selected = true;

drawArea.GraphicsList.Add(o);

drawArea.Capture = true;

drawArea.Refresh();

drawArea.SetDirty();

}

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolLine.cs (класс ToolLine). Это базовый класс для реализации инструмента рисования «Линия». Передаёт параметры объекту рисования.Реализует свойства курсора для инструмента, а также функцию добавления нового объекта линии в список объектов документа.

Пример события нажатия ЛКМ в области DrawAreaс созданием экземпляра класса DrawLineи передачей параметров нажатия:

publicoverridevoid OnMouseDown(DrawArea drawArea, MouseEventArgs e)

{

AddNewObject(drawArea, newDrawLine(e.X, e.Y, e.X + 1, e.Y + 1));

}

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolRectangle.cs (класс ToolRectnagle). Это базовый класс для реализации инструмента рисования «Прямоугольник». Передаёт параметры объекту рисования.Реализует свойства курсора для инструмента, а также функцию добавления нового объекта прямоугольника в список объектов документа. Событиенажатиякнопкимыши:

publicoverridevoid OnMouseDown(DrawArea drawArea, MouseEventArgs e)

{

AddNewObject(drawArea, newDrawRectangle(e.X, e.Y, 1, 1));

}

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolEllipse.cs (класс ToolEllipse). Это базовый класс для реализации инструмента рисования «Эллипс». Передаёт параметры объекту рисования.Реализует свойства курсора для инструмента, а также функцию добавления нового объекта эллипса в список объектов документа. Событиенажатиякнопкимыши:

publicoverridevoid OnMouseDown(DrawArea drawArea, MouseEventArgs e)

{

AddNewObject(drawArea, newDrawEllipse(e.X, e.Y, 1, 1));

}

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolPolygon.cs (класс ToolPolygon). Это базовый класс для реализации инструмента рисования «Карандаш». Передаёт параметры объекту рисования.Реализует свойства курсора для инструмента, а также функцию добавления нового объекта непрерывной линии в список объектов документа.

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

ToolPointer.cs (класс ToolPointer). Это базовый класс для реализации инструмента рисования «Выделение». Передаёт параметры объекту рисования.Реализует события связанные с выделение объектов в области рисования, работает с перемещением объекта, изменением размеров объекта и чистым выделением объект(-ов). Этот инструмент также управляет пунктирным прямоугольником выделения:

Модификация приложения WindowsForms: функциональность векторного редактора - student2.ru

if (selectMode == SelectionMode.NetSelection)

{

// Удаляем прямоугольник предыдущего выделения

ControlPaint.DrawReversibleFrame(

drawArea.RectangleToScreen(DrawRectangle.GetNormalizedRectangle(startPoint, oldPoint)),

Color.Black,

FrameStyle.Dashed);

// Рисуем прямоугольник нового выделения

ControlPaint.DrawReversibleFrame(

drawArea.RectangleToScreen(DrawRectangle.GetNormalizedRectangle(startPoint, point)),

Color.Black,

FrameStyle.Dashed);

return;

}

Код файла с классом: [искомый код можно найти в приложении к данной лабораторной работе в (описания можно того или иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 6 (Файлы классов Tool...)].

На этом формирование классов закончено. Теперь можно перейти к реализации функциональности непосредственно для главной формы и элемента, в котором будем рисовать (DrawArea).

Первым делом «добьём» форму LWP15About. Событие Loadдля формы будет таким:

privatevoid LWP15About_Load(object sender, EventArgs e)

{

this.Text = "Опрограмме " + Application.ProductName;

L_About.Text =

"Программа: " + Application.ProductName + "\n" +

"Версия: " + Application.ProductVersion;

}

СобытиеClickкнопкиB_OK:

privatevoid B_OK_Click(object sender, EventArgs e)

{

this.Close();

}

Перейдём к DrawArea.cs. Это основной элемент, в котором происходит «рисование». Любые действия мышью совершаемые в фокусе элемента перехватываются инструментами Tool... и реализуют то или иной действие в зависимости от активного инструмента и действия с мышью. Сам элемент будет растягиваться до строки состояния внизу, границ с боку формы и панели инструментов сверху. Абсолютное значение размеров будет влиять лишь на доступную для размещения объектов область. Эти размеры будут важны лишь при создании растрового изображения (об этом в конце данного материала) на основе графики в элементе.

Откроем код элемента DrawArea.csи заменим все директивы usingв начале файла следующим кодом:

#regionДирективы Using

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Text;

using System.Windows.Forms;

using LWP15Tools; // Подключаембиблиотекуклассов

#endregion

Подключаем к проекту ссылку на нашу библиотеку LWP15tools: жмём правую кнопку мыши на имени проекта LWP15Draw, затем Добавить ссылку...:

Модификация приложения WindowsForms: функциональность векторного редактора - student2.ru

Рис. 6. 1. Добавить ссылку: вкладка Проекты

В открывшемся окне переходим на вкладку Проекты и выбираем LWP15Tools, жмём ОК.

В коде находим:

namespaceLWP15Draw

{

publicpartialclassDrawArea : UserControl

{

public DrawArea()

{

InitializeComponent();

}

}

Заменяемна (последнююфигурную скобку в файле не трогаем):

namespace LWP15Draw

{

partialclassDrawArea : UserControl

{

#regionКонструктор

public DrawArea()

{

InitializeComponent();

}

#endregion

#regionПеречисления

publicenumDrawToolType

{

Pointer, Rectangle, Ellipse, Line, Polygon, NumberOfDrawTools

};

#endregion

#regionЧлены

privateGraphicsList graphicsList; // Списокобъектоврисования

privateDrawToolType activeTool; // Активныйинструментрисования

privateTool[] tools; // Массивинструментов

privateLWP15Main owner;

privateDocManager docManager;

privateContextMenuStrip m_ContextMenu;

privateUndoManager undoManager;

#endregion

#regionСвойства

///<summary>

/// Ссылка на владельца формы

///</summary>

publicLWP15Main Owner

{

get { return owner; }

set { owner = value; }

}

///<summary>

///Ссылкана DocManager

///</summary>

publicDocManager DocManager

{

get { return docManager; }

set

{

docManager = value;

}

}

///<summary>

///Активныйинструментрисования

///</summary>

publicDrawToolType ActiveTool

{

get { return activeTool; }

set { activeTool = value; }

}

///<summary>

///Списокобъектоврисования

///</summary>

publicGraphicsList GraphicsList

{

get { return graphicsList; }

set

{

graphicsList = value;

undoManager = newUndoManager(graphicsList);

}

}

///<summary>

/// true - если операция отмены возможна

///</summary>

publicbool CanUndo

{

get

{

if (undoManager != null) { return undoManager.CanUndo; }

returnfalse;

}

}

///<summary>

/// true - если операция возврата возможна

///</summary>

publicbool CanRedo

{

get

{

if (undoManager != null) { return undoManager.CanRedo; }

returnfalse;

}

}

#endregion

#regionПрочиефункции

///<summary>

///Инициализация

///</summary>

///<param name="owner"></param>

///<param name="docManager"></param>

publicvoid Initialize(LWP15Main owner, DocManager docManager)

{

SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);

// Сохраняем ссылку на владельца формы

this.Owner = owner;

this.DocManager = docManager;

// Устанавливаем инструмент по умолчанию

activeTool = DrawToolType.Pointer;

// Создаём список графических объектов

graphicsList = newGraphicsList();

// Создаём экземпляр UndoManager для текущего файла

undoManager = newUndoManager(graphicsList);

// Создаёммассивинструментоврисования

tools = newTool[(int)DrawToolType.NumberOfDrawTools];

tools[(int)DrawToolType.Pointer] = newToolPointer();

tools[(int)DrawToolType.Rectangle] = newToolRectangle();

tools[(int)DrawToolType.Ellipse] = newToolEllipse();

tools[(int)DrawToolType.Line] = newToolLine();

tools[(int)DrawToolType.Polygon] = newToolPolygon();

}

///<summary>

/// Добавления команды в историю

///</summary>

publicvoid AddCommandToHistory(Command command)

{

undoManager.AddCommandToHistory(command);

}

///<summary>

///Очисткаистории

///</summary>

publicvoid ClearHistory()

{

undoManager.ClearHistory();

}

///<summary>

///Отменить

///</summary>

publicvoid Undo()

{

undoManager.Undo();

Refresh();

}

///<summary>

///Вернуть

///</summary>

publicvoid Redo()

{

undoManager.Redo();

Refresh();

}

///<summary>

/// Устанавливаем флаг "грязный" (файл был изменён после последней операции сохранения)

///</summary>

publicvoid SetDirty()

{

DocManager.Dirty = true;

}

///<summary>

/// Обработчик нажатия правой кнопки мышки

///</summary>

///<param name="e"></param>

privatevoid OnContextMenu(MouseEventArgs e)

{

// Измененяем текущий выбор при необходимости

Point point = newPoint(e.X, e.Y);

int n = GraphicsList.Count;

DrawObject o = null;

for (int i = 0; i < n; i++)

{

if (GraphicsList[i].HitTest(point) == 0)

{

o = GraphicsList[i];

break;

}

}

if (o != null)

{

if (!o.Selected) GraphicsList.UnselectAll();

// Выбор объекта произведён

o.Selected = true;

}

else

{

GraphicsList.UnselectAll();

}

Refresh(); // В случае изменения выбора

// Выводин контекстное меню (всплывающее).

// Элементы меню вставлены из строки меня, главного элемента "Правка"

m_ContextMenu = newContextMenuStrip();

int nItems = owner.ContextParent.DropDownItems.Count;

// Получаем элементы меню "Правка" и перемещаем их на контекстное-всплывающее меню.

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

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

for (int i = nItems - 1; i >= 0; i--)

{

m_ContextMenu.Items.Insert(0, owner.ContextParent.DropDownItems[i]);

}

// Выводит контекстное меню для владельца формы, а также обрабатывает элементы выбора.

// Преобразует координаты точки в этом окне к координатам владельца

point.X += this.Left;

point.Y += this.Top;

m_ContextMenu.Show(owner, point);

Owner.SetStateOfControls(); // Включение/выключение элементов меню

// Контекстное меню вызвано, но меню "Правка" владельца теперь пусто.

// Подписываемся на событие закрытия контекстного меню и восстанавливаем там элементы

m_ContextMenu.Closed += delegate(object sender, ToolStripDropDownClosedEventArgs args)

{

if (m_ContextMenu != null)

{

nItems = m_ContextMenu.Items.Count;

for (int k = nItems - 1; k >= 0; k--) { owner.ContextParent.DropDownItems.Insert(0, m_ContextMenu.Items[k]); }

}

};

}

#endregion

}

По очереди инициализируем для элемента DrawAreaследующие события с кодом. Вначале идёт событие Paint:

#region Обработчики событий

///<summary>

/// Рисование графического объекта и группировка прямоугольника выделения (опционально)

///</summary>

privatevoid DrawArea_Paint(object sender, PaintEventArgs e)

{

SolidBrush brush = newSolidBrush(Color.FromArgb(255, 255, 255));

e.Graphics.FillRectangle(brush, this.ClientRectangle);

if (graphicsList != null) { graphicsList.Draw(e.Graphics); }

//DrawNetSelection(e.Graphics);

brush.Dispose();

}

СобытиеMouseDown, MouseMoveиMouseUpдляDrawArea:

///<summary>

/// Событие MouseDown для DrawArea

/// ЛКМ: перехватывается активным инструментом

/// ПКМ: перехватывается и описано в данном классе

///</summary>

privatevoid DrawArea_MouseDown(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left) tools[(int)activeTool].OnMouseDown(this, e);

elseif (e.Button == MouseButtons.Right) OnContextMenu(e);

}

///<summary>

/// Событие MouseMove для DrawArea

/// Перемещение без нажатия кнопок или с нажатие левой кнопки мыши перехватываетсмя активным инструментом

///</summary>

privatevoid DrawArea_MouseMove(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left || e.Button == MouseButtons.None) tools[(int)activeTool].OnMouseMove(this, e);

elsethis.Cursor = Cursors.Default;

}

///<summary>

/// Событие MouseUp для DrawArea

/// ЛКМ: перехватывается активным инструментом

///</summary>

privatevoid DrawArea_MouseUp(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Left) tools[(int)activeTool].OnMouseUp(this, e);

}

#endregion

Переходим к главной форме LWP15Main. Переписываем директивы using:

#regionДирективыUsing

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Runtime.Serialization;

using System.Diagnostics;

using System.Security;

using Microsoft.Win32;

using LWP15Tools;

#endregion

Находим:

publicpartialclassLWP15Main : Form

{

Заменяемна:

partialclassLWP15Main : Form

{

#regionЧлены

privateDrawArea drawArea;

privateDocManager docManager;

privateDragDropManager dragDropManager;

privateMruManager mruManager;

privatePersistWindowState persistState;

privatestring argumentFile = ""; // Имяфайлаизкоманднойстроки

conststring registryPath = "Software\\LWP15Draw";

#endregion

#regionСвойства

///<summary>

///Имяфайлаизкоманднойстроки

///</summary>

publicstring ArgumentFile

{

get { return argumentFile; }

set { argumentFile = value; }

}

///<summary>

///Получаемссылкунаэлементстрокименю"Правка".

/// Используется при вызове контекстного-всплывающего меню в классе DrawArea

///</summary>

///<value></value>

publicToolStripMenuItem ContextParent

{

get { returnправкаToolStripMenuItem; }

}

#endregion

#regionКонструктор

public LWP15Main()

{

InitializeComponent();

persistState = newPersistWindowState(registryPath, this);

}

#endregion

#regionОбработчикисобытийдля DocManager

///<summary>

/// Загрузка документа из потока поставляемая DocManager

///</summary>

///<param name="sender"></param>

///<param name="args"></param>

privatevoid docManager_LoadEvent(object sender, SerializationEventArgs e)

{

// DocManager просит загрузить документ в поставляемый поток

try

{

drawArea.GraphicsList = (GraphicsList)e.Formatter.Deserialize(e.SerializationStream);

}

catch (ArgumentNullException ex) { HandleLoadException(ex, e); }

catch (SerializationException ex) { HandleLoadException(ex, e); }

catch (SecurityException ex) { HandleLoadException(ex, e); }

}

///<summary>

/// Сохранение документа в поток поставляемый DocManager

///</summary>

///<param name="sender"></param>

///<param name="args"></param>

privatevoid docManager_SaveEvent(object sender, SerializationEventArgs e)

{

// DocManager просит сохранить документ в поставляемый поток

try

{

e.Formatter.Serialize(e.SerializationStream, drawArea.GraphicsList);

}

catch (ArgumentNullException ex) { HandleSaveException(ex, e); }

catch (SerializationException ex) { HandleSaveException(ex, e); }

catch (SecurityException ex) { HandleSaveException(ex, e); }

}

#endregion

Теперь поочерёдно создаём события строки меню (двойное нажатие на элементе меню для инициализации события Click). В качестве кнопок будут выступать все подпункты (дочерние пункты) меню кроме подпункта «Последние файлы» меню «Файл». Блок с кодом событий Clickвсей строки меню будет таким:

#regionОбработчикисобытийстрокименю

privatevoidсоздатьToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandNew();

}

privatevoidоткрытьToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandOpen();

}

privatevoidсохранитьToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandSave();

}

privatevoidсохранитькакToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandSaveAs();

}

privatevoidвыходToolStripMenuItem_Click(object sender, EventArgs e)

{

this.Close();

}

privatevoidотменитьToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandUndo();

}

privatevoidвернутьToolStripMenuItem1_Click(object sender, EventArgs e)

{

CommandRedo();

}

privatevoidвыделитьвсеToolStripMenuItem_Click(object sender, EventArgs e)

{

drawArea.GraphicsList.SelectAll();

drawArea.Refresh();

}

privatevoidснятьВыделениеToolStripMenuItem_Click(object sender, EventArgs e)

{

drawArea.GraphicsList.UnselectAll();

drawArea.Refresh();

}

privatevoidудалитьToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandDelete command = newCommandDelete(drawArea.GraphicsList);

if (drawArea.GraphicsList.DeleteSelection())

{

drawArea.SetDirty();

drawArea.Refresh();

drawArea.AddCommandToHistory(command);

}

}

privatevoidудалитьВсеToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandDeleteAll command = newCommandDeleteAll(drawArea.GraphicsList);

if (drawArea.GraphicsList.Clear())

{

drawArea.SetDirty();

drawArea.Refresh();

drawArea.AddCommandToHistory(command);

}

}

privatevoidпереместитьНазадToolStripMenuItem_Click(object sender, EventArgs e)

{

if (drawArea.GraphicsList.MoveSelectionToBack())

{

drawArea.SetDirty();

drawArea.Refresh();

}

}

privatevoidпереместитьВпередToolStripMenuItem_Click(object sender, EventArgs e)

{

if (drawArea.GraphicsList.MoveSelectionToFront())

{

drawArea.SetDirty();

drawArea.Refresh();

}

}

privatevoidпараметрыToolStripMenuItem_Click(object sender, EventArgs e)

{

if (drawArea.GraphicsList.ShowPropertiesDialog(drawArea))

{

drawArea.SetDirty();

drawArea.Refresh();

}

}

privatevoidвыделениеToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandPointer();

}

privatevoidкарандашToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandPolygon();

}

privatevoidлинияToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandLine();

}

privatevoidэллипсToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandEllipse();

}

privatevoidпрямоугольникToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandRectnagle();

}

privatevoidопрограммеToolStripMenuItem_Click(object sender, EventArgs e)

{

CommandAbout();

}

#endregion

Тожесамое проделываем для строки с иконками (панелью инструментов). Двойное нажатие на иконку инициализирует событие Click:

#regionОбработчикисобытийпанелиинструментов

privatevoidсоздатьToolStripButton_Click(object sender, EventArgs e)

{

CommandNew();

}

privatevoidоткрытьToolStripButton_Click(object sender, EventArgs e)

{

CommandOpen();

}

privatevoidсохранитьToolStripButton_Click(object sender, EventArgs e)

{

CommandSave();

}

privatevoidвыделениеToolStripButton_Click(object sender, EventArgs e)

{

CommandPointer();

}

privatevoidкарандашToolStripButton_Click(object sender, EventArgs e)

{

CommandPolygon();

}

privatevoidлинияToolStripButton_Click(object sender, EventArgs e)

{

CommandLine();

}

privatevoidэллипсToolStripButton_Click(object sender, EventArgs e)

{

CommandEllipse();

}

privatevoidпрямоугольникToolStripButton_Click(object sender, EventArgs e)

{

CommandRectangle();

}

privatevoidотменитьToolStripButton_Click(object sender, EventArgs e)

{

CommandUndo();

}

privatevoidвернутьToolStripButton_Click(object sender, EventArgs e)

{

CommandRedo();

}

privatevoidсправкаToolStripButton_Click(object sender, EventArgs e)

{

CommandAbout();

}

#endregion

Инициализируем три события для главной формы. Событие Loadформы LWP15Main:

#regionОбработчикисобытий

privatevoid LWP15Main_Load(object sender, EventArgs e)

{

toolStripStatusLabel.Text = "Готовкработе";

// Создаёмобластьрисования

drawArea = newDrawArea();

drawArea.Location = new System.Drawing.Point(0, 0);

drawArea.Size = new System.Drawing.Size(10, 10);

drawArea.Owner = this;

this.Controls.Add(drawArea);

// Вспомогательные объекты (DocManager и прочие)

InitializeHelperObjects();

drawArea.Initialize(this, docManager);

ResizeDrawArea();

LoadSettingsFromRegistry();

// Submit to Idle event to set controls state at idle time

Application.Idle += delegate(object o, EventArgs a)

{

SetStateOfControls();

};

// Открытый файл передаётся в командную строку

if (ArgumentFile.Length > 0) OpenDocument(ArgumentFile);

// Подписываемся на событие DropDownOpened для каждого всплывающего меню

foreach (ToolStripItem item in menuStrip1.Items)

{

if (item.GetType() == typeof(ToolStripMenuItem)) { ((ToolStripMenuItem)item).DropDownOpened += LWP15Main_DropDownOpened; }

}

}

Событие Resize:

///<summary>

/// Изменение размеров DrawArea, Когда меняются размеры формы

///</summary>

privatevoid LWP15Main_Resize(object sender, EventArgs e)

{

if (this.WindowState != FormWindowState.Minimized && drawArea != null) { ResizeDrawArea(); }

}

Событие FormClosing:

///<summary>

/// Событие закрытия формы

///</summary>

privatevoid LWP15Main_FormClosing(object sender, FormClosingEventArgs e)

{

if (e.CloseReason == CloseReason.UserClosing)

{

if (!docManager.CloseDocument()) e.Cancel = true;

}

SaveSettingsToRegistry();

}

Последнее событие формы без инициализации (просто вставляем код после последнего события):

///<summary>

/// Всплывающая строка меню ("Файл", "Правка" и прочее) открыто

///</summary>

///<param name="sender"></param>

///<param name="e"></param>

void LWP15Main_DropDownOpened(object sender, EventArgs e)

{

// Устанавливаем активный инструмент на "Выделение".

// Педотвращает редкий сбой, когда выбран ино<

Наши рекомендации