Прога лабы 5 семестр
.pdf
31
return Error(_T("Число сторон должно быть между 3 и 100"));
}
Теперь необходимо инициализировать переменную m_nSides. Добавьте следующую строчку в конструктор в файле PolyCtl.h:
CPolyCtl()
{
m_nSides = 3;
}
Это означает, что многоугольник будет изначально треугольником.
Изменение кода отвечающего за отрисовку элемента
В данном коде мы будем использовать функции синус и косинус для расчёта точек многоугольника, поэтому необходимо добавить следующие строчки в файл PolyCtl.h :
#include <math.h>
#include "resource.h" // основные символы
Для хранения точек многоугольника также необходимы переменные. Для этого объявим массив переменных типа POINT в файле PolyCtl.h:
OLE_COLOR m_clrFillColor; short m_nSides;
POINT m_arrPoint[100];
Изменим тело функции 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);
// Создаём и выбираем в контекст устройства кисть, которой
32
// будем заливать многоугольник hBrush = CreateSolidBrush(colFore); SelectObject(hdc, hBrush);
CalcPoints(rc);
Polygon(hdc, &m_arrPoint[0], m_nSides);
//Возвращаем в контекст устройства ранее сохранённые
//перо и кисть (hOldPen и hOldBrush)
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hBrush);
return S_OK;
}
Сейчас необходимо добавить функцию CalcPoints, которая будет рассчитывать координаты вершин многоугольника. Добавим в файл PolyCtl.h строчку (в public-секцию класса IPolyCtl), после чего public-секция должна выглядеть следующим образом:
// IPolyCtl public:
STDMETHOD(get_Sides)(/*[out, retval]*/ short *newVal); STDMETHOD(put_Sides)(/*[in]*/ short newVal);
void CalcPoints(const RECT& rc);
Добавим реализацию этой функции в конец файла 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; // Start at the top
double dblDiff = 2 * pi / m_nSides; // Angle each side will make ptCenter.x = (rc.left + rc.right) / 2;
ptCenter.y = (rc.top + rc.bottom) / 2;
// Calculate the points for each side 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:
CPolyCtl()
{
m_nSides = 3;
m_clrFillColor = RGB(0, 0xFF, 0);
}
33
Это значит, что многоугольник будет изначально залит зелёным цветом.
Теперь пересоберите проект (меню Build и затем пункт Build или F7) и запустите ваш элемент в ActiveX Control Test Container-е. Вы должны увидеть зелёный треугольник, вписанный в окружность. Попытаемся изменить число сторон многоугольника. В тест контейнере выберем пункт меню Control / Invoke Methods. Появится диалог Invoke Methods (см. лаб.1), в котором выберите метод Sides (PropPut) и установите в качестве значение новое количество сторон многоугольника.
Заметьте, что число сторон не изменилось. Этого не произошло из-за того, что вы не заставили элемент перерисоваться. Если вы перекроете элемент другим окном, а затем уберёте это окно, то вы увидите, что элемент перерисовался.
Чтобы разрешить эту проблему необходимо добавить вызов функции FireViewChange() в реализацию функции put_Sides. Эта функция вызывает функцию 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("Число сторон должно быть между 3 и 100"));
}
Скомпилируйте проект заново и протестируйте элемент в тест контейнере. Теперь изменение числа сторон должно происходить сразу после нажатия кнопки Invoke.
Добавление страницы свойств
Страница свойств позволит пользователю изменять свойства элемента управления. В данной лабораторной работе она необходима нам для изменения количества сторон многоугольника.
Страницы свойств реализованы как отдельные COM объекты, что позволяет отделить их при необходимости. Для добавления страницы свойств воспользуемся ATL Object Wizardом. Запустите ATL Object Wizard, выберете Controls в качестве категории. Выберете Property Page в правом окне и нажмите Next. Появится диалог изображённый на рис.26.
34
Рис. 26. Добавление страницы свойств
В окне Short Name введите PolyProp, остальные поля заполнятся автоматически. Поле интерфейс выделено серым фоном, так как страницам свойств не нужен пользовательский интерфейс.
Откройте вкладку Strings для ввода заголовка страницы свойств (рис.27).
Рис. 27. Ввод заголовка страницы свойств
Теперь отредактируем содержание нашей страницы свойств. Для этого откроем соответствующий диалог на вкладке ресурсов проекта (рис.28).
35
Добавим статический текст и окно редактирования (с идентификатором IDC_SIDES) как показано на рисунке 28.
36
Рис. 28. Редактирование страницы свойств
Добавим следующую строчку в начало файла PolyProp.h:
#include "Polygon.h" // определение IPolyCtl
Теперь позволим классу CPolyProp установить число сторон нашего многоугольника при нажатии кнопки Apply (Применить). Для этого изменим тело функции Apply в файле
PolyProp.h:
STDMETHOD(Apply)(void)
{
USES_CONVERSION; ATLTRACE(_T("CPolyProp::Apply\n")); for (UINT i = 0; i < m_nObjects; i++)
{
CComQIPtr<IPolyCtl, &IID_IPolyCtl> pPoly(m_ppUnk[i]); short nSides = (short)GetDlgItemInt(IDC_SIDES);
if FAILED(pPoly->put_Sides(nSides))
{
CComPtr<IErrorInfo> pError; CComBSTR strError; GetErrorInfo(0, &pError); pError->GetDescription(&strError);
MessageBox(OLE2T(strError), _T("Error"), MB_ICONEXCLAMATION); return E_FAIL;
}
}
m_bDirty = FALSE; return S_OK;
}
Страница свойств может иметь более, чем одного клиента прикреплённого к ней в один момент времени. Таким образом функция Apply() вызывает функцию put_Sides() для каждого клиента. Мы используем класс CComQIPtr, который вызывает функцию QueryInterface() каждого объекта, для получения интерфейса IPolyCtl из IUnknown (хранимого в m_ppUnk).
37
Вы также должны установить флаг страницы свойств для того, чтобы кнопка Apply (Применить) была доступной. Это происходит когда пользователь меняет значение в окне редактирования Sides. Щёлкните правой кнопкой мыши на классе CPolyProp и затем выберете
Add Windows Message Handler.... (рис.29). Выберете IDC_SIDES из окна объектов, а затем добавьте поддержку сообщения EN_CHANGE.
Рис. 29. Поддержка сообщения EN_CHANGE
Теперь добавим следующий код в файл PolyProp.h для функции OnChangeSides:
LRESULT OnChangeSides(WORD wNotify, WORD wID, HWND hWnd, BOOL& bHandled)
{
SetDirty(TRUE); return 0;
}
Функция OnChangeSides() будет вызываться при изменении значения в окне редактирования (IDC_EDIT1). Функция SetDirty() указывает на то, что страница свойств задействована для изменения количества сторон (dirty - грязная) и теперь кнопка “Apply” должна быть доступной.
Теперь добавим созданную страничку свойств к нашему элементу управления, так как ATL Object Wizard не делает этого автоматически. Откройте файл PolyCtl.h и добавьте следующую строчку в карту свойств:
38
PROP_ENTRY("Sides", 1, CLSID_PolyProp)
Теперь карта свойств выглядит следующим образом:
BEGIN_PROP_MAP(CPolyCtl)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage)
PROP_ENTRY("Sides", 1, CLSID_PolyProp)
//Example entries
//PROP_ENTRY("Property Description", dispid, clsid)
//PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()
Соберите проект и вставьте элемент (PolyCtl class) в ActiveX Control Test Container.
Попробуйте изменить число сторон многоугольника через страничку свойств (рис.30).
Рис. 30. Изменение числа сторон многоугольника через страницу свойств
39
ИНДИВИДУАЛЬНЫЕ ЗАДАНИЯ
1.Измените начальное число сторон многоугольника до 4, затем до 6 и 7.
2.Измените начальный цвет заливки многоугольника на красный.
3.Измените начальный цвет заливки многоугольника на синий.
4.Измените расположение описанного около многоугольника эллипса.
5.Измените цвет заливки для эллипса, описанного около многоугольника.
6.Добавьте ещё одну страничку свойств (с любым содержанием).
7.Сделайте так, чтобы вводимое пользователем число сторон ограничивалось 50-ю.
8.Измените координаты центра описанной вокруг многоугольника окружности.
9.Измените радиус описанной вокруг многоугольника окружности.
КОНТРОЛЬНЫЕ ВОПРОСЫ
1.КакиеосновныеотличияприсозданииActiveX-элементаспомощьюбиблиотекиMFC иATL.
2.Для чего необходима функция CalcPoints()?
3.Где устанавливается начальное число сторон многоугольника?
4.Для чего необходима функция FireViewChange()?
5.Для чего нужна страничка свойств?
6.Что такое контекст устройства и для чего он необходим?
7.Как работает функция SelectObject?
8.Какая пара макросов содержит карту свойств (страничек свойств) нашего объекта?
9.Что означает добавление поддержки сообщения EN_CHANGE?
10.Как создать новую кисть и перо для последующего рисования?
11.В какой функции рисуется наш многоугольник? Добавили ли вы её сами в класс?
12.Для чего необходимо подключение библиотеки math.h?
13.Как добавить стандартное свойство к элементу управления?
14.Перечислите стандартные события для элемента EditBox.
15.Для чего необходима функция OleTranslateColor()?
16.Когда вызывается функция OnDraw()?
17.Какое имя имеет конструктор любого класса и для чего он нужен?
18.Какое имя имеет деструктор любого класса и для чего он нужен?
19.Для чего необходима функция SetDirty()?
20.Что такое карта свойств, какими макросами она ограничена?
40
ЛАБОРАТОРНАЯ РАБОТА №4
СОЗДАНИЕ COM-СЕРВЕРА И СОМ-КЛИЕНТА.
Цель работы: Изучить технологию создания и основные принципы взаимодействия клиент-серверных приложений.
Ход работы:
1.Создание СОМ-сервера.
2.Создание СОМ-клиента.
Общие понятия
Любой СОМ-класс реализован внутри некоего исполняемого модуля, который в СОМ обычно называется сервером. Различают три типа серверов:
внутрипроцессорные (in-process), представляющие собой DLL, подгружаемую процессом клиента. Все объекты, создаваемые таким сервером, находятся внутри процесса клиента, отсюда и название;
локальные (local), являющиеся отдельным исполняемым файлом (EXE), которые находятся на одном компьютере с клиентом. Клиент и объект находятся в разных процессах (адресных пространствах);
удалённые (remote), которые находятся на другом компьютере, нежели клиент. Могут быть как DLL, так и EXE.
Создание COM-сервера
В среде Visual C++ выберете File/New, затем вкладку Projects. В качестве типа проекта выберете MFC AppWizard(dll), в поле имени проекта введите любое имя и нажмите OK. Появится диалог, изображённый на рис.31.
