Ввод в ячейке: временно заменить обработчик и восстановить сумму 200 → 300 → 200

Учебные задачи

Собственное числовое поле внутри табличного документа передает настоящее событие в модуль обычной формы. Сохраненное действие восстанавливается как native объект, без выдуманного имени свойства или прямого вызова обработчика.

Где и как запускать

Весь MAIN — обычная общая форма Калькулятор85 своей файловой CF, толстый обычный клиент Windows 8.5.1.1343. Только свои данные формы и сохранение native действия в памяти; записи в базу, файловых операций и сети нет.

Подготовка

  1. Все дальнейшие действия выполняйте самостоятельно только в новом пустом учебном стенде. Здесь 1С не запускалась, база и макет не создавались. Платформа Windows 8.5.1.1343; новая файловая база по точному пути C:\1c-labs\OwnSpreadsheetControls3_9e7df813-560a-49c0-9130-374c9f727bef\IB. Имя конфигурации УчебныйВводЯчейки85, ОсновнойРежимЗапуска=ОбычноеПриложение, РежимСовместимости=НеИспользовать.

  2. В Конфигураторе откройте Сервис → Параметры → Общие → Редактирование конфигурации для режима запуска и выберите Оба, чтобы редактор позволял создать обычную форму. Это настройка редактора, не свойство SupportedRunModes конфигурации. ОсновнойРежимЗапуска самой конфигурации оставьте ОбычноеПриложение. Создайте одну собственную полную роль с правом Администрирование и одного пользователя Ячейка85Ученик с этой ролью; ПолучитьПользователей требует административных прав.

  3. В дереве конфигурации Общие → ОбщиеМакеты создайте Калькулятор85 типа ТабличныйДокумент. В редакторе заполните только диапазон R1C1:R4C3 по видимой JSON-спецификации ниже. Строка1: Количество, Цена, Сумма. Выделите R2C1, откройте палитру свойств и в ее верхнем списке выберите Ячейки. Установите СодержитЗначение=Да, ТипЗначения=Число(8,0), Значение=2, Защита=Нет. Аналогично задайте R2C2 значение100 и R2C3 значение200 с тем же числовым типом, СодержитЗначение=Да и Защита=Да. Это настоящие числовые значения ячеек, не текст «2», «100», «200».

  4. В R4C1 поместите текст метки 9e7df813-560a-49c0-9130-374c9f727bef. Всем другим ячейкам прямоугольника задайте Защита=Да. Встроенный редактор типизированной ячейки предоставляет платформа: отдельный пункт настройки «редактора» или ручное создание ПолеВвода не требуется. Установленная методика Редактор табличных документов, раздел Ввод текста в ячейку табличного документа, размещенного в форме, описывает штатный ввод чисел с клавиатуры и калькулятор F4; свойства ячейки задаются через палитру Ячейки. Конкретный объект код получает только через readonly ЭлементУправления этой ячейки и проверяет ПолеВвода и ВстроенВЯчейку=Истина. Если фактический объект не соответствует, урок откажется: повторно проверьте три числовых свойства R2C1 и ее защиту, не подменяйте проверку самодельным элементом. Здесь получение редактора в реальной форме не запускалось.

  5. В Общие → ОбщиеФормы создайте Калькулятор85 типа Обычная. Реквизиты формы: Документ85 типа ТабличныйДокумент и Сообщение85 типа Строка неограниченной длины. Разместите ПолеДокумента85 типа ПолеТабличногоДокумента с Данные=Документ85 и поле сообщения только для просмотра. Поле документа связано с собственным реквизитом, не с неизвестными ТекущиеДанные или объектом рабочей базы.

  6. Вставьте MAIN целиком в модуль этой обычной формы и назначьте ПриОткрытии одноименной процедуре без параметров. ОбычныйВвод85 и ВременныйВвод85 — экспортные процедуры этого же модуля; они получают один Элемент благодаря специализированному native событию event116. Не назначайте их кнопке формы, не меняйте подпись на пустую и не вызывайте их вручную.

  7. В модуль обычного приложения вставьте APPLICATION целиком. ПриНачалеРаботыСистемы без параметров получает свою общую форму и открывает ее без владельца. Общие модули, справочники, документы, отчеты, обработки и регистры в этом стенде не нужны. Обновите только свою конфигурацию базы и запустите ее толстым обычным клиентом Windows своим пользователем.

  8. При открытии код получает готовый общий макет и проверяет заголовки, метку, типы чисел и защиту. Он включает ТолькоПросмотр=Истина, но общую Защита документа оставляет Ложь: полная защита запрещает редактирование всей таблицы. В режиме просмотра редактируется только незащищенная ячейка количества, остальные числовые ячейки защищены. Контроль кода также отказывает при изменении цены, суммы, заголовков или метки.

  9. Самостоятельно введите3 вместо2 в R2C1, затем завершите ввод переходом в другую ячейку. Временный обработчик должен показать сумму300 и восстановить сохраненное настоящее действие. Введите2 вместо3 и снова завершите ввод: исходный обработчик должен показать200 и завершение. Программное присваивание не вызывает ПриИзменении и здесь не используется для имитации этих событий.

