Почему вычисляемые поля формы недоступны в событии "ПриОткрытии" и как правильно подключить внешние классы 1С++?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Почему вычисляемые поля формы недоступны в событии "ПриОткрытии"?

Одной из наиболее распространенных причин, по которой значения вычисляемых полей формы могут быть пустыми при попытке чтения в событии ПриОткрытии (СобытиеГМ_ПриОткрытии), является жизненный цикл формы в 1С 7.7. Рассмотрим этот момент подробнее:

  1. Контекст объекта против контекста формы: В 1С 7.7 существует важное различие между контекстом самого объекта (например, документа или справочника) и контекстом его формы. В событии ПриОткрытии мы получаем доступ к данным, непосредственно связанным с объектом, которые уже загружены из базы данных (т.е. к его реквизитам). Однако элементы управления формы, включая поля ввода и, в особенности, вычисляемые поля, еще не полностью инициализированы и отрисованы.
  2. Этапы инициализации формы:
    • ПриОткрытии (OnOpen): Это событие срабатывает очень рано, до того, как система полностью подготовит и отобразит визуальные элементы формы. В этот момент значения реквизитов документа доступны, но поля ввода, чьи значения определяются функциями в модуле формы (так называемые «формулы» или «вычисляемые поля»), еще не выполнили свои расчеты. Система просто не успела их проинициализировать, и попытка чтения вернет пустое значение или 0.
    • ПослеОткрытия (AfterOpen): Это событие не является стандартным для 1С 7.7 и обычно добавляется внешними компонентами, такими как 1С++ или FormEx. К моменту вызова этого события визуальная часть формы уже построена, все элементы управления созданы, и все функции-формулы для вычисляемых полей уже отработали. Именно в этом событии мы должны читать значения вычисляемых полей.
  3. Различие между реквизитом объекта и полем ввода формы: Важно понимать, что «поле ввода» на форме и «реквизит объекта» — это не одно и то же. Поле ввода — это визуальный элемент управления формы, который может быть связан (ссылаться) как на реквизит объекта (документа, справочника), так и на произвольное значение, вычисляемое функцией формы. Когда мы говорим о «реквизите документа», мы имеем в виду данные, хранящиеся в базе данных как часть самого документа. Когда мы говорим о «поле ввода», мы имеем в виду элемент интерфейса, который отображает эти данные или результат какой-либо функции. Поэтому утверждение, что «поле ввода реквизитом объекта быть не может», верно в том смысле, что поле ввода — это элемент формы, а не сущность объекта в базе данных.

Чтобы прочитать значение вычисляемого поля, определенного функцией в модуле формы, нам следует перенести логику чтения в событие ПослеОткрытия. Например:


Процедура СобытиеГМ_ПослеОткрытия(Конт) Экспорт
    // Теперь форма полностью открыта и вычисляемые поля инициализированы
    Сообщить(КонтФормы.Форма.ВысчитываемоеПоле);
    // или если хотим вызвать напрямую функцию, которая вычисляет это поле
    // ЗначениеПоля = КонтФормы.Форма.ИмяФункцииВычисляющаяПоле();
КонецПроцедуры

Альтернативный способ получения вычисляемых значений: использование "Шаблон"

Если по каким-то причинам перенос логики в ПослеОткрытия невозможен или неудобен, 1С++ предлагает альтернативный механизм для принудительного вызова функций формы. Мы можем использовать метод контекста Шаблон для прямого вызова функции, которая определена в модуле формы и отвечает за вычисление значения поля.

Метод Шаблон позволяет выполнить произвольный код из модуля формы в заданном контексте. Например, если у нас есть функция ПолучитьВысчитываемоеЗначение(), которая возвращает искомое значение, мы можем вызвать ее следующим образом:


Процедура СобытиеГМ_ПриОткрытии(Конт,ФлагЧтенияНастройки) Экспорт
    // Прямой вызов функции, которая вычисляет значение
    ЗначениеПоля = Конт.Шаблон("ПолучитьВысчитываемоеЗначение()");
    Сообщить(ЗначениеПоля);
    // Если поле определено как [ИмяФункции()], то Шаблон также сработает:
    // ЗначениеПоля = Конт.Шаблон("[ИмяФункции()]"); 
КонецПроцедуры

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

Корректное подключение внешних классов 1С++ (ERT-файлов)

