Шаг 117 - Изменение кода рисования

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

  1. В Test Container щелкните Invoke Methods в меню Control. Появиться диалоговое окно Invoke Method.
  2. Выберите PropPut версию свойства Sides из Method Name.
  3. Напечатайте 5 в окне редактирования Parameter Value, нажмите Set Value и щелкните по Invoke.

117_1.gif (5967 b)

Обратите внимание, что элемент управления не изменяется. Что же не правильно? Хоть мы и изменили число сторон в переменной 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 изменения на экране появляются сразу.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 16.09.2002