Перем Остановка85, Фаза85, СохраненноеДействие85;

Процедура ПроверитьСвойСтенд85()
    Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnSpreadsheetControls3_9e7df813-560a-49c0-9130-374c9f727bef\IB"";" Тогда
        ВызватьИсключение "Нужна только собственная учебная файловая база 8.5.1.1343";
    КонецЕсли;
    Если Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("Калькулятор85") = Неопределено
        Или Метаданные.ОбщиеМакеты.Количество() <> 1 Или Метаданные.ОбщиеМакеты.Найти("Калькулятор85") = Неопределено
        Или Метаданные.Роли.Количество() <> 1 Или Метаданные.Справочники.Количество() <> 0
        Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Обработки.Количество() <> 0
        Или Метаданные.Отчеты.Количество() <> 0 Или Метаданные.ОбщиеМодули.Количество() <> 0 Тогда
        ВызватьИсключение "Проверьте свою пустую конфигурацию с одной формой, общим макетом и ролью";
    КонецЕсли;
    Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
    Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
    Если Текущий = Неопределено Или Пользователи.Количество() <> 1 Тогда
        ВызватьИсключение "В стенде должен быть один собственный учебный пользователь";
    КонецЕсли;
    Если Текущий.Имя <> "Ячейка85Ученик" Или Текущий.УникальныйИдентификатор <> Пользователи[0].УникальныйИдентификатор Тогда
        ВызватьИсключение "Сеанс принадлежит другому пользователю";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьЯчейку85(Документ, Колонка, ОжидаемоеЗначение)
    Ячейка = Документ.Область(2, Колонка);
    Если Не Ячейка.СодержитЗначение Или Ячейка.ТипЗначения.Типы().Количество() <> 1
        Или Не Ячейка.ТипЗначения.СодержитТип(Тип("Число"))
        Или ТипЗнч(Ячейка.Значение) <> Тип("Число") Или Ячейка.Значение <> ОжидаемоеЗначение
        Или Ячейка.Защита <> (Колонка <> 1) Тогда
        ВызватьИсключение "Изменились тип, число или защита собственной ячейки";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьДокумент85(Документ, Количество, Сумма)
    Если Документ.ВысотаТаблицы <> 4 Или Документ.ШиринаТаблицы <> 3
        Или Документ.Область(1, 1).Текст <> "Количество"
        Или Документ.Область(1, 2).Текст <> "Цена"
        Или Документ.Область(1, 3).Текст <> "Сумма"
        Или Документ.Область(4, 1).Текст <> "9e7df813-560a-49c0-9130-374c9f727bef" Тогда
        ВызватьИсключение "Изменились размер, заголовки или метка собственного макета";
    КонецЕсли;
    ПроверитьЯчейку85(Документ, 1, Количество);
    ПроверитьЯчейку85(Документ, 2, 100);
    ПроверитьЯчейку85(Документ, 3, Сумма);
КонецПроцедуры