В процессе работы с 1С++ часто возникает необходимость использования внешних обработок (.ert файлов) для определения классов. Это позволяет легче управлять кодом и обновлять его без необходимости изменения конфигурации. Однако, чтобы 1С++ корректно загрузил внешний класс, нам нужно правильно прописать его в файле defcls.ert.

  1. Синтаксис для внешних классов: Для загрузки класса из внешнего файла .ert (в отличие от класса, хранящегося в метаданных), мы используем следующий синтаксис в defcls.ert: class ИмяКласса = "путь_к_файлу\КлассПерехватСобытий.ert" {}

    Здесь ИмяКласса — это имя, под которым класс будет доступен в коде 1С, а путь_к_файлу\КлассПерехватСобытий.ert — это полный или относительный путь к файлу обработки.

  2. Относительные пути: Часто удобно использовать относительные пути, особенно если файлы классов хранятся в подкаталоге базы данных. Например, если файл КлассПерехватСобытий.ert находится в подпапке Classes относительно каталога базы, запись будет такой: class КлассПерехватСобытий = ".\Classes\КлассПерехватСобытий.ert" {}

    Обратите внимание, что . (точка) обозначает текущий каталог (каталог базы данных 1С).

  3. Использование @MD: Если же мы хотим, чтобы 1С++ принудительно искал обработку внутри конфигурации (метаданных), мы используем ключевое слово @MD: class КлассПерехватСобытий = КлассПерехватСобытий@MD {}

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

Влияние TurboMD на классы 1С++: особенности обновления

Возможно, вы замечали, что изменения, внесенные во внешний файл класса 1С++, не всегда сразу подхватываются системой, даже если используется TurboMD. Давайте выясним причину этого поведения:

  1. Механизм работы 1С++: При первом обращении к любому классу 1С++, система считывает его код (из .ert файла или из метаданных), компилирует его во внутреннее представление и затем кэширует в памяти. Это означает, что после первой загрузки класса, 1С++ использует уже скомпилированную версию.
  2. Отличие от TurboMD: TurboMD предназначен для динамической подмены модулей штатных объектов 1С (форм, документов, отчетов) "на лету" без перезапуска конфигурации. Однако он не контролирует внутренний кэш классов 1С++. Даже если TurboMD обнаруживает изменение во внешнем файле класса, 1С++ продолжает использовать уже загруженную и скомпилированную версию из своего кэша.
  3. Обновление кода класса: Для того чтобы изменения в коде класса 1С++ вступили в силу, как правило, требуется перезапуск приложения "1С:Предприятие". Существуют некоторые сборки 1С++, которые предоставляют специальные команды для сброса кэша классов или вызова метода перекомпиляции, но это не является универсальным решением и требует специфической настройки. В большинстве случаев самый надежный способ увидеть изменения — это полностью перезапустить сеанс работы с 1С.

Расширенный перехват событий с помощью FormEx

Если ваша задача состоит в более сложном управлении правами или перехвате низкоуровневых событий формы (например, нажатия клавиш, изменение размеров, программное закрытие формы), одного 1С++ может быть недостаточно. Часто в таких случаях используется внешняя компонента FormEx.

  1. Дополнительные возможности: FormEx расширяет возможности 1С 7.7 по работе с формами, позволяя перехватывать гораздо более широкий спектр событий, которые недоступны стандартными средствами или даже базовыми возможностями 1С++.
  2. Пример использования для прав: В случае, описанном автором, где нужно программно ограничить права на запись документа в зависимости от наличия связанных документов, FormEx может предложить более гибкие инструменты. Например, мы можем перехватить событие ПриЗаписи (или событие нажатия кнопки "Записать") еще до того, как сработает стандартный механизм записи 1С, провести свои проверки и, при необходимости, отменить запись или переключить форму в режим "только просмотр".
  3. Типизация контекста: При передаче контекста формы в класс 1С++, мы можем использовать типизацию (например, типизировать его как Form), что может упростить доступ к атрибутам формы через точку и сделать код более читаемым, хотя это не решает проблему "пустых значений" в ПриОткрытии для вычисляемых полей.

Резюме: Шаги для решения

Для успешного решения описанных проблем и эффективной работы с перехватчиками событий 1С++ в 1С 7.7, мы рекомендуем выполнить следующие шаги:

  1. Для чтения вычисляемых полей:
    • Используем ПослеОткрытия: Перенесите логику чтения значений вычисляемых полей в событие ПослеОткрытия (СобытиеГМ_ПослеОткрытия), которое срабатывает после полной инициализации и отрисовки формы.
    • Альтернатива с Шаблон: Если перенос в ПослеОткрытия невозможен, используйте метод контекста Конт.Шаблон("[ИмяФункции()]") для принудительного вызова функции, которая вычисляет значение поля, получая результат напрямую.
  2. Для подключения внешних классов:
    • Проверьте defcls.ert: Убедитесь, что синтаксис для подключения внешнего файла класса .ert корректен. Используйте запись вида class ИмяКласса = ".\Classes\КлассПерехватСобытий.ert" {}, избегая привязки к @MD, если вы хотите использовать внешний файл.
  3. При обновлении классов 1С++:
    • Перезапускайте 1С: Помните, что для применения изменений в коде классов 1С++ после их редактирования, обычно требуется полный перезапуск приложения "1С:Предприятие" из-за механизма кэширования классов.

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

← На главную