Алекс Jenter - Программирование на Visual C++. Архив рассылки
- Название:Программирование на Visual C++. Архив рассылки
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Алекс Jenter - Программирование на Visual C++. Архив рассылки краткое содержание
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.
Программирование на Visual C++. Архив рассылки - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
{ \
if (!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \
return FALSE; \
}
Теперь мы знаем, как устроены карты DDX. Это может помочь нам писать их более эффективно. Например, мы можем написать в карте DDX следующее:
BEGIN_DDX_MAP(CMyDialog)
...
for (int i=0; i<100; i++)
DDX_INT(IDC_BASE+i, m_numbers[i]);
...
END_DDX_MAP()
Это гораздо удобнее, чем вставлять в карту 100 записей.
Если с макросами DDX_INT, DDX_UINTи DDX_FLOATпроблем обычно не возникает, то макрос DDX_TEXTможет стать источником неприятностей. Чтобы с ними разобраться, рассмотрим реализацию функции DDX_Text.
BOOL DDX_Text(UINT nID, LPTSTR lpstrText, int nSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) {
T* pT = static_cast(this);
BOOL bSuccess = TRUE;
if (bSave) {
HWND hWndCtrl = pT->GetDlgItem(nID);
int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, nSize);
if (nRetLen < ::GetWindowTextLength(hWndCtrl)) bSuccess = FALSE;
}
…
return bSuccess;
}
Как видим, размер буфера задаётся параметром nSize. Но рассчитывается этот размер по меньшей мере странно:
#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связывает контрол с объектом класса, порождённого от CWindowImplBaseT<>. Если вы знакомы с mfc, вы знаете, что там обычной практикой является связывание объекта класса CWnd(или его потомка) с контролом, даже если вам не нужно подключать его к карте сообщений, а просто вызвать несколько обёрток типа CWnd::GetWindowTextили CListCtrl::GetItem. Это создаёт значительный, причём совершенно ненужный, перерасход ресурсов. Не используйте макрос DDX_CONTROLиз wtl подобным образом. Он используется, если вам действительно необходимо заменить оконную процедуру контрола и обрабатывать его сообщения через карту сообщений.
Если же вам нужно просто использовать функции-обёртки из класса CWindowдля работы с контролом, достаточно получить хэндл этого контрола с помощью GetDlgItem, а затем присвоить его объекту класса. Удобно делать это в обработчике WM_INITDIALOG. Например:
class CMyDialog : public CDialogImpl, public CWinDataExchange {
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используется для работы сразу с целой группой переключателей. При этом переменная var, связанная с группой, содержит порядковый номер выбранного переключателя в группе (нумерация начинается с нуля). Значение -1 соответствует состоянию группы, в котором ни один из переключателей не выбран.
А что, если нам нужно связать переменную не со всей группой, а с конкретным переключателем из неё? В этом случае нужно просто воспользоваться макросом DDX_CHECKвместо DDX_RADIO.
Вероятно, вы не раз видели диалоги, в которых манипуляции с одним контролом приводят к изменению некоторых других (они включаются/отключается, текст на них меняется и т. д.). В 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. Не имеет параметров. |
После того, как карта UI добавлена в класс, остаётся один завершающий штрих. Вы должны зарегистрировать все контейнеры элементов пользовательского интерфейса, которые нужно обновлять. В случае с контролами в качестве контейнера выступает сам диалог. В случае с меню это окно, содержащее меню. И так далее. Для каждого контейнера существует своя функция регистрации: UIAddMenuBarдля меню, UIAddToolBarдля панелей иснтрументов, UIAddStatusBarдля строк состояния и UIAddChildWindowContainerдля контейнеров дочерних окон. Все перечисленные функции принимают хэндл окна-контейнера и позвращают BOOL, сигнализирующий об успехе или неуспехе регистрации. В случае с диалогом регистрировать контейнер удобно в обработчике сообщения WM_INITDIALOG.
Читать дальшеИнтервал:
Закладка: