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