Обработка событий от компонентов

Элементы управления ActiveX могут быть источниками событий или уведомлений клиенту о возникновении ситуации, на которую клиенту следует обратить внимание. На самом деле события от клиента реализованы при помощи интерфейсов точек взаимодействия. Однако такие события используют передаточный интерфейс, наследованный от IDispatch. Это позволяет использовать их клиентам, в которых реализована автоматизация, например сценариям Web-страниц.

Для подключения механизма событий можно использовать такую же процедуру, как при подключении точек взаимодействия IConnectionPointContainer. Но поскольку интерфейс событий является передаточным, можно пойти более простым путем. Название "передаточный интерфейс" возникло из-за того, что интерфейс содержит механизм передачи вызовов методов соответствующим обработчикам во время выполнения без необходимости явной реализации всех методов интерфейса. Это основной метод обработки событий, и класс MFC CCmdTarget, от которого наследован CWnd, реализует интерфейс IDispatch и может передавать вызовы функциям-обработчикам, определенным в карте событий.

Чтобы использовать эту возможность предков в классе CWnd, нужно сделать 3 вещи. Сначала нужно указать, что класс, унаследованный от CWnd (в данном случае - CDemoClientView), имеет карту событий, добавив в файле заголовков в определение класса макрос DECLARE_EVENTSINK_MAP(). Далее, нужно добавить в файл реализации (.cpp) код улавливания событий (будет уместно расположить его сразу после макроса карты сообщений, сгенерированного мастером создания приложения):

BEGIN_EVENTSINK_MAP(CDemoClientView, CView)

ON_EVENT(CDemoClientView, IDC_CONTROLLER, 1, OnSpeedChanged, VTS_NONE)

ON_EVENT(CDemoClientView, IDC_CONTROLLER, 2, OnColorChanged, VTS_NONE)

ON_EVENT(CDemoClientView, IDC_CONTROLLER, 3, OnStopStart, VTS_BOOL)

END_EVENTSINK_MAP()

Каждый макрос ON_EVENT() определяет передаточный метод, указывая его идентификатор, который был использован в вызове CreateControl(), его DISPID, название функции-обработчика события и все параметры, передаваемые событием, если таковые имеются. Первых два события не передают параметров, на что указывает константа VTS_NONE. Третье передает значение типа Boolean, указывающее, что имеется ввиду: запуск или остановка.

Последний шаг - добавить код обработчиков, которые были определены. Для создания функций используется всплывающее меню в ClassView или ClassWizard и добавляются функции, приведенные в листинге 1. Как можно видеть, все вызовы методов классов с интеллектуальными указателями заключены в блоки try... catch. Код функций достаточно понятен. Глобальная функция dump_com_error() определена для вывода сообщения об ошибке на основании значения HRESULT.

Класс CDemoClientView является посредником между компонентом управления и компонентами модели и представления. Когда компонент управления уведомляет класс CDemoClientView об одном из трех определенных событий, вызывается соответствующая функция. В зависимости от события, класс может получить от компонента управления значение параметра и вызвать метод либо изменить свойство компонентов модели и представления.

Очистка

Перед тем, как завершить программу, нужно убедиться, что все компоненты были правильно освобождены. Классы-оболочки делают большинство необходимых операций самостоятельно. Когда интеллектуальный указатель-член класса выходит за пределы допустимого диапазона, он освобождает ссылки на интерфейсы, которые он содержал. Когда счетчик ссылок на интерфейс обнуляется, COM узнает, что может уничтожить компонент, когда это будет удобно.

Единственное, что нужно сделать самостоятельно - освободить интерфейсы точек взаимодействия, которые также следует создать самостоятельно. Для этого нужно, чтобы компонент модели освободил свою ссылку на интерфейс компонента представления при помощи метода Unadvise() интерфейса IConnectionPoint. Именно для этого использовался интеллектуальный указатель на этот интерфейс и сохранялось значение ключа, возвращенное методом Advise().

