Объект Планировщик в платформе 1С:Предприятие 8 предоставляет широкие возможности для реализации интерактивных графиков, расписаний и календарей. Однако при разработке часто возникают нестандартные задачи. Например, когда пользователь зажимает левую кнопку мыши и растягивает «резиновую рамку» выделения, нам необходимо динамически показывать ему длительность выбираемого интервала (например, «25 минут» или «1 час 15 минут»).
В этой статье мы подробно разберем, как получить доступ к границам выделяемой области в реальном времени, почему стандартные методы платформы могут «ломать» интерфейс планировщика и как красиво решить эту задачу несколькими способами.
Проанализируем распространенную ошибку начинающих разработчиков. При работе с планировщиком часто путают два разных понятия: выделенные элементы расписания (уже созданные записи/события) и выделенную область пустых ячеек сетки.
Если мы обратимся к свойству ВыделенныеЭлементы в процессе растягивания пустой области, платформа вернет пустую коллекцию. Это логично, ведь пользователь еще не выбрал существующую запись, он только намечает границы для создания новой. Для решения нашей задачи нам потребуются специализированные интерактивные события формы.
Выясним, какое именно событие срабатывает, когда пользователь зажимает мышь и тянет рамку выделения по планировщику. Экспериментальным путем и с помощью отладчика легко установить, что платформа генерирует событие ПроверкаПеретаскиванияВнутри (или ПроверкаПеретаскивания в зависимости от контекста).
Свяжем элемент формы планировщика с обработчиком события. Рассмотрим структуру параметров, которые платформа передает в этот обработчик:
Элемент — ссылка на само поле формы планировщика.ПараметрыПеретаскивания — фиксированный массив, содержащий специализированные объекты. Когда перетаскивание происходит внутри сетки времени, этот массив содержит объект типа ПараметрПеретаскиванияВнутриПланировщика.Именно внутри объекта ПараметрПеретаскиванияВнутриПланировщика содержатся самые важные для нас свойства:
Начало — дата и время начала выделяемого интервала (тип Дата).Конец — дата и время окончания выделяемого интервала (тип Дата).ЗначенияИзмерений — соответствие, определяющее ресурсы или колонки (например, конкретный кабинет или сотрудник), на которых происходит выделение.Посмотрим на частую проблему. Если мы попытаемся вывести рассчитанное время самым простым путем, используя метод Сообщить() непосредственно в обработчике перетаскивания, то столкнемся с неприятным поведением интерфейса. Синяя рамка выделения на планировщике начнет мерцать или вовсе исчезнет, а сам процесс выделения будет постоянно прерываться.
Выясним причину этого поведения. Метод Сообщить() заставляет платформу перерисовывать окно сообщений внизу экрана и перехватывать фокус ввода. Это сбивает фокус с интерактивной операции захвата мыши (Drag-and-Drop) на планировщике, прерывая визуальное отображение «резиновой рамки» выделения.
Чтобы избежать этой проблемы, нам необходимо использовать способы вывода информации, которые не вызывают обновления фокуса и перерисовки основных интерфейсных окон.
Самый простой и быстрый способ показать пользователю время без нарушения отрисовки планировщика — использовать системный метод Состояние(). Он выводит текст в строку состояния в левом нижнем углу экрана плавно и незаметно для фокуса ввода.
Разберем пример кода обработчика события:
&НаКлиенте
Процедура ПланировщикПроверкаПеретаскиванияВнутри(Элемент, ПараметрыПеретаскивания, ВариантИзмененияГраниц, ДействиеПеретаскивания, Отказ)
// Проверяем наличие параметров перетаскивания
Если ПараметрыПеретаскивания.Количество() > 0 Тогда
Для Каждого ТекущийПараметр Из ПараметрыПеретаскивания Цикл
// Нам нужен только параметр перетаскивания внутри планировщика
Если ТипЗнч(ТекущийПараметр) = Тип("ПараметрПеретаскиванияВнутриПланировщика") Тогда
ДатаНачала = ТекущийПараметр.Начало;
ДатаОкончания = ТекущийПараметр.Конец;
// Рассчитываем разницу в секундах и переводим в минуты
ДлительностьСекунд = ДатаОкончания - ДатаНачала;
ДлительностьМинут = ДлительностьСекунд / 60;
// Формируем понятный текст для пользователя
ТекстВремя = Формат(ДатаНачала, "ДФ=ЧЧ:мм") + " - " + Формат(ДатаОкончания, "ДФ=ЧЧ:мм");
ТекстДлительность = Строка(ДлительностьМинут) + " мин.";
// Выводим в статус-строку без потери фокуса
Состояние(ТекстВремя + " (Выделено: " + ТекстДлительность + ")", , , КартинкаКлиента:Информация);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Если вывод в статус-строку кажется вам недостаточно заметным, рассмотрим более красивое и профессиональное решение. Мы можем разместить элемент формы типа Декорация — Надпись непосредственно над планировщиком или в выделенной информационной панели.
При этом в процессе перетаскивания мы будем программно обновлять заголовок этой надписи. Так как изменение заголовка декорации не забирает фокус у планировщика, рамка выделения останется стабильной и красивой.
Выполним следующие шаги:
НадписьДлительность.Посмотрим на пример реализации этого алгоритма:
&НаКлиенте
Процедура ПланировщикПроверкаПеретаскиванияВнутри(Элемент, ПараметрыПеретаскивания, ВариантИзмененияГраниц, ДействиеПеретаскивания, Отказ)
Для Каждого ТекущийПараметр Из ПараметрыПеретаскивания Цикл
Если ТипЗнч(ТекущийПараметр) = Тип("ПараметрПеретаскиванияВнутриПланировщика") Тогда
ДатаНачала = ТекущийПараметр.Начало;
ДатаОкончания = ТекущийПараметр.Конец;
РазницаМинут = (ДатаОкончания - ДатаНачала) / 60;
// Если интервал большой, переведем его в часы и минуты для удобства чтения
Если РазницаМинут >= 60 Тогда
ЦелыхЧасов = Цел(РазницаМинут / 60);
ОстатокМинут = РазницаМинут % 60;
ТекстИнтервала = Строка(ЦелыхЧасов) + " ч. " + Строка(ОстатокМинут) + " мин.";
Иначе
ТекстИнтервала = Строка(РазницаМинут) + " мин.";
EndIf;
// Выводим информацию на форму
ШаблонТекста = "Выделенный интервал: с %1 по %2 (Длительность: %3)";
Элементы.НадписьДлительность.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
ШаблонТекста,
Формат(ДатаНачала, "ДФ=ЧЧ:мм"),
Формат(ДатаОкончания, "ДФ=ЧЧ:мм"),
ТекстИнтервала
);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При реализации данного механизма следует учитывать специфику работы клиент-серверной архитектуры 1С:
ПроверкаПеретаскиванияВнутри вызывается платформой непрерывно при каждом движении мыши (сотни раз в секунду). Код внутри этой процедуры должен быть максимально "легким" и выполняться строго на клиенте (директива &НаКлиенте). Любые серверные вызовы в этой процедуре приведут к катастрофическому зависанию интерфейса пользователя.Начало и Конец зависят от настроек планировщика, таких как КратностьДерева или шаг временной шкалы. Платформа автоматически округляет («примагничивает») выделяемую область к границам ячеек, поэтому вы всегда будете получать точные дискретные значения времени в соответствии с вашей шкалой.Применяя данные подходы, вы сможете сделать работу пользователей с планировщиком максимально информативной, удобной и визуально приятной.