Программирование на Visual C++. Архив рассылки
Шрифт:
Обратите внимание, что идентификатор ресурса диалога в классе CSimpleDialog<> задаётся не как константа, а как первый параметр шаблона. Благодаря этому класс можно использовать, не порождая от него собственных классов. Если, к примеру, вы нарисовали в редакторе диалоговое окно About и назначили ему идентификатор IDD_ABOUT, отобразить его можно, используя класс CSimpleDialog<> напрямую:
Ещё раз подчеркну, что класс CSimpleDialog<> не содержит реализации метода Create, а поэтому не позволяет создавать немодальные диалоги. Методы EndDialog и DestroyWindow также отсутствуют.
Механизм динамического обмена данными (DDX – Dynamic Data eXchange)
Прежде чем рассказывать про класс CWinDataExchange<>, скажу несколько слов об общих принципах реализации дополнительной функциональности в WTL.
Обычно дополнительные возможности WTL реализуются в отдельных классах. Чтобы получить доступ к этим возможностям, необходимо произвести свой класс от всех классов WTL, содержащих нужную нам функциональность. Далее каждый из базовых классов конфигурируется с помощью соответствующей карты (map), которая составляется из специально предусмотренных для этой цели макросов. Обычно карта начинается макросом BEGIN_XXX_MAP и заканчивается макросом END_XXX_MAP (XXX обозначает некоторый идентификатор, разъясняющий назначение карты). Между ними располагаются все остальные макросы карты.
Некоторые механизмы WTL, подключённые к нашему классу, требуют также начальной инициализации, которую можно выполнить, например, в обработчике сообщения WM_INITDIALOG.
Настроив нужные нам механизмы WTL, мы можем использовать их, вызывая или переопределяя предусмотренные для этой цели методы.
Вернёмся к механизму DDX. Чтобы использовать его, включите в список базовых классов вашего диалога (или другого окна, содержащего контролы) класс CWinDataExchange<> (описан в файле atlddx.h). В качестве параметра шаблона задаётся имя вашего производного класса. Например:
Следующий шаг – включить в public– секцию вашего класса карту DDX. Каждая строчка в этой карте связывает идентификатор контрола с некоторой переменной в вашей программе. Обычно это переменная-член класса, но она может быть и глобальной/статической. В обмене могут участвовать числовые или текстовые данные с ограничениями или без них. Список макросов, из которых строится карта DDX, приведён в таблице 1.
Макрос | Описание |
---|---|
BEGIN_DDX_MAP(thisClass) | Начало карты DDX. thisClass – имя класса, в котором содержится карта. |
DDX_TEXT(nID, var) | Связывает строковую переменную var с контролом nID (здесь и далее nID – это идентификатор контрола). Переменная var может иметь тип TCHAR*, BSTR, CComBSTR или CString. Обмен данными осуществляется при помощи функций SetWindowText и GetWindowText. Чаще всего макрос используется для статических контролов и полей ввода, хотя может применяться и с другими окнами. |
DDX_TEXT_LEN(nID, var, len) | Аналогичен предыдущему, но длина строки ограничивается значением len. Попытка передать строку, длина которой превышает len, приведёт к ошибке валидации (об ошибках немного позже). |
DDX_INT(nID, var) | Связывает целочисленную переменную var с контролом nID. |
DDX_INT_RANGE(nID, var, min, max) | Аналогичен предыдущему, но передаваемое значение должно лежать в диапазоне от min до max. Невыполнение этого условия приведёт к ошибке валидации. |
DDX_UINT(nID, var) | Связывает целочисленную беззнаковую переменную var с контролом nID. |
DDX_UINT_RANGE(nID, var, min, max) | Аналогичен предыдущему, но передаваемое значение должно лежать в диапазоне от min до max. Невыполнение этого условия приведёт к ошибке валидации. |
DDX_FLOAT(nID, var) | Связывает переменную с плавающей точкой var с контролом nID. var
|
DDX_FLOAT_RANGE(nID, var, min, max) | Аналогичен предыдущему, но передаваемое значение должно лежать в диапазоне от min до max. Невыполнение этого условия приведёт к ошибке валидации. |
DDX_CONTROL(nID, obj) | Связывает объект obj с контролом nID. Для связывания используется метод obj.SubclassWindow, поэтому объект должен принадлежать классу CWindowImplBaseT<> или производному от него. |
DDX_CHECK(nID, var) | Привязывает переменную var типа int к флагу checked кнопки nID. Для обмена данными используются сообщения BBM_SETCHECK и BM_GETCHECK. |
DDX_RADIO(nID, var) | Связывает переменную var типа int с группой переключателей. Контрол nID должен быть первым в группе. |
END_DDX_MAP | Этот макрос завершает карту DDX. Не имеет параметров. |
Рассмотрим пример карты DDX для диалога, который позволяет вводить имя, адрес и номер телефона.
ПРИМЕЧАНИЕ
Если вы использовали MFC, вы, вероятно, заметили сходство карты DDX с виртуальной функцией CWnd::DoDataExchange. Они действительно похожи. Одно отличие состоит в том, что макросы WTL не используют вспомогательную структуру, аналогичную CDataExchange из mfc. Это несколько упрощает их использование. Второе отличие – в WTL собственно обмен и валидация объединены вместе, а в MFC им соответствуют различные функции (DDX_* для обмена данными и DDV_* для валидации).
Вот и всё. Никакой дополнительной инициализации механизм DDX в WTL не требует. Чтобы выполнить обмен данными, используйте функцию DoDataExchange. Вот её прототип:
Параметр bSaveAndValidate задаёт направление обмена (FALSE или DDX_LOAD соответствует записи значений из переменных в контролы, а TRUE или DDX_SAVE – из контролов в переменные). Второй параметр задаёт идентификатор контрола, с которым необходимо произвести обмен. Значение по умолчанию (-1) соответствует всем контролам, упомянутым в карте DDX. Функция DoDataExchange возвращает TRUE, если обмен данными был успешным, или FALSE в противном случае.
ПРИМЕЧАНИЕ
В MFC обмен данными осуществляет функция CWnd::UpdateData, похожая на DoDataExchange из wtl. Отличие в том, что функция UpdateData не позволяет задавать идентификатор контрола. Вместо этого она всегда воздействует на все контролы, прописанные в функции CWnd::DoDataExchange. Реализация в wtl несколько гибче, но было бы ещё лучше, если бы разработчики WTL предусмотрели разбиение карты DDX на подкарты (как это сделано для карт сообщений). Часто в реальной программе требуется выполнить обмен данными не с одним контролом и не со всеми контролами, а с некоторым их подмножеством.
Иногда в процессе обмена данными возникают ошибки. Их делят на две разновидности: ошибки обмена (data exchange errors) и ошибки валидации (data validation errors). Ошибки обмена возникают, когда контрол не содержит значения, соответствующего типу связанной с ним переменной (например, поле ввода, связанное с переменной типа int, содержит пробелы или другие нецифровые символы). Ошибки валидации фиксируются в случае несоответствия передаваемого значения и наложенных на него ограничений (максимальная длина строки, минимальное и максимальное значение числа). В случае возникновения ошибки обмена вызывается виртуальная функция OnDataExchangeError, а при возникновении ошибки валидации – виртуальная функция OnDataValidateError. Дальнейший процесс обмена данными прерывается, а DoDataExchange возвращает FALSE, сигнализируя о неуспехе операции.