Собственное числовое поле внутри табличного документа передает настоящее событие в модуль обычной формы. Сохраненное действие восстанавливается как native объект, без выдуманного имени свойства или прямого вызова обработчика.
Где и как запускать
Весь MAIN — обычная общая форма Калькулятор85 своей файловой CF, толстый обычный клиент Windows 8.5.1.1343. Только свои данные формы и сохранение native действия в памяти; записи в базу, файловых операций и сети нет.
Подготовка
Все дальнейшие действия выполняйте самостоятельно только в новом пустом учебном стенде. Здесь 1С не запускалась, база и макет не создавались. Платформа Windows 8.5.1.1343; новая файловая база по точному пути C:\1c-labs\OwnSpreadsheetControls3_9e7df813-560a-49c0-9130-374c9f727bef\IB. Имя конфигурации УчебныйВводЯчейки85, ОсновнойРежимЗапуска=ОбычноеПриложение, РежимСовместимости=НеИспользовать.
В Конфигураторе откройте Сервис → Параметры → Общие → Редактирование конфигурации для режима запуска и выберите Оба, чтобы редактор позволял создать обычную форму. Это настройка редактора, не свойство SupportedRunModes конфигурации. ОсновнойРежимЗапуска самой конфигурации оставьте ОбычноеПриложение. Создайте одну собственную полную роль с правом Администрирование и одного пользователя Ячейка85Ученик с этой ролью; ПолучитьПользователей требует административных прав.
В дереве конфигурации Общие → ОбщиеМакеты создайте Калькулятор85 типа ТабличныйДокумент. В редакторе заполните только диапазон R1C1:R4C3 по видимой JSON-спецификации ниже. Строка1: Количество, Цена, Сумма. Выделите R2C1, откройте палитру свойств и в ее верхнем списке выберите Ячейки. Установите СодержитЗначение=Да, ТипЗначения=Число(8,0), Значение=2, Защита=Нет. Аналогично задайте R2C2 значение100 и R2C3 значение200 с тем же числовым типом, СодержитЗначение=Да и Защита=Да. Это настоящие числовые значения ячеек, не текст «2», «100», «200».
В R4C1 поместите текст метки 9e7df813-560a-49c0-9130-374c9f727bef. Всем другим ячейкам прямоугольника задайте Защита=Да. Встроенный редактор типизированной ячейки предоставляет платформа: отдельный пункт настройки «редактора» или ручное создание ПолеВвода не требуется. Установленная методика Редактор табличных документов, раздел Ввод текста в ячейку табличного документа, размещенного в форме, описывает штатный ввод чисел с клавиатуры и калькулятор F4; свойства ячейки задаются через палитру Ячейки. Конкретный объект код получает только через readonly ЭлементУправления этой ячейки и проверяет ПолеВвода и ВстроенВЯчейку=Истина. Если фактический объект не соответствует, урок откажется: повторно проверьте три числовых свойства R2C1 и ее защиту, не подменяйте проверку самодельным элементом. Здесь получение редактора в реальной форме не запускалось.
В Общие → ОбщиеФормы создайте Калькулятор85 типа Обычная. Реквизиты формы: Документ85 типа ТабличныйДокумент и Сообщение85 типа Строка неограниченной длины. Разместите ПолеДокумента85 типа ПолеТабличногоДокумента с Данные=Документ85 и поле сообщения только для просмотра. Поле документа связано с собственным реквизитом, не с неизвестными ТекущиеДанные или объектом рабочей базы.
Вставьте MAIN целиком в модуль этой обычной формы и назначьте ПриОткрытии одноименной процедуре без параметров. ОбычныйВвод85 и ВременныйВвод85 — экспортные процедуры этого же модуля; они получают один Элемент благодаря специализированному native событию event116. Не назначайте их кнопке формы, не меняйте подпись на пустую и не вызывайте их вручную.
В модуль обычного приложения вставьте APPLICATION целиком. ПриНачалеРаботыСистемы без параметров получает свою общую форму и открывает ее без владельца. Общие модули, справочники, документы, отчеты, обработки и регистры в этом стенде не нужны. Обновите только свою конфигурацию базы и запустите ее толстым обычным клиентом Windows своим пользователем.
При открытии код получает готовый общий макет и проверяет заголовки, метку, типы чисел и защиту. Он включает ТолькоПросмотр=Истина, но общую Защита документа оставляет Ложь: полная защита запрещает редактирование всей таблицы. В режиме просмотра редактируется только незащищенная ячейка количества, остальные числовые ячейки защищены. Контроль кода также отказывает при изменении цены, суммы, заголовков или метки.
Самостоятельно введите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": []
}Разбор по шагам
ПолучитьОбщийМакет получает заранее подготовленный собственный макет; проверяются все три числа, заголовки, размеры, метка и защита. После размещения в поле формы те же данные перечитываются через реквизит и визуальное поле.
Readonly ЭлементУправления собственной числовой ячейки возвращает настоящее поле ввода. Оно проверяется как ПолеВвода и как встроенное в ячейку; экземпляр расширения отдельно не создается.
Сначала назначается собственный исходный экспортный обработчик ОбычныйВвод85. ПолучитьДействие возвращает его настоящее Действие; объект сохраняется для восстановления. Затем УстановитьДействие назначает ВременныйВвод85.
Пользователь завершает ввод3. Native ПриИзменении без собственных формалов вместе с event116 передает один Элемент. Проверяются фактический источник, этап0, число3, прежняя сумма200 и неизменная цена100. Рассчитывается3 × 100 =300; обе поверхности документа перечитываются.
Временный обработчик передает сохраненный opaque объект Действие обратно УстановитьДействие. Никаких сравнений новых Действий, сериализации или угадывания ИмяПроцедуры нет. Сам readback типа не доказывает доставку события; ее проверяет следующий реальный пользовательский ввод.
Завершение ввода2 вызывает исходный восстановленный обработчик. Он принимает только этап1, собственный Элемент, количество2 и прежнюю сумму300, вычисляет200, перечитывает документ и завершает этап2. Повторный или чужой ввод отказывается.
Как проверить результат
В своем стенде вручную проверьте исходные2,100,200; после завершения ввода3 должны быть3,100,300, после ввода2 —2,100,200. Это ожидаемые результаты авторского примера, не выполненный здесь runtime-тест.
Проверяйте сообщения Временный обработчик и Исходный обработчик: только второй реальный callback подтверждает, что сохраненное действие восстановлено. Не вызывайте эти процедуры из отладчика или собственного тестового кода.
Попробуйте завершить ввод другого числа или повторить цепочку без новой формы: код должен остановиться. Закройте форму и откройте новую после исправления подготовки. Ошибка не сбрасывает Остановка85.
Ожидаемый результат
Новичок видит разницу между значением ячейки, настоящим встроенным полем и объектом Действие. Цена100 сохраняется, две настоящие пользовательские операции дают200 → 300 → 200, а исходный обработчик восстанавливается полученным у своего элемента объектом.
На что обратить внимание
Расширение встроенного элемента — не отдельный объект с конструктором. Оно доступно у native ЭлементУправления ячейки или рисунка. Здесь используется только собственная прямоугольная числовая ячейка; getter readonly и требует СодержитЗначение=Истина.
Для поля внутри табличного документа первый параметр Элемент доказывает специализированный event116, не событие обычной кнопки и не managed event525. Base ПриИзменении имеет0 формалов, итоговый экспортный обработчик в форме —1. Тонкий клиент не подходит.
ПриИзменении возникает после завершенного пользовательского изменения, но не после программного назначения значения. Код не симулирует native событие ручным вызовом.
ТолькоПросмотр и общая Защита документа различаются. ТолькоПросмотр учитывает защиту отдельных ячеек; полная Защита запрещает редактирование всей таблицы. Защита ячеек не является правами доступа или гарантией неизменности базы.
Исходное Действие сохраняется и возвращается native setter. У этого opaque объекта не придумывается ИмяПроцедуры, не требуется строковое представление и не утверждается равенство вновь созданным Действиям.
Изменение суммы и обработчика не обещается атомарным и автоматически не откатывается. При ошибке форма остается остановленной; собственные частичные изменения существуют только в ее памяти. Здесь не создавалась база, не компилировался макет и не запускалась1С.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.