После прочтения этой главы вы должны четко понимать, как создавать обработчики сообщений мыши и выполнять операции рисования в программах Windows. Вы узнали, как использование полиморфизма с классами форм позволяет одинаковым способом работать с любой формой независимо от ее фактического типа. Версия С++/CLI приложения Sketcher демонстрирует, насколько проще работать в среде .NET, чем с базовым языком С++ и библиотекой MFC.
Упражнения
- Добавьте в версию MFC программы Sketcher пункт меню и кнопку панели инструментов для элемента типа эллипс так же, как это делалось в упражнениях главы 15, и определите класс для поддержки рисования эллипсов по двум заданным точкам в противоположных углах описывающего прямоугольника. Решение.
- Какие функции теперь нужно изменить, чтобы обеспечить поддержку рисования эллипса? Измените программу так, чтобы она позволяла рисовать эллипс. Решение.
- Какие функции следует изменить в предыдущем упражнении, чтобы первая точка обозначала центр эллипса, а текущая позиция курсора — угол описывающего прямоугольника? Внесите все необходимые для этого изменения. (Подсказка: просмотрите в справочной системе информацию о членах класса CPoint.). Решение.
- Добавьте в меню с идентификатором IDR_SketcherTYPE новое всплывающее меню Pen Style (Стиль пера), позволяющее выбирать для рисования сплошные, пунктирные, точечные и штрихпунктирные линии, а также штрихпунктирные линии с двойной точкой. Решение.
- Какие части программы нужно изменить далее для того, чтобы обеспечить работоспособность данного меню и поддержку рисования элементов с помощью линий этих типов? Решение.
- Реализуйте поддержку для этого нового всплывающего меню и рисования элементов с помощью линий любого из предлагаемых в нем типов. Решение.
- Измените версию приложения CLRSketcher так, чтобы оно поддерживало рисование овала по двум точкам в противоположных углах описывающего прямоугольника. Добавьте в панель инструментов кнопку, а в меню — пункт для выбора режима рисования овала. Решение.
- Реализуйте в приложении CLRSketcher меню Line Style (Стиль линии) со следующими пунктами: Solid (Сплошная), Dashed (Пунктирная) и Dotted (Точечная). Добавьте для этих элементов меню соответствующие кнопки панели инструментов вместе со всплывающими подсказками. Затем реализуйте в приложении CLRSketcher возможность рисования элементов с помощью линий любого из трех стилей, представленных в этом новом меню. Создайте свое собственное представление для этих стилей линии за счет соответствующей установки свойства DashPattern объекта Pen. Решение.
Добавьте в версию MFC программы Sketcher пункт меню и кнопку панели инструментов для элемента типа эллипс так же, как это делалось в упражнениях главы 15, и определите класс для поддержки рисования эллипсов по двум заданным точкам в противоположных углах описывающего прямоугольника.
И так приступим к решению упражнений. У нас имеется приложение такое которое описано в посту MFC заставляем редактор рисовать фигуры .
как видно на рисунке у нас нету кнопки для рисования эллипса и так же нету элемента Ellipse в панели меню Element, поэтом давайте добавим эти кнопки, для этого переходим в Окно ресурсов , выбираем пункт Menu и идентификатор меню IDR_Sketcher13TYPE
выбираем меню Element и добавляем новый подпункт Ellipse, у нас создастся новый элемент с идентификатором ID_ELEMENT_ELLIPSE
кликаем правой кнопкой мышки по добавленному меню, в открывшемся контекстном меню выбираем пункт меню Добавить обработчик событий…
появится окошко Мастер обработчика событий в нем будет сгенерировано название обработчика у меня это OnElementEllipse, так же выбираем класс в который будет добавлен обработчик мы добавляем в класс вида у меня это CSketcher13Doc, тип сообщения обработчика выбираем COMMAND. После выставления нужных настроек нажимаем кнопку Добавить/Править
У нас создастся в файле CSketcher13Doc.cpp обработчик события выбора пункта меню OnElementEllipse и нас туда перенесет
1 2 3 4 |
void CSketcher13Doc::OnElementEllipse() { // TODO: добавьте свой код обработчика команд } |
Добавим код этого обработчика
1 |
elementType=ELLIPSE; |
Добавим ELLIPSE в наше перечисление ElementType в файле где мы создали все константы SketchersConstants.h
1 2 3 |
... enum ElementType{LINE,RECTANGLE,CIRCLE,CURVE,ELLIPSE}; ... |
Дальше создаем обработчик сообщения UPDATE_COMMSNT_UI, снова выбираем меню Element и пункт Ellipse кликаем правой клавишей мышки и выбираем пункт Добавить обработчик событий…
появится окошко выбираем класс в который будет добавлен обработчик у меня это CSketcher13Doc, тип сообщения UPTADE_COMMAND_UI, имя функции-обработчика OnUpdateElementEllipse и нажимаем кнопку Добавить/Править
И нас переносит в пустой обработчик событий
1 2 3 4 |
void CSketcher13Doc::OnUpdateElementEllipse(CCmdUI *pCmdUI) { // TODO: добавьте свой код обработчика ИП обновления команд } |
добавим в него код
1 |
pCmdUI->SetCheck(elementType==ELLIPSE); |
Все с пунктом меню покончено, теперь нужно добавить кнопку на панели инструментов для этого переходим в Окно ресурсов -> Toolbar -> IDR_MAINFRAME_256 рисуем изображение кнопки эллипс и в свойствах присваиваем идентификатор элемента ID_ELEMENT_ELLIPSE так как на скрине ниже
как видим на рисунке кнопочка находится там где цвета, перенесем ее к элементам, просто перетащите мышкой
На этом все можно попробовать скомпилировать программу и посмотреть что у нас получилось
пункты в панели инструментов могут быть не на своих местах, для того чтобы они стали на свои места, нужно обновить панель элементов.
после обновления кнопки должны стать на свои места. Кнопочки и пункт меню работает, но увы мы ничего рисовать не можем еще, в упражнении 1 нам нужно так же запрограммировать что б можно было рисовать. И так приступим к созданию класса Ellipse. Добавлять этот класс будем в класс вида CSketcher13View, идем в Окно классов, кликаем правой клавишей мышки и выбираем пункт меню Добавить, а дальше подпункт Класс
У нас появится окошко классов Добавление класса, выбираем класс С++ и нажимаем кнопку Добавить
Появится окошко Мастер универсальных классов устанавливаем настройки такие как на скрине ниже
У нас создастся пустой класс в файле Elements.h и его реализация в файле Elements.cpp. Реализуем класс Ellipse я как что делал описывать не буду, просто смотрите код, там все хорошо будет закомментировано.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class CEllipse : public CElement { protected: CPoint m_StartPoint; CPoint m_EndPoint; public: ~CEllipse(void); virtual void Draw(CDC* pDC);//функция рисования //конструктор преобразования CEllipse(const CPoint& start, const CPoint& end, COLORREF aColor); protected: CEllipse(void);//запрет вызова }; |
class Ellipse реализация функций »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void CEllipse::Draw(CDC* pDC) { CPen aPen;//объект пера //если не удалось создать перо if(!aPen.CreatePen(PS_SOLID,m_PenWidth,m_Color)) { //вывод сообщения AfxMessageBox(_T("Не удалось создать перо для элипса"),MB_OK); AfxAbort();//завершить программу } //выбрать перо и кисть CPen* pOldPen=pDC->SelectObject(&aPen); CBrush* pOldBrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH); //нарисовать элипс pDC->Ellipse(m_EnclosingRect); //выбрать старые перо и кисть pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); } |
И изменяем функцию CSketcher13View::CreateElement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
CElement* CSketcher13View::CreateElement(void) { CSketcher13Doc* pDoc=GetDocument();//получаем указатель на класс документа switch(pDoc->GetElementType()) { case LINE: return new CLine(m_StartPoint,m_EndPoint,pDoc->GetElementColor()); case RECTANGLE: return new CRectangle(m_StartPoint, m_EndPoint, pDoc->GetElementColor()); case CIRCLE: return new CCircle(m_StartPoint, m_EndPoint, pDoc->GetElementColor()); case CURVE: return new CCurve(m_StartPoint, m_EndPoint, pDoc->GetElementColor()); case ELLIPSE: return new CEllipse(m_StartPoint,m_EndPoint, pDoc->GetElementColor()); default: AfxMessageBox(_T("не известный обьект, не удалось создать [CreateElement]"),MB_OK); AfxAbort();//завершение программы } } |
Компилируем программу и у нас уже можно рисовать эллипс
Все на этом мы упражнение 1 сделали, переходим ко второму упражнению
Какие функции теперь нужно изменить, чтобы обеспечить поддержку рисования эллипса? Измените программу так, чтобы она позволяла рисовать эллипс.
Мы уже в первом упражнении изменили программу так, что можно уже рисовать эллипс. Изменили мы всего одну функцию CSketcher13View::CreateElement. и все, больше ничего не меняли. Переходим к следующему упражнению
Какие функции следует изменить в предыдущем упражнении, чтобы первая точка обозначала центр эллипса, а текущая позиция курсора — угол описывающего прямоугольника? Внесите все необходимые для этого изменения. (Подсказка: просмотрите в справочной системе информацию о членах класса CPoint.)
От тут ребят хз. мой код что выше там точка обозначает не центр а угол, а вторая точка обозначает нижний угол, мы рисуем из верхнего левого угла к правому нижнему углу. Да что б рисовать как в этой задаче предлагают, то нужно видимо изменить сам конструктор класса Ellipse, просто нужно переопределить левый верхний угол. Ну это в принципе не сложно сделать. Например центр будет точка с координатами х1(10,10), а низ x2(12,12), то ребятки как же определить верх? ну верх будет x0(10-(12-10),10-(12-10)). По такой формуле будем рассчитывать. Давайте изменим конструктор и посмотрим, что у нас получится.
код конструктора
1 2 3 4 5 6 7 8 9 10 11 |
CEllipse::CEllipse(const CPoint& start, const CPoint& end, COLORREF aColor) { m_StartPoint.x=start.x-(end.x-start.x); m_StartPoint.y=start.y-(end.y-start.y); m_EndPoint=end; m_PenWidth=1; m_Color=aColor; m_EnclosingRect=CRect(m_StartPoint,end); m_EnclosingRect.NormalizeRect(); } |
и от что мы можем рисовать, реально мы рисуем сейчас эллипс от центра
идем дальше
Добавьте в меню с идентификатором IDR_SketcherTYPE новое всплывающее меню Pen Style (Стиль пера), позволяющее выбирать для рисования сплошные, пунктирные, точечные и штрихпунктирные линии, а также штрихпунктирные линии с двойной точкой.
Ну тут все более менее ясно, нам нужно добавить переменную например DWORD elementStyle в класс документа CSketcher13Doc, в конструкторе инициализируем ее значением PS_SOLID (сплошная линия) это что б по умолчанию была выбрана сплошная линия. Дальше создаем меню так как мы создавали пункт меню для Ellipse так же и мы создаем целый пункт меню Pen Style и добавляем в него подпункты PS_SOLID, PS_DASH, PS_DOT и PS_DASHDOT. Добавляем для каждого из подпунктов меню обработчики событий COMMAND и UPDATE_COMMAND_UI. От код для обработчиков COMMAND
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void CSketcher13Doc::OnPenstyleSolid() { elementStyle=PS_SOLID; } void CSketcher13Doc::OnPenstyleDash() { elementStyle=PS_DASH; } void CSketcher13Doc::OnPenstyleDot() { elementStyle=PS_DOT; } void CSketcher13Doc::OnPenstyleDashdot() { elementStyle=PS_DASHDOT; } |
а это код для UPDATE_COMMAND_UI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void CSketcher13Doc::OnUpdatePenstyleSolid(CCmdUI *pCmdUI) { pCmdUI->SetCheck(elementStyle==PS_SOLID); } void CSketcher13Doc::OnUpdatePenstyleDash(CCmdUI *pCmdUI) { pCmdUI->SetCheck(elementStyle==PS_DASH); } void CSketcher13Doc::OnUpdatePenstyleDot(CCmdUI *pCmdUI) { pCmdUI->SetCheck(elementStyle==PS_DOT); } void CSketcher13Doc::OnUpdatePenstyleDashdot(CCmdUI *pCmdUI) { pCmdUI->SetCheck(elementStyle==PS_DASHDOT); } |
так же в документе еще создаем функцию для возврата стиля элемента GetElementStyle
1 2 3 4 |
DWORD CSketcher13Doc::GetElementStyle(void) { return elementStyle; } |
и теперь переходим к функции класса вида CreateElement в ней пока что для элемента CEllipse создаем элемент и в конструкторе передаем четвертый параметр это стиль элемента pDC->GetElementStyle()
1 2 |
case ELLIPSE: return new CEllipse(m_StartPoint,m_EndPoint, pDoc->GetElementColor(),pDoc->GetElementStyle()); |
Ну и все переходим к элементу CEllipse нам нужно изменить код конструктора на следующий
объявление конструктора
1 2 3 4 |
... //конструктор преобразования CEllipse(const CPoint& start, const CPoint& end, COLORREF aColor, DWORD aStyle ... |
определение конструктора
1 2 3 4 5 6 7 8 9 10 11 |
CEllipse::CEllipse(const CPoint& start, const CPoint& end, COLORREF aColor, DWORD aStyle) { m_Style=aStyle;//переменная стиля m_StartPoint.x=start.x-(end.x-start.x); m_StartPoint.y=start.y-(end.y-start.y); m_EndPoint=end; m_PenWidth=1; m_Color=aColor; m_EnclosingRect=CRect(m_StartPoint,end); m_EnclosingRect.NormalizeRect(); } |
Тут мы видим новую переменную m_Style, ее нужно определить в абстрактном классе CElement, добавим ее туда
1 2 |
protected: DWORD m_Style;//стиль линии |
еще нужно изменить функцию рисования в классе CEllipse, от новое определение этой функции
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void CEllipse::Draw(CDC* pDC) { CPen aPen;//объект пера //если не удалось создать перо if(!aPen.CreatePen(m_Style,m_PenWidth,m_Color)) { //вывод сообщения AfxMessageBox(_T("Не удалось создать перо для элипса"),MB_OK); AfxAbort();//завершить программу } //выбрать перо и кисть CPen* pOldPen=pDC->SelectObject(&aPen); CBrush* pOldBrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH); //нарисовать элипс pDC->Ellipse(m_EnclosingRect); //выбрать старые перо и кисть pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); } |
Ну и дальше все компилируем и рисуем эллипсы разными стилями. ВУаля!
Для всех остальных элементов так же само нужно изменить конструктор и функцию рисования Draw, это делается аналогично тому как мы делали для элемента CEllipse
Переходим к решению следующих упражнений.
Какие части программы нужно изменить далее для того, чтобы обеспечить работоспособность данного меню и поддержку рисования элементов с помощью линий этих типов?
Мы изменили документ и вид, а также классы элементов, смотрим упражнение 4.
Реализуйте поддержку для этого нового всплывающего меню и рисования элементов с помощью линий любого из предлагаемых в нем типов.
Ну мы ее реализовали в упражнении 4, смотрите его.
Измените версию приложения CLRSketcher так, чтобы оно поддерживало рисование овала по двум точкам в противоположных углах описывающего прямоугольника. Добавьте в панель инструментов кнопку, а в меню — пункт для выбора режима рисования овала.
И так у нас пока что следующая программа есть создание которой описано в посте С++ CLI Рисование в окне
Она имеет следующий вид
Работают кнопочки и можно рисовать фигуры, нам нужно добавить пункт в меню Element и кнопку на панели инструментов для рисования элемента Ellipse и так сделаем это. Идем в конструктор классов и добавляем пункт меню Ellipse
дальше выбираем этот пункт меню и кликаем два раза мышкой по пункту меню click этим мы создадим и перенесемся в обработчик
1 2 3 4 |
private: System::Void ellipseToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { } |
в нем добавим код
1 2 |
elementType=ElementType::ELLIPSE; ElementSet(); |
Но у нас в программе нет элемента ElementType::ELLIPSE, добавим его в наше перечисление (оно находится в начале файла)
1 |
enum class ElementType{LINE,RECTANGLE,CIRCLE,CURVE,ELLIPSE}; |
Также изменим функцию которая будет ставить галочку напротив элемента elementToolStripMenuItem_DropDownOpening
1 2 3 4 5 6 7 |
System::Void elementToolStripMenuItem_DropDownOpening(System::Object^ sender, System::EventArgs^ e) { lineToolStripMenuItem->Checked=elementType==ElementType::LINE; rectangleToolStripMenuItem->Checked=elementType==ElementType::RECTANGLE; circleToolStripMenuItem->Checked=elementType==ElementType::CIRCLE; curveToolStripMenuItem->Checked=elementType==ElementType::CURVE; ellipseToolStripMenuItem->Checked=elementType==ElementType::ELLIPSE; } |
теперь добавим кнопку на панели инструментов нарисуем там элипс
Ну я нихо щас расписывать как создавать эту кнопку, это все написано в посте C++ CLI Рисование в окне , мы просто добавили один и тот же обработчик как и для меню, так и для кнопки на панели инструментов ellipseToolStripMenuItem_Click, да и функцию которая будет выделять кнопку так же нужно добавить элемент с идентификатором кнопки, я его назвал toolStripEllipseButton
1 2 3 4 5 6 7 8 9 |
// Функция модифицирует панель элементов void ElementSet(void) { toolStripLineButton->Checked=elementType==ElementType::LINE; toolStripRectangleButton->Checked=elementType==ElementType::RECTANGLE; toolStripCircleButton->Checked=elementType==ElementType::CIRCLE; toolStripCurveButton->Checked=elementType==ElementType::CURVE; toolStripEllipseButton->Checked=elementType==ElementType::ELLIPSE; } |
и теперь пробуем компелировать программу и как мы видим все у нас работает, все выделяется!
Давайте теперь добавим код класса Ellipse который будет рисовать эллипс.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public ref class Ellipse : Element { protected: int width; int height; public: Ellipse(Color color,Point start,Point end) { pen=gcnew Pen(color); this->color=color; position=start;//позиция height=Math::Abs(end.Y-start.Y); width=Math::Abs(end.X-start.X); //описывающий четырехугольник boundRect=System::Drawing::Rectangle(position,Size(width,height)); } virtual void Draw(Graphics^ g) override { g->DrawEllipse(pen,position.X,position.Y,width,height); } }; |
нужно чуток изменить функцию обработчика мышки от ее код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
System::Void Form1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { if(drawing) { switch(elementType) { case ElementType::LINE: tempElement=gcnew Line(color,firstPoint,e->Location); break; case ElementType::RECTANGLE: tempElement=gcnew Rectangle(color,firstPoint,e->Location); break; case ElementType::CIRCLE: tempElement=gcnew Circle(color,firstPoint,e->Location); break; case ElementType::CURVE: if(tempElement==nullptr) tempElement=gcnew Curve(color,firstPoint,e->Location); else safe_cast<Curve^>(tempElement)->Add(e->Location); break; case ElementType::ELLIPSE: tempElement=gcnew Ellipse(color,firstPoint,e->Location); break; // MessageBox::Show("Hellow"); } Invalidate(); } } |
Все компилируем программу и смотри что у нас получилось.
в общем мы можем теперь рисовать эллипс, переходим к следующему упражнению.
Реализуйте в приложении CLRSketcher меню Line Style (Стиль линии) со следующими пунктами: Solid (Сплошная), Dashed (Пунктирная) и Dotted (Точечная). Добавьте для этих элементов меню соответствующие кнопки панели инструментов вместе со всплывающими подсказками. Затем реализуйте в приложении CLRSketcher возможность рисования элементов с помощью линий любого из трех стилей, представленных в этом новом меню. Создайте свое собственное представление для этих стилей линии за счет соответствующей установки свойства DashPattern объекта Pen.
И так начинаем, я только что построил чтобы работало это меню LineStyle пока только для Ellips. ну давайте разбираем, мы идем в конструктор форм и добавляем новое меню и его подпункты так как на рисунке
Для каждого меню у нас создается обработчик и мы их заполняем смотрите код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private: System::Void ellipseToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { elementType=ElementType::ELLIPSE; ElementSet(); } private: System::Void solidToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { Style=ElementStyle::SOLID; } private: System::Void dasherToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { Style=ElementStyle::DASHER; } private: System::Void dotterToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { Style=ElementStyle::DOTTER; } |
также создаем переменную Style типа ElementStyle и инициализируем ее в конструкторе значением ElementStyle::SOLID
Мы еще создаем в файле Form.h перечисление ElementStyle в пространстве имен нашего приложения где находится класс Form1, добавляем наше перечисление перед подключением файла #include «Elements.h». Это специально сделано, чтобы данное перечисление было доступно и в классах элементов.
1 2 3 4 5 6 |
#pragma once namespace CLRSketcher33{ enum class ElementStyle{SOLID,DASHER,DOTTER}; } #include"Elements.h" .... |
Подредактируем обработчик сообщения мышки, изменим немного код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private: System::Void Form1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { if(drawing) { switch(elementType) { case ElementType::LINE: tempElement=gcnew Line(color,firstPoint,e->Location); break; case ElementType::RECTANGLE: tempElement=gcnew Rectangle(color,firstPoint,e->Location); break; case ElementType::CIRCLE: tempElement=gcnew Circle(color,firstPoint,e->Location); break; case ElementType::CURVE: if(tempElement==nullptr) tempElement=gcnew Curve(color,firstPoint,e->Location); else safe_cast<Curve^>(tempElement)->Add(e->Location); break; case ElementType::ELLIPSE: tempElement=gcnew Ellipse(color,firstPoint,e->Location,Style); break; // MessageBox::Show("Hellow"); } Invalidate(); } } |
мы добавили в конструктор Ellipse переменную Style, но теперь нам нужно и изменить конструктор элемента Ellipse, вот его измененный код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Ellipse(Color color,Point start,Point end,ElementStyle aStyle) { pen=gcnew Pen(color); //создаем переменную со стилем pen->DashPattern=GetStyle(aStyle);//возвращает стиль this->color=color; position=start;//позиция height=Math::Abs(end.Y-start.Y); width=Math::Abs(end.X-start.X); //описывающий четырехугольник boundRect=System::Drawing::Rectangle(position,Size(width,height)); } |
GetStyle — возвращает переменную типа array<float>^ которой мы модифицируем наше перо и с помощью модифицированного пера мы уже рисуем нашу фигуру. Этот код добавляется в класс Element — который является базовым для Ellipse и остальных классов фигур. Вот код этой функции
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
array<float>^ GetStyle(ElementStyle s) { switch(s) { case ElementStyle::SOLID: { array<float>^ b={10.0f}; return b; break; } case ElementStyle::DASHER: { array<float>^ b={10.0f,10.0f}; return b; break; } case ElementStyle::DOTTER: { array<float>^ b={1.0f,3.0f}; return b; break; } default: exit(1); } } |
Да и это пожалуй вся модификация, компилируем проект и рисуем эллипс разными линиями
Остальные классы фигур так же само модифицируются, я приводить как это делать не буду, делаем по аналогии с классом Ellipse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
//Elements.h //Определяет типы элементов #pragma once using namespace CLRSketcher33; #include <cliext/vector> using cliext::vector; using namespace System; using namespace System::Drawing; namespace CLRSketcher33 { public ref class Element abstract { protected: Point position; Color color; System::Drawing::Rectangle boundRect; Pen^ pen;//указатель на перо public: virtual void Draw(Graphics^ g) abstract; array<float>^ GetStyle(ElementStyle s) { switch(s) { case ElementStyle::SOLID: { array<float>^ b={10.0f}; return b; break; } case ElementStyle::DASHER: { array<float>^ b={10.0f,10.0f}; return b; break; } case ElementStyle::DOTTER: { array<float>^ b={1.0f,3.0f}; return b; break; } default: exit(1); } } }; public ref class Line : Element { protected: Point end; public: //конструктор Line(Color color, Point start, Point end,ElementStyle aStyle) { pen=gcnew Pen(color); pen->DashPattern=GetStyle(aStyle); this->color=color; position = start; this->end=end; boundRect=System::Drawing::Rectangle( Math::Min(position.X,end.X), Math::Min(position.Y,end.Y), Math::Abs(position.X-end.Y), Math::Abs(position.Y-end.Y)); //Для горизонтальных и вертикальных линий if(boundRect.Width<2) boundRect.Width=2; if(boundRect.Height<2) boundRect.Height=2; } //функция рисования линии virtual void Draw(Graphics^ g) override { //Код для рисования самой линии g->DrawLine(pen,position,end); } }; public ref class Circle : Element { protected: int width; int height; public: Circle(Color color,Point start,Point end,ElementStyle aStyle) { pen=gcnew Pen(color); pen->DashPattern=GetStyle(aStyle); this->color=color; int radius=safe_cast<int>(Math::Sqrt( (start.X-end.X)*(start.X-end.X)+ (start.Y-end.Y)*(start.Y-end.Y))); width=height=2*radius; position.X=start.X-radius; position.Y=start.Y-radius; //описывающий четырехугольник boundRect=System::Drawing::Rectangle(position,Size(width,height)); } virtual void Draw(Graphics^ g) override { g->DrawEllipse(pen,position.X,position.Y,width,height); } }; public ref class Rectangle : Element { protected: int width; int height; public: Rectangle(Color color, Point p1, Point p2,ElementStyle aStyle) { pen=gcnew Pen(color); pen->DashPattern=GetStyle(aStyle); this->color=color; position=Point(Math::Min(p1.X,p2.X),Math::Min(p1.Y,p2.Y)); width=Math::Abs(p1.X-p2.X); height=Math::Abs(p1.Y-p2.Y); boundRect=System::Drawing::Rectangle(position,Size(width,height)); } virtual void Draw(Graphics^ g) override { g->DrawRectangle(pen,position.X,position.Y,width,height); } }; public ref class Curve : Element { private: vector<Point>^ points; public: Curve(Color color, Point p1, Point p2, ElementStyle aStyle) { pen=gcnew Pen(color); pen->DashPattern=GetStyle(aStyle); this->color=color; points=gcnew vector<Point>(); position=p1; points->push_back(p2-Size(position)); //Найдите минимальные и максимальные значения координат int minX=p1.X<p2.X?p1.X:p2.X; int minY=p1.Y<p2.Y?p1.Y:p2.Y; int maxX=p1.X>p2.X?p1.X:p2.X; int maxY=p1.Y>p2.Y?p1.Y:p2.Y; int width=Math::Max(2,maxX-minX); int height=Math::Max(2,maxY-minY); boundRect=System::Drawing::Rectangle(minX,minY,width,height); } //Добавьте точку кривой void Add(Point p) { points->push_back(p-Size(position)); //Изменение ограничивающего прямоугольника, так чтобы он вмещал //новую точку if(p.X<boundRect.X) { boundRect.Width=boundRect.Right-p.X; boundRect.X=p.X; } else if(p.X>boundRect.Right) boundRect.Width=p.X-boundRect.Left; if(p.Y<boundRect.Y) { boundRect.Height=boundRect.Bottom-p.Y; boundRect.Y=p.Y; } else if(p.Y>boundRect.Bottom) boundRect.Height=p.Y-boundRect.Top; } virtual void Draw(Graphics^ g) override { Point previous(position); Point temp; for each(Point p in points) { temp=position+Size(p); g->DrawLine(pen,previous,temp); previous=temp; } } }; public ref class Ellipse : Element { protected: int width; int height; public: Ellipse(Color color,Point start,Point end,ElementStyle aStyle) { pen=gcnew Pen(color); pen->DashPattern=GetStyle(aStyle); this->color=color; position=start;//позиция height=Math::Abs(end.Y-start.Y); width=Math::Abs(end.X-start.X); //описывающий четырехугольник boundRect=System::Drawing::Rectangle(position,Size(width,height)); } virtual void Draw(Graphics^ g) override { g->DrawEllipse(pen,position.X,position.Y,width,height); } }; }; |
обработчик события перемещения мышки модифицированный »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private: System::Void Form1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { if(drawing) { switch(elementType) { case ElementType::LINE: tempElement=gcnew Line(color,firstPoint,e->Location,Style); break; case ElementType::RECTANGLE: tempElement=gcnew Rectangle(color,firstPoint,e->Location,Style); break; case ElementType::CIRCLE: tempElement=gcnew Circle(color,firstPoint,e->Location,Style); break; case ElementType::CURVE: if(tempElement==nullptr) tempElement=gcnew Curve(color,firstPoint,e->Location,Style); else safe_cast<Curve^>(tempElement)->Add(e->Location); break; case ElementType::ELLIPSE: tempElement=gcnew Ellipse(color,firstPoint,e->Location,Style); break; // MessageBox::Show("Hellow"); } Invalidate(); } } |
Ну и скрин
На этом все, 8 упражнений мы разобрали, в принципе ничего сложного в них нет 🙂 !!!
[youtube]https://www.youtube.com/watch?v=eK5t6x_rqWA[/youtube]
Сделал все как описано, но почему то не видится класс Line, Rectangle, и Circle выдает компилятор следующее:
1> CLRSketcher.cpp
1>\clrsketcher\Form1.h(742): error C2061: синтаксическая ошибка: идентификатор «Line»
1>\clrsketcher\Form1.h(745): error C2061: синтаксическая ошибка: идентификатор «Rectangle»
1>\clrsketcher\clrsketcher\Form1.h(748): error C2061: синтаксическая ошибка: идентификатор «Circle»
Хотя в окне класса элементы Line Circle и Rectangle отображаются.
Разобрался
Элемент на форме называется Line и класс называется Line из за этого ошибка.
Переименовал и все заработало!!!
Good