Главная » Статьи » Excel » Пользовательский интерфейс

Выпадающий список с контекстным поиском

Я собираюсь вам рассказать о фантастически полезном и эффектном приёме. Это одна из самых интересных вещей, которую мне доводилось видеть в Excel. Кроме того, она достаточно легко реализуется. Если вы разберётесь, как она работает, то вы сразу узнаете об Excel необычно много. Я постараюсь рассказать всё максимально подробно, последовательно и внятно.

Выпадающий список с контекстным поиском

Итак, речь пойдёт о выпадающем списке (так называемый combo box), в который встроена возможность динамического поиска по подстроке, которую пользователь вводит с клавиатуры. Посмотрите пример, в котором мы имеем топ 300 крупнейших городов России. На анимированной иллюстрации видно, как мы динамически сужаем список выбора, вводя подстроку "кр" или "ниж", экономя огромное количество времени. Более того, список меняется после ввода каждого нового символа! Выглядит чрезвычайно привлекательно и профессионально, не так ли? Давайте разбираться, как это устроено.

Файл примера

Скачать

Пошаговая инструкция


Предварительные замечания

В файле примера выпадающий список с поиском реализован сразу в двух вариантах: для обычного диапазона (лист Range) и для умной таблицы (лист Table). Мы будим эти варианты обсуждать одновременно, отмечая их различия.

Шаг 1. Готовим таблицу для списка

Подготовьте таблицу с четырьмя колонками: Город (или то, что вам нужно), Статус, Индекс, Фильтр. Заполните столбец Город значениями. В остальных трёх колонках будут формулы, которые мы обсудим ниже. Я всем рекомендую использовать умную таблицу, так как это значительно проще.

Шаг 2. Формулы для столбца Статус

На примере ячейки F2 рассмотрим формулу, аналогичную для всего столбца Статус (столбец F). Из F2 формулу можно протягивать вниз до конца, а в случае умной таблицы Excel это сделает за вас. Это также относится ко всем формулам, которые мы будем обсуждать в этой статье.

$B$2 - ячейка, с которой будет связан выпадающий список (добавляется на шаге 6). Что значит связано? Всё, что вы введёте в выпадающий список, тут же отразится в ячейке B2.

Формула ПОИСК вернёт ошибку, если содержимое B2 не найдено в $E2. ЕОШИБКА перехватит ошибку и вернёт ИСТИНА, если действительно была ошибка, и - ЛОЖЬ, если строка таки была найдена. Функция НЕ делает из истины ложь и наоборот (инверсирует результат). Таким образом, мы получим в этом столбце ИСТИНА, если подстрока найдена в текущем городе, и наоборот. Обратите внимание, что пустая подстрока содержится в любой строке, поэтому все ячейки столбца Статус имеют значения ИСТИНА, когда мы не ввели ещё ничего в B2.

Обычный диапазон Умная таблица

=НЕ( ЕОШИБКА( ПОИСК( $B$2; $E2) ) )

или

=NOT( ISERROR( SEARCH( $B$2; $E2) ) )

=НЕ( ЕОШИБКА( ПОИСК( $B$2; [@Город]) ) )

или

=NOT( ISERROR( SEARCH( $B$2; [@Город]) ) )

[@Город] - на языке структурных формул умных таблиц это ссылка на ячейку столбца Город в той же строке, в которой находится сама формула. Поскольку ссылка идёт внутри таблицы, то имя самой таблицы в формуле можно не использовать. В остальном всё - тоже самое.

Шаг 3. Формула для столбца Индекс

Если B2 содержит подстроку поиска, то в столбце Статус не все ячейки примут значение ИСТИНА. Статус ИСТИНА будет только там, в чьи названия городов входит соответствующая подстрока. А в столбце Индекс мы рассчитываем номер по порядку для всех строк, которые содержат искомую подстроку. Например, на рисунке ниже B2 содержит "ни", что заставляет столбец Статус быть истинным у строк с городами Нижний Новгород, Калининград, Магнитогорск и т.д., а в столбце Индекс мы начинаем считать факты срабатываний в F: Нижний Новгород - первое срабатывание, Калининград - второе и так далее.

Функция ЕСЛИ отсекает все значения в F, которые не равны ИСТИНА. Функция СЧЁТЕСЛИ подсчитывает количество значений ИСТИНА в F.

Обычный диапазон Умная таблица

=ЕСЛИ( $F2; СЧЁТЕСЛИ( $F$2:$F2; ИСТИНА ); "")

