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