Разработчики, поддерживающие конфигурации на обычных формах в 1С:Предприятии 8, часто сталкиваются со специфическими ограничениями платформы при настройке интерфейса. Одна из классических «хотелок» пользователей — автоматическая подгонка ширины колонки табличного поля (или дерева значений) под размер текста в ее заголовке.
Сложность заключается в том, что свойство Ширина объекта КолонкаТаблицыЗначений измеряется не в пикселях или пунктах, а в абстрактных «средних символах шрифта». Поскольку в интерфейсе обычных форм чаще всего используются пропорциональные шрифты (например, Tahoma или Arial), символы имеют разную ширину: буква «Ш» визуально намного шире буквы «І» или знака точки. Простое использование функции СтрДлина() дает огромную погрешность. На выходе заголовок либо некрасиво обрезается троеточием, либо колонка становится избыточно широкой.
В этой статье мы подробно разберем несколько эффективных практических решений этой проблемы — от простых эмпирических алгоритмов до использования веб-технологий и системных вызовов.
Проанализируем самый простой способ решения задачи, который реализуется исключительно средствами встроенного языка 1С без привлечения сторонних библиотек и COM-объектов. Если мы не хотим усложнять проект, мы можем аппроксимировать ширину строки, рассчитав сумму «весов» каждого символа.
Идея заключается в создании соответствия, где каждому символу алфавита, цифрам и знакам препинания присваивается определенный коэффициент ширины относительно базового «среднего символа» шрифта:
Ш, Ж, М, Ф, Ю, W, M, @) — коэффициент 1.3 - 1.5.і, л, т, а, i, l, f, t, пробелы, точки, запятые) — коэффициент 0.4 - 0.7.1.0.Разберем по шагам создание функции на встроенном языке 1С, которая будет посимвольно обходить строку заголовка и суммировать веса:
Функция РассчитатьШиринуСтрокиВСимволах(Знач ИсходныйТекст) Экспорт
ВесаСимволов = Новый Соответствие;
// Заполняем широкие символы
ВесаСимволов.Вставить("Ш", 1.4);
ВесаСимволов.Вставить("Ж", 1.4);
ВесаСимволов.Вставить("М", 1.3);
ВесаСимволов.Вставить("Ф", 1.3);
ВесаСимволов.Вставить("Ю", 1.3);
ВесаСимволов.Вставить("W", 1.4);
ВесаСимволов.Вставить("M", 1.4);
ВесаСимволов.Вставить("@", 1.5);
// Заполняем узкие символы
ВесаСимволов.Вставить("І", 0.5);
ВесаСимволов.Вставить("і", 0.4);
ВесаСимволов.Вставить("л", 0.7);
ВесаСимволов.Вставить("т", 0.7);
ВесаСимволов.Вставить("а", 0.8);
ВесаСимволов.Вставить("i", 0.4);
ВесаСимволов.Вставить("l", 0.4);
ВесаСимволов.Вставить("f", 0.5);
ВесаСимволов.Вставить("t", 0.6);
ВесаСимволов.Вставить(" ", 0.5);
ВесаСимволов.Вставить(".", 0.4);
ВесаСимволов.Вставить(",", 0.4);
ВесаСимволов.Вставить("-", 0.6);
ВесаСимволов.Вставить("1", 0.7);
ОбщаяШирина = 0;
Для НомерСимвола = 1 По СтрДлина(ИсходныйТекст) Цикл
ТекущийСимвол = Сред(ИсходныйТекст, НомерСимвола, 1);
СимволВВерхнемРегистре = ВРег(ТекущийСимвол);
Вес = ВесаСимволов.Получить(СимволВВерхнемРегистре);
Если Вес = Неопределено Тогда
Вес = 1.0; // Значение по умолчанию для стандартных символов
КонецЕсли;
ОбщаяШирина = ОбщаяШирина + Вес;
КонецЦикла;
// Добавляем небольшой запас (2-3 символа) на рамки и отступы колонки
Возврат Цел(ОбщаяШирина) + 3;
КонецФункции
Теперь мы можем применить эту функцию при инициализации или динамическом построении нашей таблицы значений на форме:
Для Каждого Колонка Из ЭлементыФормы.ТабличноеПолеДерево.Колонки Цикл
РассчитаннаяШирина = РассчитатьШиринуСтрокиВСимволах(Колонка.Заголовок);
Колонка.Ширина = Макс(РассчитаннаяШирина, 8); // Ограничиваем минимальную ширину
КонецЦикла;
Этот алгоритм работает мгновенно, не зависит от операционной системы и сторонних библиотек, обеспечивая точность попадания до 90-95%.
Рассмотрим более продвинутый метод, предложенный автором обсуждения. Он позволяет с пиксельной точностью замерить физический размер строки шрифта. Метод базируется на использовании HTML5-контекста Canvas и его встроенной JS-функции measureText().
Для реализации этого подхода на форму добавляется элемент управления ПолеHTMLДокумента (его можно сделать невидимым или разместить за пределами видимости пользователя). В поле загружается простая HTML-страница со скриптом замера:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script>
function GetTextWidth(text, fontName, fontSize) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.font = fontSize + "px " + fontName;
return ctx.measureText(text).width;
}
</script>
</head>
<body></body>
</html>
Из встроенного языка 1С мы можем обращаться к DOM-модели загруженного документа и вызывать JavaScript-функцию напрямую. Проанализируем, как перевести полученные пиксели в "символы 1С":
GetTextWidth.Если вы не хотите размещать лишние визуальные элементы управления на форме, мы можем оптимизировать предыдущий подход. Воспользуемся возможностями скрытого COM-объекта htmlfile, который будет выполнять все расчеты непосредственно в оперативной памяти компьютера.
Разберем готовый шаблон кода для реализации этого алгоритма:
// Создаем невидимый объект в оперативной памяти
HTMLДокумент = Новый COMОбъект("htmlfile");
HTMLДокумент.open("text/html");
// Формируем разметку со встроенным JS-кодом
КодHTML =
"<html>" +
"<head>" +
"<meta http-equiv='X-UA-Compatible' content='IE=edge'>" +
"<script>" +
"function GetWidth(text, fontName, fontSize) {" +
" var canvas = document.createElement('canvas');" +
" var ctx = canvas.getContext('2d');" +
" ctx.font = fontSize + 'px ' + fontName;" +
" return ctx.measureText(text).width;" +
"}" +
"</script>" +
"</head>" +
"<body></body>" +
"</html>";
HTMLДокумент.write(КодHTML);
HTMLДокумент.close();
// Вызываем JS-функции напрямую из контекста 1С
ИмяШрифта = "Tahoma";
РазмерШрифта = 11;
Для Каждого Колонка Из ЭлементыФормы.ТабличноеПоле1.Колонки Цикл
ТекстЗаголовка = Колонка.Заголовок;
// Замеряем физическую ширину текста заголовка
ШиринаПиксели = HTMLДокумент.parentWindow.GetWidth(ТекстЗаголовка, ИмяШрифта, РазмерШрифта);
// Замеряем ширину среднего символа '0'
ШиринаБазовогоПиксели = HTMLДокумент.parentWindow.GetWidth("0", ИмяШрифта, РазмерШрифта);
// Переводим пиксели в "символы 1С" и устанавливаем значение
Колонка.Ширина = Окр(ШиринаПиксели / ШиринаБазовогоПиксели) + 2;
КонецЦикла;
Этот способ работает стабильно, незаметно для пользователя и гарантирует высокую точность соответствия ширины колонок реальным размерам текста заголовков.
Еще один оригинальный трюк заключается в использовании ячеек объекта ТабличныйДокумент. Мы можем динамически управлять свойствами ячеек и анализировать их поведение в фоновом режиме:
ТабличныйДокумент.РазмещениеТекста в значение Переносить.ШиринаКолонки (оно измеряется в тех же средних символах шрифта).Расположить() мы можем программно отслеживать высоту строки или границы текста. В момент, когда высота строки ячейки резко увеличивается, это означает, что текст начал переноситься на следующую строку. Предыдущий шаг итерации и будет являться минимальной физической шириной заголовка.Для систем, работающих исключительно под управлением Windows, разработчики часто используют прямой вызов функций операционной системы. Системная функция GetTextExtentPoint32 позволяет получить точные метрики строки для любого системного шрифта.
Для вызова WinAPI-функций из встроенного языка 1С традиционно используются внешние компоненты, такие как DynWrap (Dynamic Wrapper). Полученная ширина в пикселях затем переводится в символы 1С простым делением на среднюю ширину символа (которая для стандартного интерфейсного шрифта Tahoma 8pt составляет примерно 7-8 пикселей).
Важно подчеркнуть, что описанная проблема актуальна исключительно для обычного интерфейса. В управляемом интерфейсе (платформа 1С:Предприятие 8.2, 8.3 и новее) разработчики 1С решили эту задачу на уровне ядра платформы.
В управляемых формах для автоматического подбора размера колонок достаточно настроить свойства в конфигураторе:
АвтоШиринаВТаблице в значение ПоДаннымИЗаголовку.Ширина в значение 0 (автоподбор).Платформа сама динамически проанализирует длину заголовка, применит текущую тему оформления операционной системы, учтет масштаб интерфейса и идеально впишет текст в рамки колонки без использования костылей.