или

=IF( $F2; COUNTIF( $F$2:$F2; TRUE ); "")

=ЕСЛИ( [@Статус]; СЧЁТЕСЛИ( $F$2:[@Статус]; ИСТИНА ); "")

или

=IF( [@Статус]; COUNTIF( $F$2:[@Статус]; TRUE ); "")

Обратите внимание, что диапазон условия в СЧЁТЕСЛИ введен скользящий - вторая координата не закреплена - и во время протягивания она растёт пропорционально таблице. За счёт этого трюка мы получаем механизм подсчёта значения ИСТИНА. Например, 6-я строка будет подсчитывать ИСТИНУ по диапазону $F$2:$F6 (там одно значение - от Нижнего Новгорода), а 41-я строка будет подсчитывать ИСТИНУ уже по диапазону $F$2:$F41 (а там уже 2 значения - от Нижнего Новгорода и от Калининграда). Вот суть механизма. Это полезный приём, который стоит запомнить.

Шаг 4. Формула для столбца Фильтр

Теперь наша задача, опираясь на столбец Индекс, сформировать в столбце H отфильтрованный список городов, который необходимо показывать в выпадающем списке.

Обычный диапазон Умная таблица

=ЕСЛИОШИБКА( ИНДЕКС( стлГород; ПОИСКПОЗ( ЧСТРОК($G$2:$G2); стлИндекс; 0) ); "")

или

=IFERROR( INDEX( стлГород; MATCH( ROWS($G$2:$G2); стлИндекс; 0) ); "" )

Обратите внимание на динамический именованный диапазон стлГород и стлИндекс, которые мы вынуждены создавать для случая диапазона, чтобы придать решению должный уровень универсальности. Техника, по которой созданы эти именованные диапазоны разобрана тут.

=ЕСЛИОШИБКА( ИНДЕКС( [Город]; ПОИСКПОЗ( ЧСТРОК($G$2:[@Индекс]); [Индекс]; 0) ); "")

или

=IFERROR( INDEX( [Город]; MATCH( ROWS($G$2:[@Индекс]); [Индекс]; 0) ); "" )

Не путайте: [Индекс] - ссылка на весь столбец, а [@Индекс] - ссылка на ячейку из этого столбца в текущей строке. Никакие дополнительные именованные диапазоны нам создавать нет никакой необходимости, так как мы пользуемся встроенным в умные таблицы сервисом при ссылке на столбцы.

Формула ЧСТРОК($G$2:$G2) используется для генерации последовательных номеров от 1 (для второй строки) до N (в строке N+1), равному количеству найденных подстрок. Просто генерируется диапазон соответствующего размера, а формула ЧСТРОК возвращает его высоту в строках.

Формула ПОИСКПОЗ ищет номер реальной строки, содержащий соответствующий индекс. Например, в столбце Фильтр мы видим Магнитогорск на третьей позиции, но в реальности он взят из E45, так как в G45 стоит цифра 3, которую мы и нашли через ПОИСКПОЗ. То есть ПОИСКПОЗ сказал нам, что Магнитогорск находится в 45-й строке, а извлекли мы его оттуда уже при помощи формулы ИНДЕКС.

Если же при извлечении возникает ошибка (текущая строка находится ниже строки N+1), то формула возвращает пустую строку. За это отвечает ЕСЛИОШИБКА.

Шаг 5. Создание именованных диапазонов

Обычный диапазон Умная таблица

Именованный диапазон стлГород

=Range!$E$2:ИНДЕКС( Range!$E:$E; СЧЁТЗ(Range!$E:$E) )

или

=Range!$E$2:INDEX( Range!$E:$E; COUNTA(Range!$E:$E) )

Именованный диапазон стлИндекс

=Range!$G$2:ИНДЕКС( Range!$G:$G; СЧЁТЗ(Range!$G:$G) )

или

=Range!$G$2:INDEX( Range!$G:$G; COUNTA(Range!$G:$G) )

Именованный диапазон стлФильтр

=Range!$H$2:ИНДЕКС( Range!$H:$H; СЧЁТЗ(Range!$H:$H) )

или

=Range!$H$2:INDEX( Range!$H:$H; COUNTA(Range!$H:$H) )

Именованный диапазон DDL_Range

=Range!$H$2:ИНДЕКС( стлФильтр; МАКС(стлИндекс) )

или

=Range!$H$2:INDEX( стлФильтр; MAX(стлИндекс) )

