При динамической генерации командного интерфейса на управляемой форме нередко возникает задача упорядочить созданные кнопки или поля. Например, когда пункты подменю добавляются из разных источников и в финале их требуется выстроить по алфавиту заголовков. Попытка решить эту задачу с помощью вспомогательного списка значений и метода Элементы.Переместить часто приводит к ошибке выполнения: «Несоответствие типов (параметр номер '1')». Разберем по шагам, почему возникает эта ошибка, как работает метод перемещения элементов и как правильно реализовать сортировку.
Рассмотрим исходную ситуацию. В коде выполняется обход подчиненных элементов группы, они помещаются в СписокЗначений, список сортируется, после чего в цикле вызывается перемещение:
Сортировка = Новый СписокЗначений;
Для Каждого Пункт Из ПунктыМеню Цикл
Сортировка.Добавить(Пункт.Заголовок, Пункт);
КонецЦикла;
Сортировка.СортироватьПоПредставлению();
ПредыдущийЭлемент_ = Сортировка[0];
Для Инд = 1 По Сортировка.Количество() - 1 Цикл
Пункт = Сортировка[Инд];
_форма.Элементы.Переместить(Пункт, _элементГруппа, ПредыдущийЭлемент_);
ПредыдущийЭлемент_ = Пункт;
КонецЦикла;
Проанализируем этот фрагмент и выясним причину падения платформы. Здесь допущены сразу две принципиальные ошибки:
СписокЗначений.Добавить имеет сигнатуру: Добавить(<Значение>, <Представление>, ...). Передавая в коде Сортировка.Добавить(Пункт.Заголовок, Пункт), разработчик помещает строковый заголовок в свойство Значение, а сам элемент формы — в Представление.Сортировка[Инд] возвращается объект типа ЭлементСпискаЗначений, а не сам элемент интерфейса (КнопкаФормы, ПолеФормы и т.д.). Метод Элементы.Переместить ожидает на вход непосредственно элемент формы. Передача ЭлементСпискаЗначений в первый параметр вызывает закономерное исключение. Чтобы получить целевой объект, необходимо обращаться к свойству .Значение.Посмотрим на синтаксис метода коллекции ВсеЭлементыФормы:
Переместить(<Элемент>, <Родитель>, <СледующийЭлемент>)
Обратим внимание на третий параметр. Это следующий элемент, то есть элемент, перед которым будет спозиционирован перемещаемый объект. Если передавать туда предыдущий элемент, итоговый порядок кнопок окажется перевернутым или некорректным.
Однако у метода есть важная особенность: если третий параметр опущен или передан как Неопределено, перемещаемый элемент переносится в самый конец списка подчиненных элементов указанного родителя. Это позволяет значительно упростить весь алгоритм: не требуется отслеживать соседние элементы, достаточно поочередно переместить отсортированные объекты в родительскую группу без указания третьего параметра.
Скорректируем процедуру с учетом правильного заполнения свойств списка и упрощенного вызова метода Переместить:
Процедура ОтсортироватьЭлементы(_форма, Знач _элементГруппа) Экспорт
Если ТипЗнч(_элементГруппа) = Тип("Строка") Тогда
_элементГруппа = _форма.Элементы.Найти(_элементГруппа);
Если _элементГруппа = Неопределено Тогда
Возврат;
КонецЕсли;
КонецЕсли;
Если ТипЗнч(_элементГруппа) <> Тип("ГруппаФормы") Тогда
Возврат;
КонецЕсли;
// Заполняем список: Значение - элемент формы, Представление - заголовок для сортировки
Сортировка = Новый СписокЗначений;
Для Каждого Элемент Из _элементГруппа.ПодчиненныеЭлементы Цикл
Сортировка.Добавить(Элемент, Элемент.Заголовок);
КонецЦикла;
// Сортируем список по строковому представлению
Сортировка.СортироватьПоПредставлению();
// Последовательно перемещаем элементы в конец группы
Для Каждого ЭлементСписка Из Сортировка Цикл
_форма.Элементы.Переместить(ЭлементСписка.Значение, _элементГруппа);
КонецЦикла;
КонецПроцедуры
В этом варианте каждый элемент из отсортированного списка последовательно переносится в конец группы, благодаря чему они выстраиваются в строго заданном порядке возрастания заголовков.
Если код выполняется на сервере (например, в процедуре ПриСозданииНаСервере), более наглядным и надежным вариантом выступает использование временной ТаблицыЗначений. Это исключает путаницу между значением и представлением:
ТаблицаСортировки = Новый ТаблицаЗначений;
ТаблицаСортировки.Колонки.Добавить("Заголовок", Новый ОписаниеТипов("Строка"));
ТаблицаСортировки.Колонки.Добавить("Элемент");
Для Каждого Элемент Из _элементГруппа.ПодчиненныеЭлементы Цикл
НоваяСтрока = ТаблицаСортировки.Добавить();
НоваяСтрока.Заголовок = Элемент.Заголовок;
НоваяСтрока.Элемент = Элемент;
КонецЦикла;
ТаблицаСортировки.Сортировать("Заголовок Возр");
Для Каждого Стр Из ТаблицаСортировки Цикл
_форма.Элементы.Переместить(Стр.Элемент, _элементГруппа);
КонецЦикла;
Перемещение элементов постфактум создает дополнительную нагрузку на интерфейсную подсистему формы. Если кнопки создаются динамически в коде из массива, структуры или результата запроса, правильнее изначально отсортировать коллекцию исходных данных перед добавлением элементов на форму:
Элементы.Элементы.Добавить: добавляем элементы поочередно. Каждый новый элемент естественным образом встает в конец группы, сохраняя порядок отсортированного источника.Элементы.Вставить: если кнопку необходимо внедрить среди уже существующих элементов формы, используем метод Вставить(<Имя>, <ТипЭлемента>, <Родитель>, <СледующийЭлемент>), сразу указывая элемент, перед которым она должна отображаться. Это полностью снимает необходимость вызывать метод Переместить.