В процессе разработки интерфейсов в среде «1С:Предприятие» часто возникает задача предоставить пользователю максимальную гибкость при работе с табличными данными. Типичный пример — необходимость динамически изменять тип данных программно созданной колонки таблицы значений. Однако разработчики сразу сталкиваются с двумя серьезными платформенными ограничениями:
ТипЗначения у уже существующей колонки таблицы значений.В этой статье мы подробно разберем, как обойти эти ограничения, рассмотрим проверенные практические методы реализации данной задачи как для обычных, так и для управляемых форм, а также приведем готовые примеры кода.
Если мы попытаемся программно перезаписать свойство ТипЗначения у колонки таблицы значений напрямую, платформа вызовет ошибку выполнения. Чтобы обойти это системное ограничение, нам необходимо применить обходной алгоритм пересоздания колонки.
Разберем классический алгоритм для объекта ТаблицаЗначений в коде:
ПривестиЗначение() для описания типов).Особенности работы на управляемых формах (начиная с версии 8.2)
Если таблица значений выведена на управляемую форму и связана с реквизитом формы, простых манипуляций с объектом ТаблицаЗначений будет недостаточно. Связь между элементами формы и реквизитами жестко контролируется платформой. Для изменения типа колонки на управляемой форме нам потребуется использовать серверный метод ИзменитьРеквизиты().
Рассмотрим пример процедуры, выполняющей эту операцию на сервере:
&НаСервере
Процедура ИзменитьТипКолонкиТаблицыФормы(ИмяКолонки, НовыйТипДанных)
// Определяем пути к реквизитам
ИмяТаблицыСвязи = "ДеревоДанных"; // Имя вашей таблицы-реквизита на форме
ПолныйПутьКРеквизиту = ИмяТаблицыСвязи + "." + ИмяКолонки;
// 1. Подготавливаем массивы для удаления и добавления реквизитов формы
МассивУдаляемыхРеквизитов = Новый Массив;
МассивУдаляемыхРеквизитов.Добавить(ПолныйПутьКРеквизиту);
МассивДобавляемыхРеквизитов = Новый Массив;
НовыйРеквизит = Новый РеквизитФормы(ИмяКолонки, НовыйТипДанных, ИмяТаблицыСвязи, "Заголовок колонки");
МассивДобавляемыхРеквизитов.Добавить(НовыйРеквизит);
// 2. Вызываем транзакционное изменение реквизитов формы
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, МассивУдаляемыхРеквизитов);
// 3. Пересоздаем связанный элемент интерфейса (поле ввода в таблице)
ИмяЭлементаКолонки = ИмяТаблицыСвязи + ИмяКолонки;
ЭлементКолонки = Элементы.Найти(ИмяЭлементаКолонки);
Если ЭлементКолонки <> Неопределено Тогда
Элементы.Удалить(ЭлементКолонки);
КонецЕсли;
НовыйЭлемент = Элементы.Добавить(ИмяЭлементаКолонки, Тип("ПолеФормы"), Элементы[ИмяТаблицыСвязи]);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = ПолныйПутьКРеквизиту;
КонецПроцедуры
Поскольку перехватить стандартный клик по шапке таблицы штатными средствами невозможно, мы можем применить несколько альтернативных UX/UI решений, которые будут интуитивно понятны пользователю.
Вариант А: Использование контекстного меню (Правый клик мыши)
Это один из самых чистых способов с точки зрения интерфейса. Пользователь кликает правой кнопкой мыши по любой ячейке нужной колонки и выбирает пункт «Изменить тип колонки».
Разберем по шагам, как это реализовать:
ТекущийЭлемент.Посмотрим на пример клиентского кода:
&НаКлиенте
Процедура КомандаИзменитьТипКолонки(Команда)
ТекущийЭлементТаблицы = Элементы.ДеревоДанных.ТекущийЭлемент;
Если ТекущийЭлементТаблицы = Неопределено Тогда
Возврат;
КонецЕсли;
// Получаем имя колонки из имени элемента формы
ИмяКолонки = СтрЗаменить(ТекущийЭлементТаблицы.Имя, "ДеревоДанных", "");
// Формируем список доступных типов для выбора
СписокВыбораТипов = Новый СписокЗначений;
СписокВыбораТипов.Добавить(Новый ОписаниеТипов("Строка"), "Текст (Строка)");
СписокВыбораТипов.Добавить(Новый ОписаниеТипов("Число"), "Числовое значение");
СписокВыбораТипов.Добавить(Новый ОписаниеТипов("Дата"), "Дата");
// Показываем меню выбора пользователю
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораТипаКолонки", ЭтотОбъект, ИмяКолонки);
ПоказатьВыборИзСписка(ОписаниеОповещения, СписокВыбораТипов, ТекущийЭлементТаблицы);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораТипаКолонки(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
Если ВыбранныйЭлемент = Неопределено Тогда
Возврат;
КонецЕсли;
ИмяКолонки = ДополнительныеПараметры;
НовыйТипДанных = ВыбранныйЭлемент.Значение;
// Вызываем серверный метод пересоздания реквизита
ИзменитьТипКолонкиТаблицыФормы(ИмяКолонки, НовыйТипДанных);
КонецПроцедуры
Вариант Б: Имитация шапки таблицы («Фейковая шапка»)
Если проект требует, чтобы действие происходило именно при нажатии сверху над колонками, мы можем полностью скрыть стандартную шапку таблицы (установив свойство таблицы формы ОтображатьВШапке = Ложь). Вместо нее непосредственно над таблицей мы размещаем горизонтальную группу элементов, в которую программно или визуально добавляем обычные кнопки. Каждая кнопка визуально имитирует заголовок колонки. При нажатии на кнопку-заголовок срабатывает стандартное событие ПриНажатии, где мы открываем диалог выбора типа.
Вариант В: Специальная строка настройки параметров
Мы можем зафиксировать самую первую строку в таблице значений и сделать ее служебной. Например, записать туда текст «[Нажмите для выбора типа колонки]». В обработчиках событий таблицы ПриНачалеРедактирования или Выбор мы анализируем, является ли текущая строка первой. Если да — мы прерываем стандартный ввод данных и открываем диалог изменения типа текущей колонки.
Чтобы избежать ресурсоемких операций удаления и повторного добавления реквизитов формы на сервере (что может замедлять работу интерфейса при больших объемах данных), мы можем спроектировать архитектуру иначе.
При программном создании колонок мы сразу присваиваем им составной тип данных, содержащий все возможные типы из нашего ограниченного списка. Для того чтобы избавить пользователя от необходимости каждый раз видеть стандартную кнопку выбора типа «Т» при активации ячейки, мы будем динамически управлять доступным типом ввода "на лету" через событие таблицы ПриНачалеРедактирования.
Рассмотрим, как это реализовать:
&НаКлиенте
Процедура ДеревоДанныхПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
ТекущиеДанные = Элементы.ДеревоДанных.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущаяКолонка = Элементы.ДеревоДанных.ТекущийЭлемент;
ИмяКолонки = ТекущаяКолонка.Имя;
// Допустим, тип для этой колонки уже был сохранен нами ранее в настройках
// Ограничиваем доступный для ввода тип конкретной ячейки динамически
Если ИмяКолонки = "ДеревоДанныхКолонка1" Тогда
// Ограничиваем только типом "Число"
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
ОграниченныйОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
// Устанавливаем свойства элемента формы текущей колонки
ТекущаяКолонка.ОграничениеТипа = ОграниченныйОписаниеТипов;
ТекущаяКолонка.ВыбиратьТип = Ложь; // Отключает кнопку выбора типа "Т"
КонецЕсли;
КонецПроцедуры
Этот подход со временем зарекомендовал себя как наиболее производительный для динамических таблиц на управляемых формах 1С, поскольку он полностью исключает серверные вызовы метода ИзменитьРеквизиты() при изменении типов пользователем.