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