При разработке интерфейсов на управляемых формах 1С начинающие программисты часто сталкиваются с одной и той же проблемой: при попытке переместить строку в СпискеЗначений с помощью кнопок «Вверх» или «Вниз» визуальное выделение остается на старом месте. В итоге, если пользователь нажимает кнопку дважды, вместо повторного сдвига того же элемента начинает перемещаться совершенно другой, который занял освободившееся место. Разберем подробно, почему так происходит и как реализовать этот механизм правильно.
Для начала проанализируем ситуацию, описанную в теме. Когда мы работаем с таким объектом, как ТаблицаЗначений, свойство ТекущаяСтрока возвращает уникальный идентификатор строки. Этот идентификатор «приклеен» к данным: куда бы вы ни переместили строку, её ID не изменится, и платформа сама передвинет фокус на форме вслед за данными.
Однако со СпискомЗначений ситуация иная. Свойство ТекущаяСтрока элемента формы, связанного со списком, возвращает не объект и не ID, а обычный числовой индекс (порядковый номер, начиная с нуля). Рассмотрим ситуацию по шагам:
ТекущаяСтрока равна 5.Сдвинуть(5, -1). Элемент перемещается на позицию 4.Таким образом, для корректной работы нам необходимо не только сдвинуть данные в коллекции, но и принудительно обновить индекс в элементе управления формы. Рассмотрим, как это сделать программно.
Рассмотрим подробнее реализацию процедуры для движения элемента к началу списка. Нам нужно получить текущий индекс, проверить, не является ли строка самой первой, выполнить сдвиг и установить новый индекс для фокуса.
Посмотрим на пример кода:
&НаКлиенте
Процедура ПереместитьВверх(Команда)
// Получаем индекс текущей выделенной строки
ТекущийИндекс = Элементы.СписокЗначений.ТекущаяСтрока;
// Проверяем, выбрана ли строка и не является ли она первой (индекс 0)
Если ТекущийИндекс <> Неопределено И ТекущийИндекс > 0 Тогда
// Сдвигаем элемент в коллекции данных на -1 (вверх)
Объект.СписокЗначений.Сдвинуть(ТекущийИндекс, -1);
// Устанавливаем фокус на новый индекс, чтобы выделение «уехало» вместе с данными
Элементы.СписокЗначений.ТекущаяСтрока = ТекущийИндекс - 1;
КонецЕсли;
КонецПроцедуры
Ситуация с перемещением вниз аналогична, но здесь важно правильно определить границу списка. Выясним причину возможной ошибки: если мы попытаемся сдвинуть последний элемент еще ниже, платформа может выдать исключение или поведение будет неопределенным. Нам нужно знать общее количество элементов в коллекции.
Разберем по шагам код для кнопки «Вниз»:
&НаКлиенте
Процедура ПереместитьВниз(Команда)
ТекущийИндекс = Элементы.СписокЗначений.ТекущаяСтрока;
// Проверяем, что строка выбрана и она не последняя в списке
// Последний индекс всегда равен (Количество - 1)
Если ТекущийИндекс <> Неопределено
И ТекущийИндекс < (Объект.СписокЗначений.Количество() - 1) Тогда
// Сдвигаем элемент на 1 позицию вниз
Объект.СписокЗначений.Сдвинуть(ТекущийИндекс, 1);
// Переносим выделение на новый индекс
Элементы.СписокЗначений.ТекущаяСтрока = ТекущийИндекс + 1;
КонецЕсли;
КонецПроцедуры
При работе с этим кодом следует обратить внимание на несколько важных моментов, которые помогут избежать ошибок в будущем:
Сдвинуть() доступен на клиенте, поэтому нам не нужно передавать данные на сервер и обратно (делать контекстный вызов). Это делает интерфейс очень быстрым и отзывчивым.ТекущаяСтрока на Неопределено. Если пользователь нажмет кнопку, когда в списке нет выделения (или список пуст), программа выдаст ошибку, если проверки не будет.ОповеститьОбИзменении().Иногда (хотя и редко) стандартный метод Сдвинуть() может не подходить для специфических задач. В таком случае можно использовать классический алгоритм «извлечь и вставить». Рассмотрим его логику:
1. Мы копируем текущий элемент (значение, представление, пометку) в буферную переменную.
2. Удаляем строку из коллекции по текущему индексу методом Удалить().
3. Вставляем данные обратно в нужную позицию методом Вставить().
4. Программно переводим фокус.
Этот метод более громоздкий, но он наглядно показывает, как 1С управляет индексами внутри коллекций. Однако для обычного перемещения строк метод Сдвинуть() является предпочтительным и наиболее оптимальным.
Если в вашем списке разрешено множественное выделение (свойство РежимВыделения установлено в значение Множественный), то использование ТекущаяСтрока станет недостаточным. Вам придется работать с массивом ВыделенныеСтроки.
Проанализируем ситуацию: при перемещении группы строк «вверх», начинать цикл перебора массива выделенных индексов нужно с самого маленького индекса к самому большому. А при движении «вниз» — наоборот, с самого большого индекса к меньшему. Это необходимо для того, чтобы индексы элементов, которые еще ждут своей очереди на перемещение, не изменялись в процессе работы цикла.
Резюме: Главный секрет управления списком значений на форме заключается в ручной синхронизации индекса данных и индекса визуального выделения. Используя приведенные выше примеры, вы сможете создать удобный и интуитивно понятный интерфейс для ваших пользователей.