Функция ПолучитьСвойВвод85()
    Поле = ЭлементыФормы.ПолеДокумента85;
    Если ТипЗнч(Поле) <> Тип("ПолеТабличногоДокумента") Или Поле.Данные <> "Документ85"
        Или Не Поле.ТолькоПросмотр Или Поле.Защита Тогда
        ВызватьИсключение "Нужны свое поле, режим просмотра и выключенная общая защита документа";
    КонецЕсли;
    Ячейка = Поле.Область(2, 1);
    Если Не Ячейка.СодержитЗначение Или Ячейка.Защита Тогда
        ВызватьИсключение "Редактируется только собственная числовая ячейка количества";
    КонецЕсли;
    Ввод = Ячейка.ЭлементУправления;
    Если ТипЗнч(Ввод) <> Тип("ПолеВвода") Или Не Ввод.ВстроенВЯчейку Тогда
        ВызватьИсключение "Макет должен содержать настоящее встроенное поле ввода количества";
    КонецЕсли;
    Возврат Ввод;
КонецФункции

Процедура ПриОткрытии()
    Если Остановка85 <> Неопределено Или Фаза85 <> Неопределено Тогда
        Остановка85 = Истина;
        ВызватьИсключение "Повторная подготовка не снимает остановку этой формы";
    КонецЕсли;
    Остановка85 = Ложь;
    Попытка
#Если ТолстыйКлиентОбычноеПриложение Тогда
        ПроверитьСвойСтенд85();
        Если ВладелецФормы <> Неопределено Тогда
            ВызватьИсключение "Открывайте самостоятельную собственную общую форму без чужого владельца";
        КонецЕсли;
        Кандидат = ПолучитьОбщийМакет("Калькулятор85");
        Если ТипЗнч(Кандидат) <> Тип("ТабличныйДокумент") Тогда
            ВызватьИсключение "Нужен заранее подготовленный собственный табличный макет";
        КонецЕсли;
        ПроверитьДокумент85(Кандидат, 2, 200);
        Кандидат.ТолькоПросмотр = Истина;
        Кандидат.Защита = Ложь;
        Документ85 = Кандидат;
        Поле = ЭлементыФормы.ПолеДокумента85;
        ПроверитьДокумент85(Документ85, 2, 200);
        ПроверитьДокумент85(Поле, 2, 200);
        Ввод = ПолучитьСвойВвод85();
        Если ТипЗнч(Ввод.Значение) <> Тип("Число") Или Ввод.Значение <> 2 Тогда
            ВызватьИсключение "Встроенное поле должно показывать собственное число 2";
        КонецЕсли;
        Ввод.УстановитьДействие("ПриИзменении", Новый Действие("ОбычныйВвод85"));
        СохраненноеДействие85 = Ввод.ПолучитьДействие("ПриИзменении");
        Если ТипЗнч(СохраненноеДействие85) <> Тип("Действие") Тогда
            ВызватьИсключение "Нужно сохранить настоящее действие собственного элемента";
        КонецЕсли;
        Ввод.УстановитьДействие("ПриИзменении", Новый Действие("ВременныйВвод85"));
        Если ТипЗнч(Ввод.ПолучитьДействие("ПриИзменении")) <> Тип("Действие") Тогда
            ВызватьИсключение "Встроенный элемент не вернул назначенное действие";
        КонецЕсли;
        Фаза85 = 0;
        Сообщение85 = "Сумма 200. Измените количество с 2 на 3 и завершите ввод переходом в другую ячейку.";
#Иначе
        ВызватьИсключение "Этот пример работает только в толстом обычном клиенте";
#КонецЕсли
    Исключение
        Остановка85 = Истина;
        Сообщение85 = "СТОП: подготовка не завершена. Закройте форму и исправьте свой стенд.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

Процедура ПроверитьСобытие85(Элемент, ОжидаемаяФаза, Количество, ПредыдущаяСумма)
    Если Остановка85 Или Фаза85 <> ОжидаемаяФаза Тогда
        ВызватьИсключение "Форма остановлена либо это другой этап ввода";
    КонецЕсли;
    ПроверитьСвойСтенд85();
    Ввод = ПолучитьСвойВвод85();
    Если ТипЗнч(Элемент) <> Тип("ПолеВвода") Или Элемент <> Ввод
        Или ТипЗнч(Элемент.Значение) <> Тип("Число") Или Элемент.Значение <> Количество Тогда
        ВызватьИсключение "Событие должно прийти от своего ввода с ожидаемым числом";
    КонецЕсли;
    Если ТипЗнч(СохраненноеДействие85) <> Тип("Действие") Тогда
        ВызватьИсключение "Исходное собственное действие потеряно";
    КонецЕсли;
    ПроверитьДокумент85(Документ85, Количество, ПредыдущаяСумма);
    ПроверитьДокумент85(ЭлементыФормы.ПолеДокумента85, Количество, ПредыдущаяСумма);
КонецПроцедуры

Процедура ВременныйВвод85(Элемент) Экспорт
    Попытка
        ПроверитьСобытие85(Элемент, 0, 3, 200);
        Количество = Документ85.Область(2, 1).Значение;
        Цена = Документ85.Область(2, 2).Значение;
        Документ85.Область(2, 3).Значение = Количество * Цена;
        ПроверитьДокумент85(Документ85, 3, 300);
        ПроверитьДокумент85(ЭлементыФормы.ПолеДокумента85, 3, 300);
        Ввод = ПолучитьСвойВвод85();
        Ввод.УстановитьДействие("ПриИзменении", СохраненноеДействие85);
        Если ТипЗнч(Ввод.ПолучитьДействие("ПриИзменении")) <> Тип("Действие") Тогда
            ВызватьИсключение "Не удалось прочитать восстановленное собственное действие";
        КонецЕсли;
        Фаза85 = 1;
        Сообщение85 = "Временный обработчик: 3 × 100 = 300. Восстановлено исходное действие. Введите 2 и завершите ввод.";
    Исключение
        Остановка85 = Истина;
        Сообщение85 = "СТОП: исход неизвестен; сумма или действие могли измениться частично. Закройте форму.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

Процедура ОбычныйВвод85(Элемент) Экспорт
    Попытка
        ПроверитьСобытие85(Элемент, 1, 2, 300);
        Количество = Документ85.Область(2, 1).Значение;
        Цена = Документ85.Область(2, 2).Значение;
        Документ85.Область(2, 3).Значение = Количество * Цена;
        ПроверитьДокумент85(Документ85, 2, 200);
        ПроверитьДокумент85(ЭлементыФормы.ПолеДокумента85, 2, 200);
        Фаза85 = 2;
        Сообщение85 = "Исходный обработчик: 2 × 100 = 200. Учебная цепочка завершена; для повторения откройте новую форму.";
    Исключение
        Остановка85 = Истина;
        Сообщение85 = "СТОП: восстановленная цепочка не завершена; частичные изменения автоматически не откатываются.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры
Модуль обычного приложения: открыть свою форму

Своя CF → Модуль обычного приложения целиком

Только толстый обычный клиент; ПриНачалеРаботыСистемы без параметров открывает свою общую форму без владельца.

Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентОбычноеПриложение Тогда
    Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnSpreadsheetControls3_9e7df813-560a-49c0-9130-374c9f727bef\IB"";" Тогда
        ВызватьИсключение "Запускайте только свой отдельный файловый стенд толстым обычным клиентом";
    КонецЕсли;
    Форма = ПолучитьОбщуюФорму("Калькулятор85");
    Если Форма.ВладелецФормы <> Неопределено Тогда
        ВызватьИсключение "Владелец собственной общей формы должен отсутствовать";
    КонецЕсли;
    Форма.Открыть();
#Иначе
    ВызватьИсключение "Урок предназначен только для толстого обычного клиента";
#КонецЕсли
КонецПроцедуры
Спецификация ручной подготовки собственного макета, не импорт CF

Своя CF → ОбщиеМакеты → Калькулятор85 → редактор табличного документа

Видимые данные для ручного заполнения и настройки собственных ячеек; JSON не импортируется в 1С.

{
  "asset": "ОбщиеМакеты.Калькулятор85",
  "type": "ТабличныйДокумент",
  "height": 4,
  "width": 3,
  "headers": [
    [
      1,
      1,
      "Количество"
    ],
    [
      1,
      2,
      "Цена"
    ],
    [
      1,
      3,
      "Сумма"
    ]
  ],
  "numericCells": [
    {
      "row": 2,
      "column": 1,
      "value": 2,
      "containsValue": true,
      "valueType": "Число(8,0)",
      "protection": false,
      "control": "ПолеВвода"
    },
    {
      "row": 2,
      "column": 2,
      "value": 100,
      "containsValue": true,
      "valueType": "Число(8,0)",
      "protection": true
    },
    {
      "row": 2,
      "column": 3,
      "value": 200,
      "containsValue": true,
      "valueType": "Число(8,0)",
      "protection": true
    }
  ],
  "marker": [
    4,
    1,
    "9e7df813-560a-49c0-9130-374c9f727bef"
  ],
  "allOtherCellsProtected": true,
  "databaseRecords": []
}

Разбор по шагам

  1. ПолучитьОбщийМакет получает заранее подготовленный собственный макет; проверяются все три числа, заголовки, размеры, метка и защита. После размещения в поле формы те же данные перечитываются через реквизит и визуальное поле.

  2. Readonly ЭлементУправления собственной числовой ячейки возвращает настоящее поле ввода. Оно проверяется как ПолеВвода и как встроенное в ячейку; экземпляр расширения отдельно не создается.

  3. Сначала назначается собственный исходный экспортный обработчик ОбычныйВвод85. ПолучитьДействие возвращает его настоящее Действие; объект сохраняется для восстановления. Затем УстановитьДействие назначает ВременныйВвод85.

  4. Пользователь завершает ввод3. Native ПриИзменении без собственных формалов вместе с event116 передает один Элемент. Проверяются фактический источник, этап0, число3, прежняя сумма200 и неизменная цена100. Рассчитывается3 × 100 =300; обе поверхности документа перечитываются.

  5. Временный обработчик передает сохраненный opaque объект Действие обратно УстановитьДействие. Никаких сравнений новых Действий, сериализации или угадывания ИмяПроцедуры нет. Сам readback типа не доказывает доставку события; ее проверяет следующий реальный пользовательский ввод.

  6. Завершение ввода2 вызывает исходный восстановленный обработчик. Он принимает только этап1, собственный Элемент, количество2 и прежнюю сумму300, вычисляет200, перечитывает документ и завершает этап2. Повторный или чужой ввод отказывается.

Как проверить результат

  1. В своем стенде вручную проверьте исходные2,100,200; после завершения ввода3 должны быть3,100,300, после ввода2 —2,100,200. Это ожидаемые результаты авторского примера, не выполненный здесь runtime-тест.

  2. Проверяйте сообщения Временный обработчик и Исходный обработчик: только второй реальный callback подтверждает, что сохраненное действие восстановлено. Не вызывайте эти процедуры из отладчика или собственного тестового кода.

  3. Попробуйте завершить ввод другого числа или повторить цепочку без новой формы: код должен остановиться. Закройте форму и откройте новую после исправления подготовки. Ошибка не сбрасывает Остановка85.

Ожидаемый результат

Новичок видит разницу между значением ячейки, настоящим встроенным полем и объектом Действие. Цена100 сохраняется, две настоящие пользовательские операции дают200 → 300 → 200, а исходный обработчик восстанавливается полученным у своего элемента объектом.

На что обратить внимание

  1. Расширение встроенного элемента — не отдельный объект с конструктором. Оно доступно у native ЭлементУправления ячейки или рисунка. Здесь используется только собственная прямоугольная числовая ячейка; getter readonly и требует СодержитЗначение=Истина.

  2. Для поля внутри табличного документа первый параметр Элемент доказывает специализированный event116, не событие обычной кнопки и не managed event525. Base ПриИзменении имеет0 формалов, итоговый экспортный обработчик в форме —1. Тонкий клиент не подходит.

  3. ПриИзменении возникает после завершенного пользовательского изменения, но не после программного назначения значения. Код не симулирует native событие ручным вызовом.

  4. ТолькоПросмотр и общая Защита документа различаются. ТолькоПросмотр учитывает защиту отдельных ячеек; полная Защита запрещает редактирование всей таблицы. Защита ячеек не является правами доступа или гарантией неизменности базы.

  5. Исходное Действие сохраняется и возвращается native setter. У этого opaque объекта не придумывается ИмяПроцедуры, не требуется строковое представление и не утверждается равенство вновь созданным Действиям.

  6. Изменение суммы и обработчика не обещается атомарным и автоматически не откатывается. При ошибке форма остается остановленной; собственные частичные изменения существуют только в ее памяти. Здесь не создавалась база, не компилировался макет и не запускалась1С.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.