Лучше всего выполнить эти операции тогда, когда окно посылает сообщение WM_DESTROY. Для создания обработчика этого события выберается класс CDemoCleintView в окне ClassView и выбераются из всплывающего меню операции Add Windows Message Handler (см. рис. 1.3). Из предложенного списка выберается сообщение WM_DESTROY и нажмаются кнопки Add и Edit. В теле функции OnDestroy() добавляется следующий код перед вызовом CView::OnDestroy() базового класса (а не после него, где мастер приложения расположил комментарий TODO):

try

{

m_pCP->Unadvise(m_dwCookie);

}

catch(_com_error &e)

{

dump_com_error( e );

}

CView::OnDestroy();

Этот код передает значение ключа, идентифицирующего интерфейс взаимодействия с компонентом, и освобождает этот интерфейс.

Обработка событий от компонентов - student2.ru

Рис. 1.3. Новые сообщения и события окна

Теперь можно откомпилировать и запустить приложение, если уже созданы и зарегистрированы сами компоненты, изменять параметры компонента управления и наблюдать за изменениями в компоненте представления (см. листинг 1.1.).

// Листинг 1.1. Эти функции-члены обрабатывают события от компонента

// DemoController, когда пользователь использует элементы управления

// в определении класса CDemoClientView

class CDemoClientView : public CView

{

BOOL OnStopStart(VARIANT_BOOL start);

BOOL OnColorChanged();

BOOL OnSpeedChanged();

}

// в файле реализации CDEmoClientView

BOOL CDemoClientView::OnSpeedChanged()

{

try

{

short speed = m_pController->speed;

m_pModel->ChangeSpeed(speed);

}

catch (_com_error &e)

{

dump_com_error( e );

}

return TRUE;

}

BOOL CDemoClientView::OnColorChanged()

{

try

{

COLORREF color = (COLORREF)m_pController->bkcolor;

m_pView->bkgndColor = (long) color;

}

catch (_com_error &e)

{

dump_com_error( e );

}

return TRUE;

}

BOOL CDemoClientView::OnStopStart(VARIANT_BOOL start)

{

try

{

if (start)

m_pModel->Spin();

else

m_pModel->Stop();

}

catch (_com_error &e)

{

dump_com_error( e );

}

return TRUE;

}

Шаблонный код

В приложении 1 приведен полный шаблонный код DemoComponents и DemoClient.

1.8 Использование среды Visual C++

В качестве примера использования среды Visual C++ возможно создать два компонента – один для просмотра, второй для управления. Возможно начать с компонента управления DemoController2. Обращение к меню в среде Visual C++ следующее: New->Projects->ATLCOM APPWIZARD. Здесь включается поддержка MFC и dll-файлов, далее можно ничего не менять. После того как мастер создаст заготовку для компонента, выбирается Insert->New ATL Object->Controls->Composite Control. Компонент управления именуется, например, DemoContoller. После этого включается поддержка connection points.

В результате мастер создаст интерфейс _IdemoControllerEvents (см. рис.1.4).

Обработка событий от компонентов - student2.ru

Рис. 1.4 Создание мастером интерфейса _IdemoControllerEvents

Для работы с контроллером в этот интерфейс добавляются методы, например:

· ShowMessage – реализует старт;

· Stop – реализует стоп;

· Spd(long speed) – управляет скоростью.

Обработка событий от компонентов - student2.ru

Рис. 1.5 Добавление методов в интерфейс

На вкладке ресурсов конструируется вид компонента контроля и выполняется компиляция.

Обработка событий от компонентов - student2.ru

Рис. 1.6 Конструирование внешнего вида компонента контроля

Для класса CdemoController выбирается Implement Connection Point и отмечается интерфейс.

Обработка событий от компонентов - student2.ru

Рис. 1.7 Выбор Implement Connection Point

