В процессе разработки интерфейсов на обычных формах (не управляемых) часто возникает задача ограничить редактирование конкретных строк или ячеек в дереве значений. Например, если статус объекта в строке отличается от значения Новый, мы должны запретить пользователю изменять флажки или вводить данные в этой строке. В отличие от управляемых форм, где используется "Условное оформление", в обычных формах механизм управления внешним видом и поведением ячеек завязан на событиях табличного поля. Рассмотрим подробно, как правильно реализовать эту логику.
Разберем типичную ошибку начинающих разработчиков. Часто пытаются управлять доступностью непосредственно через объект данных строки, например, так: СтрокаДерева.МояКолонка.Доступность = Ложь;. Проанализируем, почему это не сработает: объект СтрокаДерева — это строка ДереваЗначений, которая содержит только данные (числа, строки, ссылки). У этих данных нет свойства Доступность. Это свойство относится исключительно к элементам управления на форме или к оформлению ячеек табличного поля.
Для решения задачи нам необходимо использовать специальные события табличного поля, которые отвечают за отрисовку строк на экране. Самым популярным и простым в реализации является событие ПриВыводеСтроки.
Событие ПриВыводеСтроки вызывается каждый раз, когда система отрисовывает строку в табличном поле. Это идеальное место для того, чтобы "на лету" изменить свойства ячеек. Посмотрим на практический пример кода, который блокирует ячейку и меняет цвет фона в зависимости от статуса:
Процедура ДеревоЗначенийВходногоКонтроляПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Проверяем статус из данных текущей строки
Если ДанныеСтроки.Статус = Перечисления.ТХ_СтатусыВходногоКонтроля.Новый Тогда
// Окрашиваем строку в белый цвет
ОформлениеСтроки.ЦветФона = WebЦвета.Белый;
// Скрываем флажок в конкретной ячейке, чтобы пользователь не мог его нажать
ОформлениеСтроки.Ячейки.НачатьПроверку.ОтображатьФлажок = Ложь;
ИначеЕсли ДанныеСтроки.Статус = Перечисления.ТХ_СтатусыВходногоКонтроля.ВыполняетсяПроверка Тогда
ОформлениеСтроки.ЦветФона = WebЦвета.Желтый;
// Устанавливаем режим "Только просмотр" для ячейки
ОформлениеСтроки.Ячейки.НачатьПроверку.ТолькоПросмотр = Истина;
ИначеЕсли ДанныеСтроки.Статус = Перечисления.ТХ_СтатусыВходногоКонтроля.ПроверкаПройденаУспешно Тогда
ОформлениеСтроки.ЦветФона = WebЦвета.Зеленый;
ОформлениеСтроки.Ячейки.НачатьПроверку.ТолькоПросмотр = Истина;
КонецЕсли;
КонецПроцедуры
Важные моменты:
ОформлениеСтроки позволяет нам обращаться к коллекции Ячейки. Каждая ячейка имеет свойства ТолькоПросмотр, Доступность, ОтображатьФлажок, ЦветТекста и другие.ТолькоПросмотр = Истина предпочтительнее, чем Доступность = Ложь, так как во втором случае ячейка полностью исключается из навигации (на неё нельзя кликнуть), что иногда путает пользователя.ОтображатьФлажок = Ложь полностью скрывает элемент управления из ячейки.При работе с деревом значений часто возникает ситуация: если все дочерние элементы строки заблокированы, то и родительская строка (ветка) должна быть заблокирована или отображать определенное состояние. Рассмотрим, как программно рассчитать состояние родительской ветки на основе подчиненных строк.
Проанализируем алгоритм: нам нужно создать рекурсивную процедуру, которая будет обходить дерево вверх от измененного элемента. Рассмотрим пример реализации:
Процедура УстановитьСостояниеРодителя(РодительскаяВетка)
Если РодительскаяВетка = Неопределено Тогда
Возврат;
КонецЕсли;
ВсеДочерниеЗаблокированы = Истина;
Для Каждого ПодчиненнаяСтрока Из РодительскаяВетка.Строки Цикл
// Допустим, у нас есть скрытая колонка "РазрешеноРедактирование"
Если ПодчиненнаяСтрока.РазрешеноРедактирование Тогда
ВсеДочерниеЗаблокированы = Ложь;
Прервать;
КонецЕсли;
КонецЦикла;
// Обновляем данные в родителе
РодительскаяВетка.РазрешеноРедактирование = НЕ ВсеДочерниеЗаблокированы;
// Идем выше по иерархии
УстановитьСостояниеРодителя(РодительскаяВетка.Родитель);
КонецПроцедуры
Эту логику лучше вызывать в момент первоначального заполнения дерева или при изменении статуса в строке. В самом же событии ПриВыводеСтроки мы будем просто считывать значение из колонки РазрешеноРедактирование и устанавливать ОформлениеСтроки.Ячейки.ИмяКолонки.ТолькоПросмотр.
Не стоит забывать, что визуальное оформление (серый цвет или скрытый флажок) — это лишь внешняя оболочка. Опытный пользователь может попытаться изменить значение горячими клавишами. Чтобы гарантированно запретить редактирование, проанализируем использование события ПередНачаломИзменения самого табличного поля:
Процедура ДеревоЗначенийПередНачаломИзменения(Элемент, Отказ)
ТекущиеДанные = Элемент.ТекущаяСтрока;
Если ТекущиеДанные <> Неопределено Тогда
Если ТекущиеДанные.Статус <> Перечисления.ТХ_СтатусыВходногоКонтроля.Новый Тогда
// Запрещаем переход в режим редактирования
Отказ = Истина;
Сообщить("Редактирование строк в текущем статусе запрещено!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод является самым надежным с точки зрения целостности данных, так как он блокирует саму возможность открытия редактора в ячейке.
Если ваше дерево содержит тысячи строк, событие ПриВыводеСтроки может существенно замедлить работу интерфейса при прокрутке, так как оно вызывается для каждой строки отдельно. В таких случаях рассмотрим альтернативный метод — ПриПолученииДанных.
Выясним причину его эффективности: это событие вызывается один раз для всей порции данных, видимых на экране. Оно передает коллекцию ОформленияСтрок, которую можно обработать в одном цикле. Это позволяет, например, выполнить один запрос к базе данных для всех видимых строк сразу, вместо того чтобы делать маленькие запросы в ПриВыводеСтроки.
Подведем итоги:
ОформлениеСтроки.Ячейки.ИмяЯчейки.ТолькоПросмотр = Истина в событии ПриВыводеСтроки.ОтображатьФлажок = Ложь.ПередНачаломИзменения с параметром Отказ = Истина.Используя эти методы в совокупности, мы получим отзывчивый и защищенный интерфейс, который четко дает понять пользователю, какие данные доступны для редактирования, а какие — нет.