Чтение онлайн

ЖАНРЫ

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

#define DDX_TEXT(nID, var) \

 if (nCtlID == (UINT)-1 || nCtlID == nID) \

 { \

if (!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \

return FALSE; \

 }

#define DDX_TEXT_LEN(nID, var, len) \

 if (nCtlID == (UINT)-1 || nCtlID == nID) \

 { \

if (!DDX_Text(nID, var, sizeof(var), bsaveandvalidate, true, len)) \

return FALSE; \

 }

Другими словами, за размер буфера принимается

размер переменной var, которая связывается с контролом. Отсюда следует два вывода. Во-первых, переменная var может быть только статическим массивом, а динамическим – нет. Во-вторых, в программе, использующей набор символов Unicode, этот размер будет всегда определяться неправильно. Выход в том и в другом случае – отказаться от макроса DDX_TEXT и обратиться к функции DDX_Text напрямую, передав ей правильный размер. Замечу также, что при передаче строки из переменной в контрол размер буфера значения не имеет, так что если вы передаёте данные только в этом направлении, DDX_TEXT использовать можно.

С набором символов Unicode связана ещё одна интересная проблема. Посмотрим на следующую карту DDX:

LPTSTR m_msg;

BEGIN_DDX_MAP(CMyDialog)

 ...

 DDX_Text(IDC_MESSAGE, m_msg, ...)

 ...

END_DDX_MAP

Если попытаться откомпилировать этот код, задав макрос UNICODE, компилятор выдаст следующую ошибку: 'DDX_Text' : ambiguous call to overloaded function (неоднозначность при обращении к перегруженной функции). Дело в том, что в классе CWinDataExchange<> существует несколько перегруженных версий DDX_Text. Вот две из них:

// Text exchange

BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int nSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) {

 ...

}

BOOL DDX_Text(UINT nID, BSTR& bstrText, int /*nsize*/, bool bsave, bool bvalidate = false, int nlength = 0) {

 ...

}

Если макрос UNICODE определён, LPTSTR превращается в wchar_t*, а BSTR&– в wchar_t*&. Получается неоднозначность. Чтобы решить эту проблему, можно переписать карту DDX так:

BEGIN_DDX_MAP(CMyDialog)

 ...

 DDX_Text(IDC_MESSAGE, (TCHAR * const)m_msg, ...)

 ...

END_DDX_MAP

Поскольку в C++ константный указатель можно передать по значению, но не по ссылке, неоднозначность тем самым удаётся разрешить. В любом случае, если вы собираетесь компилировать программу с поддержкой Unicode, я советую вам использовать для обмена текстом переменные типа CString. Это избавит вас от многих проблем, подобных рассмотренным выше.

Использование DDX_CONTROL

Макрос DDX_CONTROL связывает контрол с объектом класса, порождённого от CWindowImplBaseT<>. Если вы знакомы с mfc, вы знаете, что там обычной практикой является связывание объекта класса CWnd (или его потомка) с контролом, даже если вам не нужно подключать его к карте сообщений, а просто вызвать несколько обёрток типа CWnd::GetWindowText или CListCtrl::GetItem. Это создаёт значительный, причём совершенно ненужный, перерасход ресурсов. Не используйте макрос DDX_CONTROL из wtl подобным образом. Он используется, если вам действительно необходимо заменить оконную процедуру контрола и обрабатывать его сообщения через карту сообщений.

Если же вам нужно просто использовать функции-обёртки из класса CWindow

для работы с контролом, достаточно получить хэндл этого контрола с помощью GetDlgItem, а затем присвоить его объекту класса. Удобно делать это в обработчике WM_INITDIALOG. Например:

class CMyDialog : public CDialogImpl<CMyDialog>, public CWinDataExchange<CMyDialog> {

private:

 CWindow m_control;

 ...

public:

 BEGIN_MSG_MAP_EX(CMyDialog)

MSG_WM_INITDIALOG(OnInitDialog)

...

 END_MSG_MAP

 LRESULT OnInitDialog(HWND, LPARAM) {

m_control = GetDlgItem(IDC_SOME_CONTROL);

...

 }

 ...

};

Ниже в этой статье мы увидим, что кроме CWindow в WTL существует целый набор классов для работы с контролами – CStatic, CButton, CEdit и т. д. Их можно использовать так же, как и CWindow в приведённом выше примере.

Использование DDX_RADIO

Макрос DDX_RADIO используется для работы сразу с целой группой переключателей. При этом переменная var, связанная с группой, содержит порядковый номер выбранного переключателя в группе (нумерация начинается с нуля). Значение -1 соответствует состоянию группы, в котором ни один из переключателей не выбран.

А что, если нам нужно связать переменную не со всей группой, а с конкретным переключателем из неё? В этом случае нужно просто воспользоваться макросом DDX_CHECK вместо DDX_RADIO.

Класс CUpdateUI<>: обновление дочерних окон в стиле WTL

Вероятно, вы не раз видели диалоги, в которых манипуляции с одним контролом приводят к изменению некоторых других (они включаются/отключается, текст на них меняется и т. д.). В WTL, как и в MFC, существует специальный механизм, поддерживающий изменение состояния контролов в диалоге (или в любом другом окне). На самом деле, этот механизм универсален и применяется также для обновления состояния пунктов меню, кнопок на панели инструментов и т. д.

Чтобы подключить механизм обновления дочерних контролов к вашему диалогу, добавьте в список базовых классов класс CUpdateUI<>, который описан в файле atlframe.h. Кроме этого, необходимо написать карту обновления пользовательского интерфейса (далее карта UI). Набор макросов, из которых составляется карта UI, минимален. Их всего 3 штуки. Все они описаны в таблице 2.

Макрос Описание
BEGIN_UPDATE_UI_MAP(thisClass) Начало карты UI. thisClass – имя класса, в котором содержится карта.
UPDATE_ELEMENT(nID, wType) Определяет, какие типы элементов пользовательского интерфейса с идентификатором nID должны обновляться. Нужные типы объединяются с помощью операции "ИЛИ" и передаются в качестве второго параметра макроса wType. WTL распознаёт следующие типы: UPDUI_MENUPOPUP (пункт всплывающего меню), UPDUI_MENUBAR (пункт полоски меню), UPDUI_CHILDWINDOW (дочернее окно, контрол), UPDUI_TOOLBAR (кнопка на панели инструментов) и UPDUI_STATUSBAR (панель на строке состояния). В этой статье мы сосредоточимся на контролах, а об остальных элементах поговорим, когда доберёмся до окон-рамок.
END_UPDATE_UI_MAP Этот макрос завершает карту UI. Не имеет параметров.
Поделиться с друзьями: