Как получить состояние элемента планировщика до его изменения или перетаскивания в 1С

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

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

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

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

Постановка проблемы на конкретном примере

Представим ситуацию: в планировщике отображается задача, запланированная на 01.10. Пользователь перетаскивает ее на 20.10. Нам нужно внести изменения в базу данных.

Если мы попытаемся перехватить событие окончания редактирования следующим образом:


&НаКлиенте
Процедура ПланировщикПриОкончанииРедактирования(Элемент, НовыйЭлемент, ОтменаРедактирования)
    // На этот момент пользователь уже передвинул элемент
    ДатаПлан = Элемент.ВыделенныеЭлементы[0].Начало; // Тут дата уже равна 20.10!
КонецПроцедуры

В этот момент старая дата (01.10) уже стерта из свойств элемента на клиенте. Чтобы найти исходную запись в регистре сведений, нам нужно как-то сохранить предыдущее состояние. Рассмотрим, как это сделать наиболее эффективно.

Способ 1. Хранение исходных данных в структуре свойства «Значение» (Рекомендуемый)

Каждый ЭлементПланировщика имеет свойство Значение, которое может принимать произвольный тип. Часто разработчики записывают туда только ссылку на документ или задачу (например, ссылку на Заявку). Однако, если у вас к одной заявке привязано несколько разных временных интервалов, их невозможно будет уникально идентифицировать только по ссылке.

Решением является передача в свойство Значение не одиночной ссылки, а структуры с исходными данными.

Шаг 1. Заполнение планировщика на сервере

При формировании элементов планировщика упакуем исходные даты и уникальный идентификатор во вспомогательную структуру:


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

Шаг 2. Получение старых и новых данных при редактировании

Когда пользователь перемещает элемент, платформа меняет свойства Начало и Конец самого элемента планировщика, но содержимое объекта Значение (наша структура) остается неизменным!


&НаКлиенте
Процедура ПланировщикПриОкончанииРедактирования(Элемент, НовыйЭлемент, ОтменаРедактирования)
    
    Если ОтменаРедактирования Или НовыйЭлемент Тогда 
        Возврат; 
    КонецЕсли;
    
    Если Элемент.ВыделенныеЭлементы.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    ТекущийЭлемент = Элемент.ВыделенныеЭлементы[0];
    СтруктураДанных = ТекущийЭлемент.Значение;
    
    // Проверяем, что в Значении действительно лежит наша структура
    Если ТипЗнч(СтруктураДанных) = Тип("Структура") Тогда
        
        // Получаем исходные («старые») данные из структуры
        СтароеНачало = СтруктураДанных.ИсходноеНачало;
        СтарыйКонец  = СтруктураДанных.ИсходныйКонец;
        СсылкаНаОбъект = СтруктураДанных.Ссылка;
        УникальныйИД = СтруктураДанных.УникальныйИдентификатор;
        
        // Получаем новые измененные данные напрямую из элемента
        НовоеНачало  = ТекущийЭлемент.Начало;
        НовыйКонец   = ТекущийЭлемент.Конец;
        
        // Вызываем серверный метод для перезаписи регистра сведений
        ЗаписатьИзмененияВРегистрНаСервере(СсылкаНаОбъект, СтароеНачало, НовоеНачало, НовыйКонец, УникальныйИД);
        
        // КРИТИЧЕСКИ ВАЖНО: обновляем структуру "на лету", 
        // чтобы при последующем переносе этого же элемента старые даты были актуальными
        СтруктураДанных.ИсходноеНачало = НовоеНачало;
        СтруктураДанных.ИсходныйКонец  = НовыйКонец;
        
    КонецЕсли;
    
КонецПроцедуры

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

Способ 2. Отслеживание через событие «ПроверкаПеретаскиванияВнутри»

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

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

Разберем по шагам, как настроить кэширование через это событие:

Шаг 1. Объявление переменной модуля формы

В самом верху модуля управляемой формы определим переменную, которая будет хранить информацию о перетаскиваемом элементе:


&НаКлиенте
Перем ТекущийПеретаскиваемыйЭлемент;

Шаг 2. Перехват события «ПроверкаПеретаскиванияВнутри»

В свойствах элемента формы «Планировщик» создаем обработчик события ПроверкаПеретаскиванияВнутри:


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

Шаг 3. Обработка сохраненных данных при завершении операции

Теперь в процедуре окончания редактирования мы можем обратиться к нашей переменной модуля формы:


&НаКлиенте
Процедура ПланировщикПриОкончанииРедактирования(Элемент, НовыйЭлемент, ОтменаРедактирования)
    
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;
    
    Если ТекущийПеретаскиваемыйЭлемент <> Неопределено Тогда
        
        СтараяДата = ТекущийПеретаскиваемыйЭлемент.СтароеНачало;
        НоваяДата  = Элемент.ВыделенныеЭлементы[0].Начало;
        ЗначениеЭлемента = ТекущийПеретаскиваемыйЭлемент.Значение;
        
        // Выполняем логику обновления данных в БД
        ОбновитьПериодВБазеДанных(ЗначениеЭлемента, СтараяДата, НоваяДата);
        
        // Очищаем кэш переменной
        ТекущийПеретаскиваемыйЭлемент = Неопределено;
        
    КонецЕсли;
    
КонецПроцедуры

Способ 3. Использование события «ПриАктивизации» для временного кэширования

Этот способ наиболее прост в реализации и базируется на логике, что перед тем как перетащить элемент или изменить его границы, пользователь обязательно должен его выделить (активизировать).

Мы можем запоминать состояние элемента в момент его активизации:

Шаг 1. Создание кэш-переменных модуля


&НаКлиенте
Перем КэшДатаНачала;
Перем КэшДатаОкончания;
Перем КэшЗначение;

Шаг 2. Заполнение кэша при активизации элемента

Добавим обработчик события планировщика ПриАктивизацииИлиВыделении (или ПриАктивизации в зависимости от версии платформы):


&НаКлиенте
Процедура ПланировщикПриАктивизации(Элемент)
    
    Если Элемент.ВыделенныеЭлементы.Количество() > 0 Тогда
        АктивныйЭлемент = Элемент.ВыделенныеЭлементы[0];
        
        // Сохраняем состояние элемента в кэш-переменные
        КэшДатаНачала    = АктивныйЭлемент.Начало;
        КэшДатаОкончания = АктивныйЭлемент.Конец;
        КэшЗначение      = АктивныйЭлемент.Значение;
    КонецЕсли;
    
КонецПроцедуры

При окончании редактирования мы сравниваем измененные значения элемента со значениями из кэш-переменных КэшДатаНачала и КэшДатаОкончания.

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

Сравнительный анализ методов

Для того чтобы выбрать оптимальный инструмент под ваши задачи, проанализируем сильные и слабые стороны каждого решения:

  1. Способ 1 (Структура в свойстве «Значение»):
    • Плюсы: Абсолютная надежность. Решает проблему уникальной идентификации дублирующихся элементов (благодаря GUID). Данные хранятся внутри самого объекта планировщика, отсутствует риск рассинхронизации клиентских переменных.
    • Минусы: Требуется небольшая переработка серверного кода заполнения планировщика.
  2. Способ 2 (Проверка перетаскивания внутри):
    • Плюсы: Работает непосредственно в момент интерактивного перемещения. Позволяет тонко управлять процессом (например, запретить перетаскивание в определенные зоны прямо во время движения мыши).
    • Минусы: Отрабатывает только на физический "драг-энд-дроп", не сработает при обычном текстовом редактировании периода через форму элемента.
  3. Способ 3 (Кэширование ПриАктивизации):
    • Плюсы: Быстрота реализации, не нужно менять структуры данных.
    • Минусы: Менее надежен при сложных сценариях работы пользователя (быстрые клики, множественный выбор).

Используйте Способ 1 как стандарт разработки для сложных комплексных систем автоматизации, а Способ 2 и Способ 3 — как вспомогательные инструменты для кастомизации поведения интерфейса "на лету".

← На главную