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