Автозаполнение.
Автор: Paul DiLascia
Скачать исходник к статье - 21 Кб (Компилятор:
Visual C++ 6.0)
Каждый раз, набирая что-нибудь в строке
"Адрес" интернет эксплорера, выпадает
список со ссылками, удовлетворяющими тому, что Вы
уже набрали в адресной строке. При этом наиболее
удовлетворяющая ссылка уже выделена и на ней
достаточно нажать Enter:
Делается это при помощи интерфейса IAutoComplete.
IAutoComplete связан с IEnumString - универсальным
интерфайсом для получения списка строк. Дасточно
передать в объект IAutoComplete указатель на Ваш
получатель строк и дескриптор окошка
редактирования или выпадающего списка и он
сделает всё остальное самостоятельно. IAutoComplete2
служит для того, чтобы задать какие-либо
специфические опции.
Однако не всё так просто. IAutoComplete присутствует
только в Windows 2000. COM-объект, который содержит в
себе IAutoComplete (CLSID_IAutoComplete), живёт только в пятой
версии shell32.dll, которая поставляется с Windows 2000, но
никак не с Windows 95, Windows 98, или Windows NT 4.0. Поэтому,
чтобы автозаполнение в Вашем приложение
работало на всех операционках, прийдётся
отказаться от данной технологии.
Итак, давайте сперва посмотрим на класс CAutoComplete
(см. прилагаемый к статье исходник), который был
создан мною специально для этой цели. Никаких COM
или shell32.dll, а всего лишь класс и файл .cpp, которые
можно добавить в своё приложение или DLL и при том
работающих на любой версии Windows:
CAutoComplete не содержит в себе всех возможностей
IAutoComplete. Например, IAutoComplete имеет строку
форматирования, которая срабатывает при нажатии
пользователем Ctrl+Enter. Строка форматирования, это
строка sprintf, которую Windows использует для
трансляции пользовательского ввода. Например,
если строка форматирования равна
"http://www.%s.com" и пользователь ввёл "woowoo",
то IAutoComplete подставит http://www.woowoo.com. В основном
такая возможность ориентирована на Internet Explorer,
поэтому я не стал усложнать класс CAutoComplete.
Для того, чтобы продемонстрировать работу
класса, я сделал небольшое тестовое приложение
ACTest (исходник которого тоже содержится в
прилагаемом архиве). ACTest это диалоговое
приложение, в главном диалоге которого
присутствует окошко редактирования, выпадающий
список (combobox) и два экземпляра CAutoComplete:
class CMyDialog : public CDialog {
protected:
CAutoComplete m_acEdit; // для окошка редактирования
CAutoComplete m_acCombo; // для выпадающего списка (combobox)
•••
};
Для использования CAutoComplete, необходимо
инициализировать каждый экземпляр с указателем
на окно (окошко редактирования или список), а
затем добавить несколько строк. CMyDialog делает это
в OnInitDialog:
// в CMyDialog::OnInitDialog
m_acCombo.Init(GetDlgItem(IDC_COMBO1));
m_acEdit.Init(GetDlgItem(IDC_EDIT1));
static LPCTSTR STRINGS[] = {
"alpha",
"alphabet",
•••
NULL
};
for (int i=0; STRINGS[i]; i++) {
m_acCombo.GetStringList().Add(STRINGS[i]);
m_acEdit.GetStringList().Add(STRINGS[i]);
}
С этого момента ACTest больше не использует окошко
редактирования или список для чего-нибудь ещё
(так как это всего лишь демонстрационное
приложение). Для получения элементов управления
диалога я использую CWnd::GetDlgItem. В реальном
приложении, вероятнее всего Вы воспользуетесь
членами класса m_wndEdit и m_wndCombo для передачи их
адресов после сабклассинга SubclassDlgItem.
Как работает класс CAutoComplete ? Основная идея
заключается в том, что CAutoComplete наследуется от
CSubclassWnd, который позволяет различным объектам
перехватывать сообщения посылаемые окну. CSubclassWnd
работает по стандартному принципу сабклассинга
окон, инсталируя оконную процедуру (WindowProc). Когда
приложение вызывает CAutoComplete::Init, то CAutoComplete в свою
очередь вызывает CSubclassWnd::HookWindow, которая
сабкласит окно. CSubclassWnd::HookWindow "присоединяет"
(используя механизм наподобие MFC) объект CSubclassWnd к
окну, чтобы сообщения, адресованные окну, сперва
попадали в виртуальную функцию CSubclassWnd::WindowProc. В
CAutoComplete эта функция переопределена для обработки
интересующих нас сообщений:
// Переопределяем CSubclassWnd::WindowProc
LRESULT CAutoComplete::WindowProc(...)
{
if (/* EN_CHANGE or CBN_EDITCHANGE */))) {
// try to complete
}
return CSubclassWnd::WindowProc(...);
}
Обратите внимание, что CAutoComplete это объект не
наследованный от CWnd. Он наследован от CSubclassWnd,
который в свою очередь наследуется от CObject. После
обработки сообщения (или не обработки), CAutoComplete
вызывает CSubclassWnd::WindowProc, которая передает
сообщение по его законному пути через
изначальную оконную процедуру различным картам
сообщений с обработчиками, ожидающими, чтобы
обработать это сообщение.
Дальше всё очень просто. В виртуальной функции
OnComplete производится сравнение того, что ввёл
пользователь с внутренним списком и, если по
необходимости корректируется вводимая
информация. При этом для комбобокса показывается
выпадающий список.
Так же присутствуют некоторые тонкости.
Например, когда CAutoComplete перехватывает EN_CHANGE (изменение
окошка редактирования) или CBN_ EDITCHANGE, то перед тем
как вызвать SetWindowText для помещения в окошко
редактирования нового текста, класс должен сам
отключиться. Иначе, SetWindowText сгенерирует другое
уведомление CHANGE и элемент управления погрязнет в
бесконечном цикле самосгенерированных EN_ или
CBN_CHANGE сообщений.
Тонкость номер два. Предположим, что
пользователь вводит "al", которое CAutoComplete
дополняет до "alpha", подсвечивая при этом
"pha". Теперь пользователь нажимает Backspace,
чтобы удалить "pha". Нам естевственно не
хотелось бы возвращаться назад и по-новой
сравнивать с alpha. Бедный пользователь так и не
поймёт, почему Backspace не работает. Решение
заключается в том, чтобы игнорировать сравнение,
если пользователький ввод сокращается, а не
увеличивается. Для этого я добавил виртуальную
функцию IgnoreCompletion. CAutoComplete производит
автодобавление только если эта функция
возвращает FALSE. На мой взгляд алгоритм этой
функции немного некорректный, поэтому я зделал
эту функцию виртуальной, чтобы Вы могли спосойно
её переопределять.
И в заключении хотелось бы привести сравнение
CAutoComplete с IAutoComplete. Цель приведённой таблицы не в
том, чтобы сравнить что лучше, а в том, чтобы
выбрать что нужнее:
CAutoComplete
|
IAutoComplete
|
Собственный
механим используя C++/MFC |
COM
объект в shell32.dll |
Работает
на всех платформах Win32 |
только
Windows 2000 |
Довольно
просто можно изменить определённые возможности |
Ничего
уже не изменишь |
Не
имеет строки формата |
Ctrl +
Enter строка формата типа www.%s.com |
Не
поддерживает реестр
|
По
умолчанию строки хранит в реестре |
Нет
необходимости в получателе строк (IEnumString) |
Необходимо
использовать IEnumString |
Вы
имеете полный контроль |
Вы
ограничены тем, что поддерживается |
|