Именованный диапазон DDL_Table

=Table!$H$2:ИНДЕКС( tblData[Фильтр]; МАКС(tblData[Индекс]) )

или

=Table!$H$2:INDEX( tblData[Фильтр]; MAX(tblData[Индекс]) )

tblData - имя умной таблицы


Именованный диапазон DDL_Fake

=DDL_Table

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

DDL_Range и DDL_Table это диапазоны, которые формируются на базе значений столбца Фильтр без пустых строк. Эти ИД указываются в свойствах ListFillRange выпадающих списков (DDL_Table - через DDL_Fake).

Шаг 6. Вставляем Combo box на лист

На ленте Разработчик в группе Элементы управления через кнопку Вставить выберите элемент управления ActiveX Поле со списком и вставьте его на лист.

Далее:

  • Нажмите кнопку Режим конструктора (предварительно убедитесь, что вставленный элемент управления активен)

  • Нажмите кнопку Свойства на ленте

  • Отредактируйте свойства в соответствии с рисунком:

    • Поле AutoWordSelect должно быть равно False
    • Поле LinkedCell сделайте равным B2
    • Поле ListFillRange должно быть либо DDL_Range для обычного диапазона (лист Range), либо DDL_Fake для умной таблицы (лист Table).
    • Поле MatchEntry = 2 - fmMatchEntryNone

Шаг 7. Модификация события Change для элементов Поле со списком

Ну и изюминка нашего приёма, то, что заставляет Поле со списком показывать нам обновленный список выбора после каждого изменения строки ввода, - событие Change этого элемента управления, которое переприсваивает свойство ListFillRange и вызывает событие раскрытия списка DropDown.

Вот и всё!

P.S. К сожалению, не я придумал такую крутую штуку. Это сделал индийский товарищ Sumit Bansal, решение которого я лишь немного оптимизировал (исправил VBA событие - у него оно почему-то было привязано к GotFocus, что всё портило, и сократил формулы).

Читайте также:

Категория: Пользовательский интерфейс | Добавил: dsb75 (19.01.2015) | Автор: Батьянов Денис E W
Просмотров: 73876 | Комментарии: 6 | Теги: Search, выпадающий список, dropdown list, контекстный поиск | Рейтинг: 3.8/9
Всего комментариев: 6
5 Rafisisus   (16.10.2017 15:00) [Материал]
Свойству комбобокса ListFillRange не присваивается DDL_Table. Вписываю этот ИД, жму Enter и редактор VB тут же его обнуляет. В чем может быть загвоздка, кто подскажет?
 upd
Была ошибка в формуле ИД

4 fantaevas   (13.04.2017 16:51) [Материал]
Хочется верить что на сообщение кто-то ответит!
спасибо автору, реально работает, НО....
после внедрения в книгу (допустим на лист 1) вышеуказанного выпадающего списка с контекстным поиском (через combo box) при попытке выбрать значения из выпадающего списка на другом листе (допустим лист 2) (на нем встроен другой, обычный выпадающий список созданный через проверку данных) на экране появляется выбранное значение и сам combo box с первого листа. Как сделать чтобы он не появлялся?

0
6 andrewkozlovskiy   (09.03.2019 23:17) [Материал]
поменяйте метод обработчика

Private Sub ComboBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    On Error Resume Next
    ComboBox1.ListFillRange = "=DDL_Fake"
    Me.ComboBox1.DropDown
End Sub

3 Wild   (23.09.2016 20:53) [Материал]
Рано радовался...
Криво работает при однотипных данных.
Если в списке будет "Значение 1, Значение 2, Значение 3,..., Значение 100", то при выборе из спика "Значение 2", подставится "Значение 20".
Вместо "Значение 3""Значение 31" и т.д.
Как пофиксить?

2 Wild   (19.09.2016 18:23) [Материал]
Шикарная вещь!
Но жаль, что можно использовать только один ListBox.
Может подскажете решение, когда на листе их МНОГО!
Чтобы каждый из них мог фильтровать одни и те же данные!

Т.е. к примеру есть блюдо, которое формируется из списка некоторых ингридиентов.
Ингридиентов несколько, но данные в них одинаковы.
Просто из списка нужно "собрать" блюдо.
По этому листбоксов много, а данные одни и те же.

1 JEYTY   (13.11.2015 11:39) [Материал]
Подскажите, а как быть если в списке города повторяются? Как сделать, что бы в выпадающем окне не было повторений?

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Яндекс.Метрика