При разработке интерфейсов на управляемых формах 1С программисты часто сталкиваются с необходимостью программно выделить все строки в динамическом списке. Казалось бы, стандартная комбинация клавиш Ctrl+A должна решать эту задачу. Однако при попытке имитировать это нажатие через WScript.Shell возникает странное поведение: визуально строки в списке выделяются, но программное свойство Элементы.Список.ВыделенныеСтроки остается пустым. В этой статье мы подробно разберем, почему это происходит, как заставить платформу «увидеть» выделение и какие существуют альтернативные способы работы с данными динамического списка.
Разберем ситуацию: вы используете объект WScript.Shell для отправки команды ^a (аналог Ctrl+A). Проблема заключается в асинхронной природе взаимодействия операционной системы Windows и платформы 1С. Когда выполняется ваш код, управление находится внутри потока исполнения встроенного языка 1С. Метод SendKeys всего лишь помещает сообщение о нажатии клавиш в системную очередь сообщений Windows. 1С начнет обрабатывать эту очередь только тогда, когда основной интерфейсный поток освободится — то есть после завершения вашей процедуры.
Проанализируем цепочку событий:
SendKeys("^a").Элементы.Список.ВыделенныеСтроки.Выясним причину успеха при повторном нажатии кнопки: к моменту второго вызова 1С уже успела обработать предыдущую команду из очереди сообщений. Чтобы автоматизировать этот процесс, нам нужно «разорвать» выполнение кода. Для этого идеально подходит метод ПодключитьОбработчикОжидания. Рассмотрим по шагам, как это реализовать.
Во-первых, создадим основную процедуру, которая инициирует выделение:
&НаКлиенте
Процедура ВыделитьВсеСтрокиИОбработать()
// Устанавливаем фокус на нужный элемент формы
ТекущийЭлемент = Элементы.Список;
// Имитируем нажатие Ctrl+A
WScript = Новый COMОбъект("WScript.Shell");
WScript.SendKeys("^a");
// Вместо мгновенного обращения к массиву, подключаем обработчик
// 0.1 или 0.5 секунды обычно достаточно для обработки очереди сообщений
ПодключитьОбработчикОжидания("ЗавершитьОбработкуВыделения", 0.1, Истина);
КонецПроцедуры
Во-вторых, опишем саму процедуру обработки, в которой массив ВыделенныеСтроки уже будет заполнен:
&НаКлиенте
Процедура ЗавершитьОбработкуВыделения()
МассивСтрок = Элементы.Список.ВыделенныеСтроки;
Если МассивСтрок.Количество() > 0 Тогда
Сообщить("Количество выделенных строк: " + МассивСтрок.Количество());
// Здесь можно выполнять действия с выделенными строками
Иначе
Сообщить("Строки не были выделены, попробуйте увеличить время ожидания.");
КонецЕсли;
КонецПроцедуры
Важный нюанс: использование SendKeys критически зависит от активного окна. Если в момент выполнения кода пользователь переключится на другое приложение, команда ^a уйдет в пустоту или в другое окно. Рекомендуется использовать метод WScript.AppActivate, чтобы гарантированно направить команды в окно 1С.
Рассмотрим подробнее, почему разработчики вообще прибегают к SendKeys. В отличие от таблиц значений или деревьев на форме, динамический список не хранит все данные в памяти клиента. Он подгружает их порциями («ленивая загрузка»).
При использовании SendKeys("^a") платформа пытается подгрузить идентификаторы строк, чтобы поместить их в ВыделенныеСтроки. Однако если в списке десятки тысяч записей, такое выделение может привести к существенным задержкам или даже зависанию интерфейса, так как клиент запрашивает у сервера ключи всех записей сразу. В качестве альтернативы для удобного выбора строк можно использовать способ реализации флажков в динамическом списке.
Если ваша задача — не визуальное управление группировками (сворачивание/разворачивание), а программная обработка всех строк, попавших в текущий отбор списка, лучше отказаться от SendKeys. Проанализируем более надежный метод получения данных через систему компоновки данных (СКД).
Каждый динамический список имеет свойство КомпоновщикНастроек. Мы можем программно выполнить тот же запрос, который выполняет список, с учетом всех пользовательских фильтров:
&НаСервере
Функция ПолучитьВсеСтрокиСпискаНаСервере()
// Получаем схему компоновки данных динамического списка
Схема = Элементы.Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
Настройки = Элементы.Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(Схема, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(Макет);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТаблицаРезультат = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТаблицаРезультат; // Теперь у вас есть все данные без эмуляции нажатий
КонецФункции
Этот метод работает быстрее, стабильнее и не зависит от фокуса окна или раскладки клавиатуры. Однако он возвращает данные из базы, а не идентификаторы визуальных строк формы.
В некоторых случаях (как у автора исходной темы) целью является именно визуальное управление — например, разворачивание всех группировок динамического списка. Прямого метода РазвернутьВсе() для динамического списка в платформе 8.3 не существует.
В этой ситуации связка SendKeys + ПодключитьОбработчикОжидания остается рабочим «костылем». После того как строки выделены через паузу, вы можете перебрать идентификаторы в массиве ВыделенныеСтроки и применить к ним метод Элементы.Список.Развернуть(Идентификатор). Но помните, что развернуть можно только те узлы, которые уже загружены на клиент.
Подведем итог: если вам нужно быстро получить данные — используйте запрос или СКД на сервере. Если же требуется имитировать действия пользователя для работы с интерфейсом — используйте SendKeys, но обязательно разделяйте процесс нажатия и процесс чтения данных с помощью ПодключитьОбработчикОжидания.