Мастер создает класс CProxy_IDemoControllerEvents, в котором будут реализованы обработчики методов, повторяющих названия методов интерфейса _IDemoControllerEvents с префиксом Fire_.

Обработка событий от компонентов - student2.ru

Рис. 1.8 Отметка интерфейса

Теперь можно добавить некоторый код обработчиков событий от нажатия кнопок и скроллинга:

// Листинг 1.2. Добавление кода обработчиков событий от нажатия кнопок и скроллинга

public:

long oldspeed;

long speed;

OLE_COLOR m_clrBackColor;

OLE_COLOR m_clrBorderColor;

LONG m_nBorderStyle;

LONG m_nBorderWidth;

enum { IDD = IDD_DEMOCONTROLLER };

LRESULT OnClickedButton1(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

{

// TODO : Add Code for control notification handler.

//MessageBox("44444");

Fire_ShowMesg();

return 0;

}

LRESULT OnClickedButton2(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

{

// TODO : Add Code for control notification handler.

Fire_stop();

return 0;

}

LRESULT OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

// TODO : Add Code for message handler. Call DefWindowProc if necessary.

Fire_spd(LOWORD(wParam));

switch(LOWORD(wParam))

{

case SB_RIGHT:

{

speed = 100;

}

break;

case SB_LEFT:

{

speed = 1;

}

break;

case SB_LINELEFT:

{

if (speed > 1) --speed;

}

break;

case SB_LINERIGHT:

{

if (speed < 100) ++speed;

}

break;

case SB_PAGELEFT:

{

if (speed > 20) speed-=20;

}

break;

case SB_PAGERIGHT:

{

if (speed <= 80) speed+=20;

}

break;

case SB_THUMBTRACK:

case SB_THUMBPOSITION: speed = HIWORD(wParam);

default:;

}

Fire_spd(speed);

return 0;

}

}

После компиляции компонент готов к работе. Библиотека размещается в папке Debug текущего каталога.

После этого возможно перейти к компоненту просмотра DemoView. Ход создания компонента, описанный ранее, повторяется вплоть до добавления методов в интерфейс. При добавлении нового ATL класс будет называться CDDemoView. Далее в ресурсах формируется интерфейс компонента.

Обработка событий от компонентов - student2.ru

Рис. 1.9 Формирование интерфейса компонента

В форму, созданную мастером, может быть помещен компонент DirectAnimation. Для работы с этим компонентом следует импортировать библиотеку.

#import "c:\windows\system\danim.dll" no_namespace // Путь указан для Windows 98

В библиотеке, в частности, описаны интерфейсы, с которыми впоследствии будет выполняться работа при объявлении переменных класса.

long m_speed,m_oldspeed;

IDAViewerControlPtr m_viewer;

IDAStaticsPtr m_statics;

IDAImagePtr m_image,m_imageinit,m_imagenew;

IDATransform2Ptr m_transinit,m_transnew;

Далее добавляются те или иные методы в интерфейс IDDemoView, например, функции prep(), strt(), stp(), spd(long speed).

В классе CDDemoView в интерфейсе IDDemoView вместо шаблонов добавляется реализация этих методов.

// Листинг 1.3. Реализация методов в классе CDDemoView в интерфейсе IDDemoView

STDMETHODIMP CDDemoView::strt()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

// TODO: Add your implementation code here

m_speed = m_oldspeed;

m_transnew = m_statics->Rotate2Rate(m_speed);

m_imagenew = m_image->Transform(m_transnew);

m_imageinit->SwitchTo(m_imagenew);

return S_OK;

}

STDMETHODIMP CDDemoView::stp()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

// TODO: Add your implementation code here

m_oldspeed = m_speed;

m_speed = 0;

m_transnew = m_statics->Rotate2Rate(m_speed);

m_imagenew = m_image->Transform(m_transnew);

m_imageinit->SwitchTo(m_imagenew);

return S_OK;

}

STDMETHODIMP CDDemoView::prep()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

// TODO: Add your implementation code here

