Как динамически изменять видимость или доступность команд формы в 1С 8.3 на управляемых формах?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке функционала в 1С 8.3 на управляемых формах мы часто сталкиваемся с необходимостью динамического управления элементами интерфейса. Одной из распространенных задач является включение или отключение команд формы в зависимости от текущих условий: роли пользователя, состояния данных или других факторов. Однако, порой возникает путаница с тем, какое свойство команды следует использовать для этой цели. В этой статье мы подробно разберем, как правильно управлять видимостью и доступностью команд формы, чтобы ваш интерфейс был функциональным и интуитивно понятным.

Разбираемся с понятиями: КомандаФормы.Использование и ЭлементыФормы.Доступность/Видимость

Первостепенная задача, которую мы должны решить, это понять фундаментальную разницу между двумя ключевыми механизмами управления командами и элементами формы. Опытные разработчики 1С часто видят, что новички пытаются использовать свойство Использование у объекта КомандаФормы для динамического управления её видимостью или доступностью во время выполнения программы. Однако, такой подход не принесет желаемого результата, и давайте выясним, почему.

КомандаФормы.Использование – это свойство, которое определяется на этапе конфигурации, то есть в режиме "Конфигуратор", и относится непосредственно к объекту команды. Оно указывает платформе, должна ли данная команда вообще быть доступна для использования в интерфейсе. Если мы установим Использование = Ложь в конфигураторе, эта команда не будет создана и отображена в интерфейсе при открытии формы. Важно понимать, что это статическое, дизайн-тайм свойство, которое не предназначено для изменения в процессе работы пользователя с программой. Попытка изменить его через код в управляемой форме не даст эффекта, так как интерфейс уже сформирован.

ЭлементыФормы.Доступность и ЭлементыФормы.Видимость – это совсем другие свойства, и они относятся не к самой команде, а к элементу формы, который визуально представляет эту команду в интерфейсе. Таким элементом может быть кнопка, пункт меню, или элемент в командной панели. Эти свойства предназначены именно для динамического управления состоянием элементов формы во время выполнения:

  1. Доступность = Ложь: Когда мы устанавливаем это свойство, элемент формы (например, кнопка) становится "заблокированным" или "серым" (greyed out). Он остается видимым для пользователя, но его невозможно нажать или активировать. Этот вариант идеально подходит, когда мы хотим показать пользователю, что какая-то операция существует, но в данный момент она недоступна из-за текущего состояния данных или прав пользователя. Например, кнопка "Провести документ" может быть недоступна, если документ уже проведен.
  2. Видимость = Ложь: В этом случае элемент формы полностью исчезает из интерфейса. Пользователь его не видит, и для него это равносильно тому, что такой команды просто не существует. Мы используем это свойство, когда хотим полностью скрыть функционал, который абсолютно неактуален или к которому у пользователя нет никаких прав. Например, кнопка "Изменить структуру" для обычного пользователя.

Таким образом, для динамического управления поведением команд в управляемых формах мы всегда работаем со свойствами Доступность или Видимость того элемента формы, который эту команду отображает. Наша задача сводится к тому, чтобы правильно найти этот элемент и установить нужное свойство.

Применение Доступности/Видимости для стандартных кнопок

Начнем с самого простого сценария: у нас есть команда, которая привязана к кнопке, явно размещенной на форме, и мы знаем имя этой кнопки. Например, если в конфигураторе мы создали команду "ПровестиДокументКоманда" и разместили ее на форме как кнопку с именем "КнопкаПровести".

В этом случае, чтобы динамически управлять ее доступностью, мы будем обращаться к элементу формы по его имени и изменять его свойство Доступность или Видимость. Рассмотрим пример, где мы отключаем кнопку "Провести" для уже проведенного документа.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Мы выполняем проверку на сервере, так как данные объекта доступны только там.
    // При создании формы на сервере, она уже сформирована, и мы можем менять свойства ее элементов.
    Если Объект.Проведен Тогда
        // Если документ проведен, делаем кнопку "Провести" недоступной.
        // Элементы.ИмяКнопки - это коллекция элементов формы, доступная на сервере и клиенте.
        Элементы.КнопкаПровести.Доступность = Ложь;
        // Если мы хотим полностью скрыть кнопку, используем Видимость
        // Элементы.КнопкаПровести.Видимость = Ложь;
    Иначе
        // Если документ не проведен, кнопка должна быть доступна (по умолчанию она доступна)
        Элементы.КнопкаПровести.Доступность = Истина;
    КонецЕсли;
КонецПроцедуры

Обратите внимание на директиву &НаСервере. Мы выполняем этот код на сервере, так как нам нужен доступ к данным объекта (Объект.Проведен). Изменения свойств элементов формы, сделанные на сервере, автоматически отображаются на клиенте после завершения вызова.

Помимо события ПриСозданииНаСервере, мы можем вызывать подобный код в других обработчиках, например:

Управление доступностью команд в панелях команд и подменю

Ситуация усложняется, когда команда не привязана к явно созданной нами кнопке, а попадает на форму из общих команд, команд подсистем или размещается внутри автоматически генерируемых панелей команд (например, "Действия" или "Создать"). В таких случаях имя элемента формы, представляющего команду, может быть неочевидным или даже динамически присвоенным платформой. Наша задача – найти этот элемент среди всех элементов формы.

Мы можем использовать цикл для перебора всех элементов формы и их подэлементов, чтобы найти нужную кнопку по имени команды, к которой она привязана. У элемента формы типа КнопкаФормы есть свойство ИмяКоманды, которое позволяет установить соответствие.


&НаСервере
Процедура УстановитьДоступностьКоманды(ИмяИскомойКоманды, НовоеЗначениеДоступности)

    Для Каждого ЭлементФормы Из Элементы Цикл
        // Проверяем, является ли текущий элемент кнопкой формы
        Если ТипЗнч(ЭлементФормы) = Тип("КнопкаФормы") Тогда
            // Если это кнопка, проверяем, привязана ли она к нашей команде
            Если ЭлементФормы.ИмяКоманды = ИмяИскомойКоманды Тогда
                // Нашли кнопку, связанную с нашей командой, и устанавливаем ей доступность
                ЭлементФормы.Доступность = НовоеЗначениеДоступности;
                // В большинстве случаев одна команда представлена одной основной кнопкой.
                // Если мы уверены, что нашли единственный экземпляр, можем выйти из цикла: Прервать;
                // Но если команда может быть в нескольких местах (например, в главном меню и в контекстном),
                // то продолжаем поиск, чтобы изменить все её представления.
                Продолжить; // Продолжаем, чтобы найти все возможные вхождения
            КонецЕсли;
        // Проверяем, является ли текущий элемент группой формы
        ИначеЕсли ТипЗнч(ЭлементФормы) = Тип("ГруппаФормы") Тогда
            // Если это группа, то команды могут быть вложены в нее (например, в панели команд)
            // Мы должны перебрать элементы этой группы
            Для Каждого ПодЭлементФормы Из ЭлементФормы.Элементы Цикл
                Если ТипЗнч(ПодЭлементФормы) = Тип("КнопкаФормы") Тогда
                    Если ПодЭлементФормы.ИмяКоманды = ИмяИскомойКоманды Тогда
                        ПодЭлементФормы.Доступность = НовоеЗначениеДоступности;
                        Продолжить; // Продолжаем, чтобы найти все возможные вхождения
                    КонецЕсли;
                // Важно: если группы могут быть вложенными (группа в группе),
                // то потребуется рекурсивный обход. Для большинства типовых задач достаточно двух уровней.
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

// Пример вызова этой универсальной процедуры
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Допустим, у нас есть общая команда "ПровестиДокумент"
    Если Объект.Проведен Тогда
        УстановитьДоступностьКоманды("ПровестиДокумент", Ложь);
    Иначе
        УстановитьДоступностьКоманды("ПровестиДокумент", Истина);
    КонецЕсли;

    // Пример для другой команды
    Если НЕ РольДоступна("ПолныеПрава") Тогда
        УстановитьДоступностьКоманды("КомандаТолькоДляАдминистратора", Ложь);
    КонецЕсли;
КонецПроцедуры

Мы видим, что эта процедура обеспечивает более гибкий подход, позволяя управлять доступностью команды, даже если мы не знаем точного имени элемента формы, который ее представляет. Код осуществляет поиск по всем элементам формы и их непосредственным подэлементам-группам. Для разработчиков, стремящихся к стандартизации, подобные алгоритмы часто выносятся в общие модули для программного изменения форм, чтобы не дублировать код в каждой форме.

Работа с командами в меню функций и других стандартных местах

Команды могут быть представлены не только в виде явных кнопок на форме или в панелях. Многие команды попадают на форму через механизм командного интерфейса приложения, стандартные подсистемы или контекстные меню динамических списков. Принцип их динамического управления остается тем же: мы должны найти соответствующий элемент формы и изменить его свойства Доступность или Видимость.

