В коде рисования надо использовать функции sin и cos, чтобы вычислить точки многоугольника. Для использования этих функций необходимо подключить math.h в PolyCtl.h:
#include <math.h> #include "resource.h" // main symbols
Обратите внимание при создании Release версии проекта на то, что когда ATL COM AppWizard генерирует проект он по умолчанию задает макродиректива _ATL_MIN_CRT. Эта макрокоманда определяется для того, чтобы не использовать библиотеку C Run-Time Library в коде, когда она не нужна. Элемент управления многоугольник нуждается в коде C Run-Time Library, чтобы инициализировать функции работы с плавающей запятой. Следовательно надо удалить макрокоманду _ATL_MIN_CRT, чтобы сформировать Release версию. Для удаления макрокоманды выберите пункт Settings в меню Project. В раскрывающимся списке Settings For: выберите Multiple Configurations. В диалоговом окне Select project configuration(s) to modify, которое появится нажмите переключатели для всех четырех Release версий, затем нажмите OK. На вкладке C/C++ выберите General категорию, затем удалите _ATL_MIN_CRT из окна редактирования определений (definitions) препроцессора.
Как только точки многоугольника будут вычислены надо будет их сохранять. Для этого надо добавить массив типа POINT в конец определения класса PolyCtl.h:
OLE_COLOR m_clrFillColor; short m_nSides; POINT m_arrPoint[100];
Теперь измените функцию OnDraw() в PolyCtl.h, как в коде ниже. Обратите внимание, что Вы удаляете обращения к функциям Rectangle и DrawText, а также явно получаете и выбираете черную и белую кисть. Если окно не Ваше собственное, то Вы не можете получить контекст устройства, в котором вы будете рисовать.
Завершенный OnDraw выглядит следующим образом:
HRESULT CPolyCtl::OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; HDC hdc = di.hdcDraw; COLORREF colFore; HBRUSH hOldBrush, hBrush; HPEN hOldPen, hPen; // преобразуем m_colFore в тип COLORREF OleTranslateColor(m_clrFillColor, NULL, &colFore); // создаем и выбираем цвет для рисования окружности hPen = (HPEN)GetStockObject(BLACK_PEN); hOldPen = (HPEN)SelectObject(hdc, hPen); hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); // создаем кисть для заполнения полигона hBrush = CreateSolidBrush(colFore); SelectObject(hdc, hBrush); CalcPoints(rc); Polygon(hdc, &m_arrPoint[0], m_nSides); // возвращаем старые кисти и удаляем те, которые создали SelectObject(hdc, hOldPen); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); return S_OK; }
Теперь Вы должны добавить функцию с именем CalcPoints, которая вычислит координаты пересечений линий. Эти вычисления будут основаны на переменной RECT, которая передается в функцию. Сначала Вы должны добавить описание CalcPoints в общий раздел для класса IPolyCtl в PolyCtl.h:
void CalcPoints(const RECT& rc);
Общий раздел класса IPolyCtl должен теперь выглядеть следующим образом:
// IPolyCtl public: STDMETHOD(get_Sides)(/*[out, retval]*/ short *newVal); STDMETHOD(put_Sides)(/*[in]*/ short newVal); void CalcPoints(const RECT& rc);
Затем добавьте реализацию функции CalcPoints в конец PolyCtl.cpp файла:
void CPolyCtl::CalcPoints(const RECT& rc) { const double pi = 3.14159265358979; POINT ptCenter; double dblRadiusx = (rc.right - rc.left) / 2; double dblRadiusy = (rc.bottom - rc.top) / 2; double dblAngle = 3 * pi / 2; // начинаем сверху double dblDiff = 2 * pi / m_nSides; // угол на каждую сторону ptCenter.x = (rc.left + rc.right) / 2; ptCenter.y = (rc.top + rc.bottom) / 2; // вычисляем точки для каждой стороны for (int i = 0; i < m_nSides; i++) { m_arrPoint[i].x = (long)(dblRadiusx * cos(dblAngle) + ptCenter.x + 0.5); m_arrPoint[i].y = (long)(dblRadiusy * sin(dblAngle) + ptCenter.y + 0.5); dblAngle += dblDiff; } }
Инициализируйте m_clrFillColor. Выберите зеленый как заданный по умолчанию цвет и добавьте строку ниже в конструктор CPolyCtl внутри файла PolyCtl.h:
m_clrFillColor = RGB(0, 0xFF, 0);
Конструктор теперь должен выглядеть следующим образом:
CPolyCtl() { m_nSides = 3; m_clrFillColor = RGB(0, 0xFF, 0); }
Дальше соберите снова элемент управления. Откройте ActiveX Control Test Container и вставьте элемент управления. Вы увидите зеленый треугольник внутри круга.
Пробуйте изменить число сторон. Чтобы изменять свойство в интерфейсе Test Container используйте Invoke Methods:
Обратите внимание, что элемент управления не изменяется. Что же не правильно? Хоть мы и изменили число сторон в переменной m_nSides, но не заставляли элемент управления повторно перерисовываться. Если Вы переключитесь на другое приложение и затем вернетесь обратно, чтобы проверить контейнер, Вы увидите, что элемент управления повторно окрашен и теперь имеет правильное число сторон. Чтобы исправить эту проблему Вы должны добавить обращение к функции FireViewChange, которая определена в IViewObjectExImpl после того как Вы устанавливаете число сторон. Если элемент управления выполняется в собственном окне, FireViewChange вызовет InvalidateRect API. Если элемент управления выполняется без своего окна, то метод InvalidateRect будет вызван из интерфейса контейнера.
Новый put_Sides метод следующий:
STDMETHODIMP CPolyCtl::put_Sides(short newVal) { if (newVal > 2 && newVal < 101) { m_nSides = newVal; FireViewChange(); return S_OK; } else return Error(_T("Shape must have between 3 and 100 sides")); }
После того, как вы добавили FireViewChange соберите элемент управления снова. На сей раз при изменении числа сторон и нажатии на Invoke изменения на экране появляются сразу.