В коде рисования надо использовать функции 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 изменения на экране появляются сразу.