Если команда попадает в командную панель динамического списка, мы можем обращаться к ней через коллекцию элементов командной панели этого списка. Эти же приемы работы с коллекциями элементов полезны, когда требуется выполнить программное перемещение колонки динамического списка или организовать программный вывод произвольной таблицы значений на управляемую форму. Рассмотрим пример отключения команды:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Допустим, у нас есть динамический список с именем "МойДинамическийСписок"
    // и в его командной панели есть кнопка, представляющая команду "КомандаСписка"
    Если ЭтоУсловиеДляОтключенияКомандыСписка Тогда // Ваше условие
        // Обращаемся к командной панели динамического списка и ее элементам
        // Важно: имя элемента в командной панели может быть автоматическим или заданным явно.
        // Если имя команды совпадает с именем элемента-кнопки в панели:
        Элементы.МойДинамическийСписок.КоманднаяПанель.Элементы.КомандаСписка.Доступность = Ложь;
    КонецЕсли;

    // Если команда добавлена в группу "Действия" формы (часто автоматически генерируется):
    Если ЭтоУсловиеДляОтключенияДействияФормы Тогда
        // "ДействияФормы" - часто это автосгенерированная группа для стандартных команд формы.
        // Нужно знать имя элемента-кнопки внутри этой группы, например, "ДействияФормыКоманда1"
        Элементы.ДействияФормы.Элементы.ДействияФормыКоманда1.Доступность = Ложь;
    КонецЕсли;
КонецПроцедуры

В некоторых случаях, особенно для команд, которые появляются в контекстных меню или формируются очень динамично, может потребоваться более глубокий анализ структуры формы через отладчик или специализированный редактор форм в режиме предприятия, чтобы точно определить имя элемента, который нужно изменять. Однако, общие подходы с перебором элементов формы, как показано выше, остаются применимыми и для этих сценариев.

Важные моменты и лучшие практики

Чтобы эффективно управлять доступностью и видимостью команд, мы рекомендуем придерживаться следующих практических советов:

  1. Определяйте место выполнения кода: Всегда четко понимайте, где должен выполняться ваш код. Если для определения доступности команды необходим доступ к данным объекта (например, проверка реквизитов документа), то код должен выполняться на сервере (например, в ПриСозданииНаСервере или через серверные вызовы с клиента). Если же решение о доступности принимается исключительно на основе клиентских данных или пользовательских действий, то можно использовать клиентский код.
  2. Используйте функции для поиска: Как мы уже видели, создание универсальной функции для поиска элемента по имени команды (как УстановитьДоступностьКоманды) значительно упрощает код и делает его более поддерживаемым. При большом объеме изменений кода удобным инструментом может стать текучий конструктор программной модификации форм, который использует паттерн Builder для улучшения читаемости.
  3. Проверяйте существование элемента: Перед тем как пытаться изменить свойство элемента формы, всегда полезно убедиться, что этот элемент действительно существует. Особенно это актуально для элементов, которые могут быть добавлены или удалены динамически, или для конфигураций с изменяемым интерфейсом. Это помогает избежать ошибок выполнения. Для облегчения написания такого кода и предварительного анализа состава элементов можно воспользоваться конструктором управляемых форм. Например:
    
    Если Элементы.Есть("КнопкаПровести") Тогда
        Элементы.КнопкаПровести.Доступность = Ложь;
    КонецЕсли;
    
  4. Учитывайте производительность: При очень большом количестве элементов на форме или частых вызовах функций перебора, это может повлиять на производительность. Однако, для большинства стандартных форм и разумного количества команд это обычно не является критичной проблемой. Если производительность становится узким местом, рассмотрите возможность кеширования ссылок на элементы или более специфичного поиска, если это возможно.
  5. Сохраняйте консистентность интерфейса: Используйте Доступность и Видимость обдуманно. Доступность = Ложь обычно предпочтительнее для временно недоступных действий, так как это помогает пользователю понимать, что такая функция существует. Видимость = Ложь лучше использовать, когда функция полностью неактуальна для текущего контекста или пользователя.

Следуя этим рекомендациям и понимая разницу между свойствами команд и элементов формы, вы сможете эффективно и корректно управлять интерфейсом ваших управляемых форм в 1С 8.3, делая их интуитивно понятными и функциональными для конечных пользователей.

← На главную