CAxWindow(GetDlgItem(IDC_DA)).QueryControl(&m_viewer);

m_statics = m_viewer->GetMeterLibrary();

m_image = m_statics->ImportImage("c:\\image.gif");

m_transinit = m_statics->Rotate2Rate(1);

m_imageinit = m_statics->ModifiableBehavior(m_image->Transform(m_transinit));

m_viewer->PutImage(m_imageinit);

// m_viewer->PutSound(m_statics->Silence);

m_viewer->Start();

return S_OK;

}

STDMETHODIMP CDDemoView::spd(long speed)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

// TODO: Add your implementation code here

m_speed = speed;

m_transnew = m_statics->Rotate2Rate(speed);

m_imagenew = m_image->Transform(m_transnew);

m_imageinit->SwitchTo(m_imagenew);

return S_OK;

}

После построения компонент готов к использованию.

После этого целесообразно перейти к приложению-серверу, которое будет использовать эти компоненты. Для начала импортируются библиотеки, например, с помощью кода:

#import "C:\Documents and Settings\UserXX\Мои документы\Com\DemoController2\DemoController2.tlb" no_namespace

#import "C:\Documents and Settings\UserXX\Мои документы\Com\DemoView\DemoView.tlb" no_namespace

В секции объявлений для класса CDemoAppView может быть записано:

CDemoAppDoc* GetDocument();

CWnd m_viewWnd;

CWnd m_controllerWnd;

IDDemoViewPtr m_pView;

IDemoControllerPtr m_pController;

IConnectionPointPtr m_pCP;

DWORD m_dwCookie;

BOOL Fire_ShowMesg();

BOOL Fire_stop();

К этому же классу также может быть добавлена виртуальная функция OnInitialUpdate.

CView::OnInitialUpdate();

// TODO: Add your specialized code here and/or call the base class

m_viewWnd.CreateControl(__uuidof(DDemoView),"View",WS_VISIBLE,CRect(0,0,600,200),this,101);

m_controllerWnd.CreateControl(__uuidof(DemoController),"Controller",WS_VISIBLE,CRect(0,201,600,400),this,102);

m_pModel.CreateInstance(__uuidof(DemoModel2));

m_viewWnd.GetControlUnknown()->QueryInterface(&m_pView);

m_controllerWnd.GetControlUnknown()->QueryInterface(&m_pController);

IConnectionPointContainerPtr pCPC = m_pModel;

IUnknownPtr pUnk = m_pView;

if(pCPC != NULL)

{

pCPC->FindConnectionPoint(__uuidof(IDemoModelConnection),&m_pCP);

if(m_pCP != NULL)

{

m_pCP->Advise(pUnk,&m_dwCookie);

}

}

_pView->prep();

В файл DemoAppView.cpp кроме того добавляется карта событий

BEGIN_EVENTSINK_MAP(CDemoAppView, CView)

ON_EVENT(CDemoAppView, IDC_CONTROLLER, 2, Fire_stop, VTS_NONE)

ON_EVENT(CDemoAppView,102, 1,Fire_ShowMesg, VTS_NONE)

ON_EVENT(CDemoAppView, IDC_CONTROLLER, 3, Fire_spd, VTS_I4)

END_EVENTSINK_MAP()

Наконец, описывается реализация методов, которые целесообразно предварительно добавить во все тот же класс с именами методов класса CProxy_IDemoControllerEvents.

BOOL CDemoAppView::Fire_stop()

{

m_pView->stp();

return TRUE;

}

BOOL CDemoAppView::Fire_spd(long speed)

{

m_pView->spd(speed);

return TRUE;

}

BOOL CDemoAppView::Fire_ShowMesg()

{

m_pView->strt();

return TRUE;

}

Ниже приведен вид возможного варианта откомпилированного приложения.

Обработка событий от компонентов - student2.ru

Рис. 1.10 Результат компиляции приложения

Наши рекомендации