Здравствуйте.
Сегодня речь пойдет о реализации подсистемы выдачи информационных сообщений пользователю. Стандартный способ проинформировать о чем-либо пользователя – это использование процедуры «Сообщить». Однако в некоторых случаях, хочется создать отдельный список, который не будет замусоривать своим выводом основной список сообщений платформы. Также желательно иметь возможность выделять цветом различные сообщения (в зависимости от их типа, например). В общем, расскажу, как это получилось сделать у меня. Приведенное в статье решение можно встроить в свою программу без какой-либо адаптации.

Предыстория.


В свое время, работая с системой контроля версий Perforce, а именно с ее графическими клиентами, я обратил внимание на удобный элемент интерфейса – многострочное поле вывода сообщений внизу главного окна программы. После того, как пользователь выполнял какие-то обращения к серверу системы, в это поле добавлялись текстовые сообщения, описывающие процесс взаимодействия клиента с сервером (какие конкретно действия выполнялись, и результат их выполнения). Не знаю, почему именно Perforce произвел на меня такое впечатление, хотя абсолютно ту же систему я наблюдал до и после в различных графических IDE (Delphi, C++ и т.д.) – окно вывода сообщений той консольной программы (компилятор или клиент системы контроля версий), в качестве оболочки которой выступает графическая программа. Но впрочем, статья вовсе не про Perforce, просто я считаю удобным способ многострочного вывода сообщений в процессе выполнения какой-то длительной работы. Получить большой список сообщений гораздо лучше (информативнее), чем одиночный MessageBox в самом конце этой работы. Подобный подход мы, кстати, видим и в антивирусных программах.

На создание собственной подсистемы сообщений меня натолкнула необходимость реализации программы, которая выполняет относительно длительный процесс обработки данных (т.е. пользователь нажал кнопку «Выполнить», а потом может, условно говоря, несколько минут ждать, пока процесс выполняется). Подобные программы (обработки) есть и в типовых конфигурациях 1С (в основном это различные выгрузки-загрузки). Стандартное окно сообщений платформы (то, куда выводится информация процедурой «Сообщить») меня не устраивало. Я хотел, чтобы сообщения, связанные с моей обработкой были сгруппированы, и не смешивались ни с какими другими сообщениями. Также я хотел, чтобы список сообщений можно было как-то сохранять на всякий случай (в текстовый файл, как это сделано в антивирусах). Ну и главное, я планировал сделать сообщения разного типа (информационные, предупреждения, сообщения об ошибках). Ну и соответственно раскрашивать их в различные цвета.

В рассматриваемом примере таблица вывода сообщения (элемент интерфейса ТабличноеПоле) выглядит примерно так:


Описание реализации.


В общем, опишу здесь то, что получилось. Для реализации буду опять использовать свою технологию «ООП в 1С» (метод подробно описан здесь). Соответственно будет создан «класс»: «СообщенияПрограммы».
В дальнейшем при объяснениях будем считать, что делаем внешнюю обработку.

Нам понадобятся иконки для отображения статусов сообщений. Загрузим из соответствующих BMP-файлов (прилагаются к данной статье) в ресурсы (макеты типа «Двоичные данные»)
Изображение Название исходного файла Название макета
statok.bmp КартинкаСообщениеСтатусОК
statwarning.bmp КартинкаСообщениеСтатусПредупреждение
staterror.bmp КартинкаСообщениеСтатусОшибка

Затем нужно будет как-то эти картинки загрузить для использования – превратим их в ресурсы.
Определим в главном модуле глобальную переменную «КоллекцияКартинок» в виде структуры из объектов стандартного класса «Картинка». Напишем подпрограмму «СформироватьКоллекциюКартинок», которая будет инициализировать коллецию. Будем вызывать эту подпрограмму в инициализирующей секции модуля.

Также наши сообщения будут иметь некоторые числовые типы – уровни. Номер уровня будет влиять на количество отступов из пробельных символов слева при выводе сообщения в интерфейс или файл (как бы наглядно показываем уровень вложенности сообщений друг в друга).
Название Описание Числовое значение
ТИПСООБЩ_НЕОПРЕД Неопределенное значение 0
ТИПСООБЩ_УРОВЕНЬ1 Уровень вложенности 1 1
ТИПСООБЩ_УРОВЕНЬ2 Уровень вложенности 2 2
ТИПСООБЩ_УРОВЕНЬ3 Уровень вложенности 3 3

У каждого сообщения есть определенный статус, показывающий, чем именно является сообщение.
Название Описание Числовое значение
СТАТСООБЩ_НЕОПРЕД Неопределенное значение 0
СТАТСООБЩ_ОК Уровень вложенности 1 1
СТАТСООБЩ_ПРЕДУПР Уровень вложенности 2 2
СТАТСООБЩ_ОШИБКА Уровень вложенности 3 3

Представим типы и статусы в виде числовых констант, как это делается в С/С++ (#define) или Pascal (const). В 1С будем имитировать константы вставкой их в глобальную переменную-структуру (ключ элемента структуры – это символьное имя константы, а значение элемента структуры – это численное значение константы).
Функция ИнициализацияПрочихКонстант()
	
	Проч = Новый Структура;
	
	// Типы сообщений
	Проч.Вставить("ТИПСООБЩ_НЕОПРЕД", 0);   // Тип неопределен
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ1", 1);  // Сообщение уровня 1
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ2", 2);  // Сообщение уровня 2
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ3", 3);  // Сообщение уровня 3

	// Статусы сообщений
	Проч.Вставить("СТАТСООБЩ_НЕОПРЕД", 0);  // Статус неопределен
	Проч.Вставить("СТАТСООБЩ_ОК", 1);       // Статус "ОК"
	Проч.Вставить("СТАТСООБЩ_ПРЕДУПР", 2);  // Статус "Предупреждение"
	Проч.Вставить("СТАТСООБЩ_ОШИБКА", 3);   // Статус "Ошибка"
	
	Возврат Проч
	
КонецФункции

Еще нам нужно будет получать текущее значение времени в миллисекундах. Используем для этого машину JavaScript в виде глобальной переменной COM-объекта.

Вот что получилось в главном модуле:
Исходный текст
////////////////////////////////////////////////////////////////////////////////
// ПРОЧИЕ КОНСТАНТЫ

Перем Проч Экспорт;                       // Структура с константами

Перем КоллекцияКартинок Экспорт;          // Набор иконок интерфейса


////////////////////////////////////////////////////////////////////////////////
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ

Перем JavaScript;                         // Машина для работы с JavaScript


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    И Н И Ц И А Л И З А Ц И И    К О Н С Т А Н Т
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Инициализирует набор констант общего назначения
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Структура с полями - константами общего назначения
//
Функция ИнициализацияПрочихКонстант()
	
	Проч = Новый Структура;
	
	// Типы сообщений
	Проч.Вставить("ТИПСООБЩ_НЕОПРЕД", 0);   // Тип неопределен
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ1", 1);  // Сообщение уровня 1
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ2", 2);  // Сообщение уровня 2
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ3", 3);  // Сообщение уровня 3

	// Статусы сообщений
	Проч.Вставить("СТАТСООБЩ_НЕОПРЕД", 0);  // Статус неопределен
	Проч.Вставить("СТАТСООБЩ_ОК", 1);       // Статус "ОК"
	Проч.Вставить("СТАТСООБЩ_ПРЕДУПР", 2);  // Статус "Предупреждение"
	Проч.Вставить("СТАТСООБЩ_ОШИБКА", 3);   // Статус "Ошибка"
	
	Возврат Проч
	
КонецФункции


// Формирует набор картинок, на основании изображений, сохраненных в двоичных
// макетах обработки.
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Структура с полями - объектами типа Картинка с загруженными картинками
//
Функция СформироватьКоллекциюКартинок()
	
	КоллекцияКартинок = Новый Структура;
	
	КоллекцияКартинок.Вставить("СообщениеСтатусОК", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусОК")));
	КоллекцияКартинок.Вставить("СообщениеСтатусПредупреждение", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусПредупреждение")));
	КоллекцияКартинок.Вставить("СообщениеСтатусОшибка", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусОшибка")));
	
	Возврат КоллекцияКартинок
	
КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    И Н И Ц И А Л И З А Ц И И    Г Л О Б А Л Ь Н Ы Х    П Е Р Е М Е Н Н Ы Х
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Создает объект JavaScript и возвращает его в качестве значения
// Формирует набор картинок, на основании изображений, сохраненных в двоичных
// макетах обработки.
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Ссылка на машину JavaScript или Неопределено, если машина не создана
//
Функция СоздатьОбъектJavaScript()
	
	Рез = Неопределено;
	Попытка
		Рез = Новый COMОбъект("MSScriptControl.ScriptControl");
	    Рез.Language = "javascript";	
	Исключение
	КонецПопытки;

    Возврат Рез

КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    О Б Щ Е Г О    Н А З Н А Ч Е Н И Я
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Расширяет строку до заданной ширины пробелами, добавляя их слева
//
// Параметры:
//  Стр - исходная строка
//  Ширина - кол-во символов до которого нужно расширить строку
// Возврат:
//  Полученная строка
//
Функция РасширСтрЛев(Стр, Ширина)
	
	Рез = Стр;
	Инд = Ширина - СтрДлина(Рез);
	Пока Инд > 0 Цикл
		Рез = " " + Рез;
		Инд = Инд - 1
	КонецЦикла;
	
	Возврат Рез
	
КонецФункции

// Расширяет строку до заданной ширины пробелами, добавляя их справа
//
// Параметры:
//  Стр - исходная строка
//  Ширина - кол-во символов до которого нужно расширить строку
// Возврат:
//  Полученная строка
//
Функция РасширСтрПрав(Стр, Ширина)
	
	Рез = Стр;
	Инд = Ширина - СтрДлина(Рез);
	Пока Инд > 0 Цикл
		Рез = Рез + " ";
		Инд = Инд - 1
	КонецЦикла;
	
	Возврат Рез
	
КонецФункции


// Возвращает текущую дату и время в миллисекундах
//
// Параметры:
//  НЕТ
// Возврат:
//  Числовое значение или 0, если значение определить не удалось
//
Функция ПолучитьТекущееВремяВМиллисекундах() Экспорт
	
	Время = 0;
	Если JavaScript <> Неопределено Тогда
		Время = JavaScript.Eval("new Date().getTime()");
	КонецЕсли;
	
	Возврат Время;
	
КонецФункции




////////////////////////////////////////////////////////////////////////////////
// ОПЕРАТОРЫ ОСНОВНОЙ ПРОГРАММЫ - НАЧАЛЬНАЯ ИНИЦИАЛИЗАЦИЯ



// ИНИЦИАЛИЗАЦИЯ КОНСТАНТ

// Константы общего назначения
Проч = ИнициализацияПрочихКонстант();

// Коллекция картинок
КоллекцияКартинок = СформироватьКоллекциюКартинок();



// ИНИЦИАЛИЗАЦИЯ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ

// Создать машину JavaScript
JavaScript = СоздатьОбъектJavaScript();


Набор сообщений существует в виде таблицы значений (стандартный класс ТаблицаЗначений) со следующими колонками:
Название колонки Тип данных Назначение
ТипСообщ Число Тип сообщения (уровень) – одна из констант ТИПСООБЩ_
СтатусСообщ Число Статус сообщения – одна из констант СТАТСООБЩ_
ТекстСообщ Строка Основной текст сообщения. Здесь обычно указываем, какое действие выполняется длительным процессом в данный момент.
КомментарийСообщ Строка Дополнительный текст сообщения. Здесь обычно указываем результат выполнения действия, описанного в колонке ТекстСообщ. Если действие завершилось ошибкой, то сообщение об ошибке приводим в этой же колонке

Эта таблица значений уже может отображаться в каких-то элементах интерфейса пользователя (например в объекте класса TaбличноеПоле).

Набор сообщений будет реализован в виде класса, имеющего следующие методы:
Заголовок метода Описание
Функция Сообщения_Конструктор() Начальное создание объекта. Возвращает объект класса «Сообщения».
Процедура Сообщения_УстАтриб(Сообщ, Форма, ТабПоле, ТабЗнач) Установка атрибутов объекта. Нужно передать:
— ссылку на форму (Форма), на которой будет отображаться список сообщений;
— элемент формы типа ТабличноеПоле (ТабПоле), в который будут выводится сообщения;
— ТаблицаЗначений (ТабЗнач), в которой будет храниться собственно список сообщений.
Процедура Сообщения_Инициализация(Сообщ) Будет проинициализирована ТаблицаЗначений (ранее установленная процедурой Сообщения_УстАтриб)
Процедура Сообщения_Обновить(Сообщ) Выполняет принудительную отрисовку списка сообщений на форме (ранее установленная процедурой Сообщения_УстАтриб)
Функция Сообщения_Добавить(Сообщ, ТипСообщ, ТекстСообщ, СтатусСообщ=Неопределено, КомментСообщ=Неопределено) Добавляет в набор новое сообщение с соответствующими атрибутами. Возвращает идентификатор сообщения с помощью которого, к данному сообщению можно будет обращаться в дальнейшем.
Процедура Сообщения_Изменить(Сообщ, ИдСообщ, ТипСообщ=Неопределено, ТекстСообщ=Неопределено, СтатусСообщ=Неопределено, КомментСообщ=Неопределено) Изменяет атрибуты уже существующего сообщения с указанным идентификатором.
Процедура Сообщения_Удалить(Сообщ, ИдСообщ) Удалить ранее добавленное сообщение с соответствующим идентификатором.
Процедура Сообщения_Очистить(Сообщ) Удалить все сообщения набора.
Функция Сообщения_ПолучитьТип(Сообщ, ИдСообщ) Получить атрибут «Тип» для существующего сообщения с указанным идентификатором.
Процедура Сообщения_УстановитьТип(Сообщ, ИдСообщ, ТипСообщ) Установить атрибут «Тип» для существующего сообщения с указанным идентификатором.
Функция Сообщения_ПолучитьСтатус(Сообщ, ИдСообщ) Получить атрибут «Статус» для существующего сообщения с указанным идентификатором.
Процедура Сообщения_УстановитьСтатус(Сообщ, ИдСообщ, СтатусСообщ) Установить атрибут «Статус» для существующего сообщения с указанным идентификатором.
Функция Сообщения_ПолучитьТекст(Сообщ, ИдСообщ) Получить атрибут «Текст сообщения» для существующего сообщения с указанным идентификатором.
Процедура Сообщения_УстановитьТекст(Сообщ, ИдСообщ, ТекстСообщ) Установить атрибут «Текст сообщения» для существующего сообщения с указанным идентификатором.
Функция Сообщения_ПолучитьКоммент(Сообщ, ИдСообщ) Получить атрибут «Комментарий» для существующего сообщения с указанным идентификатором.
Процедура Сообщения_УстановитьКоммент(Сообщ, ИдСообщ, КомментСообщ) Установить атрибут «Комментарий» для существующего сообщения с указанным идентификатором.
Функция Сообщения_ЦветТекста(ТипСообщ, СтатусСообщ) Определить цвет текста сообщения для указанного статуса. Используется при раскраске сообщений в интерфейсе.
Функция Сообщения_ЦветКомментария(ТипСообщ, СтатусСообщ) Определить цвет дополнительного текста (комментария) сообщения для указанного статуса. Используется при раскраске сообщений в интерфейсе.
Функция Сообщения_Картинка(ТипСообщ, СтатусСообщ) Определить иконку для сообщения с указанным статусом. Используется при выводе сообщений в интерфейсе.
Функция Сообщения_СохранитьВФайл(Сообщ) Сохранить набор сообщений в текстовый файл (имя файла запрашивает у пользователя).

Итак, полный текст главного модуля обработки:
Текст модуля
////////////////////////////////////////////////////////////////////////////////
// ПРОЧИЕ КОНСТАНТЫ

Перем Проч Экспорт;                       // Структура с константами

Перем КоллекцияКартинок Экспорт;          // Набор иконок интерфейса


////////////////////////////////////////////////////////////////////////////////
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ

Перем JavaScript;                         // Машина для работы с JavaScript


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    И Н И Ц И А Л И З А Ц И И    К О Н С Т А Н Т
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Инициализирует набор констант общего назначения
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Структура с полями - константами общего назначения
//
Функция ИнициализацияПрочихКонстант()
	
	Проч = Новый Структура;
	
	// Типы сообщений
	Проч.Вставить("ТИПСООБЩ_НЕОПРЕД", 0);   // Тип неопределен
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ1", 1);  // Сообщение уровня 1
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ2", 2);  // Сообщение уровня 2
	Проч.Вставить("ТИПСООБЩ_УРОВЕНЬ3", 3);  // Сообщение уровня 3

	// Статусы сообщений
	Проч.Вставить("СТАТСООБЩ_НЕОПРЕД", 0);  // Статус неопределен
	Проч.Вставить("СТАТСООБЩ_ОК", 1);       // Статус "ОК"
	Проч.Вставить("СТАТСООБЩ_ПРЕДУПР", 2);  // Статус "Предупреждение"
	Проч.Вставить("СТАТСООБЩ_ОШИБКА", 3);   // Статус "Ошибка"
	
	Возврат Проч
	
КонецФункции


// Формирует набор картинок, на основании изображений, сохраненных в двоичных
// макетах обработки.
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Структура с полями - объектами типа Картинка с загруженными картинками
//
Функция СформироватьКоллекциюКартинок()
	
	КоллекцияКартинок = Новый Структура;
	
	КоллекцияКартинок.Вставить("СообщениеСтатусОК", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусОК")));
	КоллекцияКартинок.Вставить("СообщениеСтатусПредупреждение", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусПредупреждение")));
	КоллекцияКартинок.Вставить("СообщениеСтатусОшибка", Новый Картинка(ПолучитьМакет("КартинкаСообщениеСтатусОшибка")));
	
	Возврат КоллекцияКартинок
	
КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    И Н И Ц И А Л И З А Ц И И    Г Л О Б А Л Ь Н Ы Х    П Е Р Е М Е Н Н Ы Х
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Создает объект JavaScript и возвращает его в качестве значения
// Формирует набор картинок, на основании изображений, сохраненных в двоичных
// макетах обработки.
//
// Параметры:
//  НЕТ.
//
// Возврат:
//  Ссылка на машину JavaScript или Неопределено, если машина не создана
//
Функция СоздатьОбъектJavaScript()
	
	Рез = Неопределено;
	Попытка
		Рез = Новый COMОбъект("MSScriptControl.ScriptControl");
	    Рез.Language = "javascript";	
	Исключение
	КонецПопытки;

    Возврат Рез

КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// П О Д П Р О Г Р А М М Ы    О Б Щ Е Г О    Н А З Н А Ч Е Н И Я
//
///////////////////////////////////////////////////////////////////////////////////////////////////

// Расширяет строку до заданной ширины пробелами, добавляя их слева
//
// Параметры:
//  Стр - исходная строка
//  Ширина - кол-во символов до которого нужно расширить строку
// Возврат:
//  Полученная строка
//
Функция РасширСтрЛев(Стр, Ширина)
	
	Рез = Стр;
	Инд = Ширина - СтрДлина(Рез);
	Пока Инд > 0 Цикл
		Рез = " " + Рез;
		Инд = Инд - 1
	КонецЦикла;
	
	Возврат Рез
	
КонецФункции

// Расширяет строку до заданной ширины пробелами, добавляя их справа
//
// Параметры:
//  Стр - исходная строка
//  Ширина - кол-во символов до которого нужно расширить строку
// Возврат:
//  Полученная строка
//
Функция РасширСтрПрав(Стр, Ширина)
	
	Рез = Стр;
	Инд = Ширина - СтрДлина(Рез);
	Пока Инд > 0 Цикл
		Рез = Рез + " ";
		Инд = Инд - 1
	КонецЦикла;
	
	Возврат Рез
	
КонецФункции


// Возвращает текущую дату и время в миллисекундах
//
// Параметры:
//  НЕТ
// Возврат:
//  Числовое значение или 0, если значение определить не удалось
//
Функция ПолучитьТекущееВремяВМиллисекундах() Экспорт
	
	Время = 0;
	Если JavaScript <> Неопределено Тогда
		Время = JavaScript.Eval("new Date().getTime()");
	КонецЕсли;
	
	Возврат Время;
	
КонецФункции


// Преобразует массив чисел из объекта COMSafeArray в строку символов
//
// Параметры:
//  ОбъектCOMSafeArray - ссылка на массив чисел типа COMSafeArray
//                       (размерность массива должна быть 1)
// Возврат:
//  Полученная строка или "", если операцию выполнить не удалось
//
Функция COMSafeArrayВСтроку(ОбъектCOMSafeArray) Экспорт
	
	Рез = "";
	
	Если ТипЗнч(ОбъектCOMSafeArray) = Тип("COMSafeArray") И
		 (ОбъектCOMSafeArray.GetDimensions() = 1) И
		 (ОбъектCOMSafeArray.GetLength(0) > 0) Тогда
		Массив = ОбъектCOMSafeArray.Выгрузить();
		Для Каждого КодСимв Из Массив Цикл
			Если ТипЗнч(КодСимв) = Тип("Число") Тогда
				// Перекодировка символа из Windows-1251 в UTF-8
				Если (КодСимв >= 192) И (КодСимв <= 223) Тогда
					// А - Я
					КодСимв = КодСимв + 848
				ИначеЕсли (КодСимв >= 224) И (КодСимв <= 239) Тогда
					// а - п
					КодСимв = КодСимв + 848
				ИначеЕсли (КодСимв >= 240) И (КодСимв <= 255) Тогда
					// р - я
					КодСимв = КодСимв + 848
				ИначеЕсли (КодСимв = 184) Тогда
					// ё
					КодСимв = 1105
				ИначеЕсли (КодСимв = 168) Тогда
					// Ё
					КодСимв = 1025
				ИначеЕсли (КодСимв = 185) Тогда
					// №
					КодСимв = 8470
				КонецЕсли;
				// Добавить символ к строке
				Рез = Рез + Символ(КодСимв)
			КонецЕсли
		КонецЦикла
	КонецЕсли;
	
	Возврат Рез
	
КонецФункции



///////////////////////////////////////////////////////////////////////////////////////////////////
//
// К Л А С С Ы    О Б Щ Е Г О    Н А З Н А Ч Е Н И Я
//
///////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Реализация класса СообщенияПрограммы (Сообщения)

// Создание полей объекта в виде структуры.
//
// Параметры:
//  Нет.
// Возврат:
//  Возвращает созданную структуру
//
Функция Сообщения_СоздатьОбъект() Экспорт
	
	Сообщ = Новый Структура;
	// Общие параметры сущности
	Сообщ.Вставить("Форма", Неопределено);      // Форма с табличным полем
	Сообщ.Вставить("ТабПоле", Неопределено);    // Табличное поле на форме
	Сообщ.Вставить("ТабЗнач", Неопределено);    // Таблица значений с сообщениями
	Сообщ.Вставить("ВремяПредОбновл", Неопределено); // Время последн. обновл. формы, мс (INT)
	
	Возврат Сообщ;
	
КонецФункции

// Имитирует конструктор объекта.
//
// Параметры:
//  НЕТ
// Возврат:
//  (Структура) - Структура с полями объекта
//
Функция Сообщения_Конструктор() Экспорт
	
	Сообщ = Сообщения_СоздатьОбъект();
	
	Возврат Сообщ;
	
КонецФункции

// Имитирует деструктор объекта - освобождает память.
//
// Параметры:
//  Сообщ - ссылка на объект
//
Процедура Сообщения_Деструктор(Сообщ) Экспорт
	
КонецПроцедуры

// Устанавливает основные атрибуты для вывода сообщений.
//
// Параметры:
//  Сообщ - ссылка на объект
//  Форма - ссылка на форму, на которой расположено табличное поле списка
//  ТабПоле - ссылка табличное поле списка для вывода сообщений
//  ТабЗнач - таблица значений с сообщениями
//
Процедура Сообщения_УстАтриб(Сообщ, Форма, ТабПоле, ТабЗнач) Экспорт
	
	Сообщ.Форма = Форма;
	Сообщ.ТабПоле = ТабПоле;
	Сообщ.ТабЗнач = ТабЗнач
	
КонецПроцедуры

// Инициализирует таблицу сообщений. Добавляет недостающие колонки
//
// Параметры:
//  Сообщ - ссылка на объект
//
Процедура Сообщения_Инициализация(Сообщ) Экспорт
	
	Если Сообщ.ТабЗнач <> Неопределено Тогда
		КЧ = Новый КвалификаторыЧисла(10, 0);
		КС = Новый КвалификаторыСтроки();
		МассивТипов = Новый Массив;
		МассивТипов.Добавить(Тип("Число"));
		ОписаниеТиповЧисла = Новый ОписаниеТипов(МассивТипов, , ,КЧ);
		МассивТипов.Очистить();
		МассивТипов.Добавить(Тип("Строка"));
		ОписаниеТиповСтроки = Новый ОписаниеТипов(МассивТипов, , КС);
		
		// Колонка "ТипСообщ"
		Если Сообщ.ТабЗнач.Колонки.Найти("ТипСообщ") = Неопределено Тогда
			Сообщ.ТабЗнач.Колонки.Добавить("ТипСообщ", ОписаниеТиповЧисла, "Тип", 30)
		КонецЕсли;
		// Колонка "СтатусСообщ"
		Если Сообщ.ТабЗнач.Колонки.Найти("СтатусСообщ") = Неопределено Тогда
			Сообщ.ТабЗнач.Колонки.Добавить("СтатусСообщ", ОписаниеТиповЧисла, "Статус", 30)
		КонецЕсли;
		// Колонка "ТекстСообщ"
		Если Сообщ.ТабЗнач.Колонки.Найти("ТекстСообщ") = Неопределено Тогда
			Сообщ.ТабЗнач.Колонки.Добавить("ТекстСообщ", ОписаниеТиповСтроки, "Текст", 200)
		КонецЕсли;
		// Колонка "КомментарийСообщ"
		Если Сообщ.ТабЗнач.Колонки.Найти("КомментарийСообщ") = Неопределено Тогда
			Сообщ.ТабЗнач.Колонки.Добавить("КомментарийСообщ", ОписаниеТиповСтроки, "Комментарий", 200)
		КонецЕсли
	КонецЕсли

КонецПроцедуры

// Выполняет овновление формы, на которой размещено табличное поле
// Тем самым вызывает прорисовку сообщений.
// Выполняет это не чаще заданного интервала, мс
//
// Параметры:
//  Сообщ - ссылка на объект
//
Процедура Сообщения_Обновить(Сообщ) Экспорт
	
	Если Сообщ.Форма <> Неопределено Тогда
		Интервал = 500;  // Интервал обновлений (не чаще), мс
		ВремяТекущОбновл = ПолучитьТекущееВремяВМиллисекундах();
		Если (Сообщ.ВремяПредОбновл = Неопределено) ИЛИ
			 (Сообщ.ВремяПредОбновл = 0) ИЛИ
			 (ВремяТекущОбновл - Сообщ.ВремяПредОбновл >= Интервал) Тогда
			// Выполняем обновление
			Сообщ.ВремяПредОбновл = ВремяТекущОбновл;
			Сообщ.Форма.Обновить()
		КонецЕсли
	КонецЕсли
	
КонецПроцедуры

// Добавляет новое сообщение в таблицу значений
//
// Параметры:
//  Сообщ - ссылка на объект;
//  ТипСообщ - тип добавляемого сообщения;
//  ТекстСообщ - текст добавляемого сообщения;
//  СтатусСообщ - статус добавляемого сообщения;
//  КомментСообщ - комментарий к добавляемому сообщению
// Возврат:
//  Уникальный идентификатор добавленного сообщения. Используется для дальнейшего обращения к сообщению с целью
//  его редактирования. По сути представляет собой порядковый номер строки в таблице Сообщ.ТабЗнач
//
Функция Сообщения_Добавить(Сообщ, ТипСообщ, ТекстСообщ, СтатусСообщ=Неопределено, КомментСообщ=Неопределено) Экспорт
	
	ИдСообщ = Неопределено;
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ТипСообщ <> Неопределено) И
		 (ТипСообщ >= Проч.ТИПСООБЩ_УРОВЕНЬ1) И (ТипСообщ <= Проч.ТИПСООБЩ_УРОВЕНЬ3) И
 		 (ТекстСообщ <> Неопределено) Тогда
		НоваяСтрока = Сообщ.ТабЗнач.Добавить();
		НоваяСтрока.ТипСообщ = ТипСообщ;
		// Преобразовать текст
		Если ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ1 Тогда
			
		ИначеЕсли ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ2 Тогда
			ТекстСообщ = "  " + ТекстСообщ
		ИначеЕсли ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ3 Тогда
			ТекстСообщ = "    " + ТекстСообщ
		КонецЕсли;
		// Установить текст
		НоваяСтрока.ТекстСообщ = ТекстСообщ;
		Если (СтатусСообщ <> Неопределено) И
			 (СтатусСообщ >= Проч.СТАТСООБЩ_ОК) И (СтатусСообщ <= Проч.СТАТСООБЩ_ОШИБКА) Тогда
			НоваяСтрока.СтатусСообщ = СтатусСообщ;
		Иначе
			НоваяСтрока.СтатусСообщ = Проч.СТАТСООБЩ_НЕОПРЕД;
		КонецЕсли;
		НоваяСтрока.КомментарийСообщ = КомментСообщ;
		Если Сообщ.ТабПоле <> Неопределено Тогда
			// Изменение текущей строки табличного поля на добавляемую,
			// чтобы пользователь видел, что добавляется в список в данный момент
			Сообщ.ТабПоле.ТекущаяСтрока = НоваяСтрока;
		КонецЕсли;
		Сообщения_Обновить(Сообщ);
		 
		ИдСообщ = Сообщ.ТабЗнач.Индекс(НоваяСтрока);
	КонецЕсли;
	 
	Возврат ИдСообщ

КонецФункции

// Изменяет атрибуты сообщения с идентификатором ИдСообщ. Те, значение которых не "Неопределено"
//
// Параметры:
//  Сообщ - ссылка на объект;
//  ИдСообщ - идентификатор сообщения (порядковый номер строки);
//  ТипСообщ - тип добавляемого сообщения;
//  ТекстСообщ - текст добавляемого сообщения;
//  СтатусСообщ - статус добавляемого сообщения;
//  КомментСообщ - комментарий к добавляемому сообщению
//
Процедура Сообщения_Изменить(Сообщ, ИдСообщ, ТипСообщ=Неопределено, ТекстСообщ=Неопределено, 
							 СтатусСообщ=Неопределено, КомментСообщ=Неопределено) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		ФлагИзменения = Ложь; // Признак того, что что-то изменили (хоть один атрибут)
		Если ТипСообщ <> Неопределено Тогда
			Если (ТипСообщ <> Сообщ.ТабЗнач[ИдСообщ].ТипСообщ) И
				  (ТипСообщ >= Проч.ТИПСООБЩ_УРОВЕНЬ1) И (ТипСообщ <= Проч.ТИПСООБЩ_УРОВЕНЬ3) Тогда
				Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = ТипСообщ;
				ФлагИзменения = Истина
			КонецЕсли
		КонецЕсли;
		Если ТекстСообщ <> Неопределено Тогда
			Если ТекстСообщ <> Сообщ.ТабЗнач[ИдСообщ].ТекстСообщ Тогда
				// Преобразовать текст
				Если Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ1 Тогда
			
				ИначеЕсли Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ2 Тогда
					ТекстСообщ = "  " + ТекстСообщ
				ИначеЕсли Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ3 Тогда
					ТекстСообщ = "    " + ТекстСообщ
				КонецЕсли;
				// Установить текст
				Сообщ.ТабЗнач[ИдСообщ].ТекстСообщ = ТекстСообщ;
				ФлагИзменения = Истина
			КонецЕсли
		КонецЕсли;
		Если СтатусСообщ <> Неопределено Тогда
			Если СтатусСообщ <> Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ Тогда
				Если (СтатусСообщ <> Неопределено) И
					 (СтатусСообщ >= Проч.СТАТСООБЩ_ОК) И (СтатусСообщ <= Проч.СТАТСООБЩ_ОШИБКА) Тогда
					Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ = СтатусСообщ;
				Иначе
					Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ = Проч.СТАТСООБЩ_НЕОПРЕД;
				КонецЕсли;
				ФлагИзменения = Истина
			КонецЕсли
		КонецЕсли;
		Если КомментСообщ <> Неопределено Тогда
			Если КомментСообщ <> Сообщ.ТабЗнач[ИдСообщ].КомментарийСообщ Тогда
				Сообщ.ТабЗнач[ИдСообщ].КомментарийСообщ = КомментСообщ;
				ФлагИзменения = Истина
			КонецЕсли
		КонецЕсли;
		Если ФлагИзменения = Истина Тогда
			// Прорисовка, если реально було обновление
			Сообщения_Обновить(Сообщ)
		КонецЕсли
	КонецЕсли

КонецПроцедуры

// Удаляет сообщение с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
//
Процедура Сообщения_Удалить(Сообщ, ИдСообщ) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		Сообщ.ТабЗнач.Удалить(ИдСообщ);
		Сообщения_Обновить(Сообщ)
	КонецЕсли
	
КонецПроцедуры

// Удаляет все сообщения из таблицы
//
// Параметры:
//  Сообщ - ссылка на объект
//
Процедура Сообщения_Очистить(Сообщ) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (Сообщ.ТабЗнач.Количество() > 0) Тогда
		Сообщ.ТабЗнач.Очистить();
		Сообщения_Обновить(Сообщ)
	КонецЕсли
	
КонецПроцедуры

// Возвращает тип сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
// Возврат:
//  Тип сообщения или Неопределено, если сообщение не найдено
//
Функция Сообщения_ПолучитьТип(Сообщ, ИдСообщ) Экспорт
	
	Рез = Неопределено;
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		Рез = Сообщ.ТабЗнач[ИдСообщ].ТипСообщ
	КонецЕсли;
	 
	Возврат Рез
	
КонецФункции

// Устанавливает тип сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
//  ТипСообщ - тип сообщения
//
Процедура Сообщения_УстановитьТип(Сообщ, ИдСообщ, ТипСообщ) Экспорт

	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) И
		 (ТипСообщ <> Сообщ.ТабЗнач[ИдСообщ].ТекстСообщ) И
		 (ТипСообщ >= Проч.ТИПСООБЩ_УРОВЕНЬ1) И (ТипСообщ <= Проч.ТИПСООБЩ_УРОВЕНЬ3) Тогда
		Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = ТипСообщ;
		Сообщения_Обновить(Сообщ)
	КонецЕсли;
	 
КонецПроцедуры

// Возвращает статус сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
// Возврат:
//  Статус сообщения или Неопределено, если сообщение не найдено
//
Функция Сообщения_ПолучитьСтатус(Сообщ, ИдСообщ) Экспорт
	
	Рез = Неопределено;
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		 Рез = Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ
	КонецЕсли;
	 
	Возврат Рез
	
КонецФункции

// Устанавливает статус сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
//  СтатусСообщ - статус сообщения
//
Процедура Сообщения_УстановитьСтатус(Сообщ, ИдСообщ, СтатусСообщ) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) И
		 (СтатусСообщ <> Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ) Тогда
		Если (СтатусСообщ <> Неопределено) И
			 (СтатусСообщ >= Проч.СТАТСООБЩ_ОК) И (СтатусСообщ <= Проч.СТАТСООБЩ_ОШИБКА) Тогда
			Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ = СтатусСообщ;
		Иначе
			Сообщ.ТабЗнач[ИдСообщ].СтатусСообщ = Проч.СТАТСООБЩ_НЕОПРЕД;
		КонецЕсли;
		Сообщения_Обновить(Сообщ)
	КонецЕсли
	
КонецПроцедуры

// Возвращает текст сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
// Возврат:
//  Текст сообщения или Неопределено, если сообщение не найдено
//
Функция Сообщения_ПолучитьТекст(Сообщ, ИдСообщ) Экспорт
	
	Рез = Неопределено;
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		Рез = Сообщ.ТабЗнач[ИдСообщ].ТекстСообщ
	КонецЕсли;
	 
	Возврат Рез
	
КонецФункции

// Устанавливает текст сообщения с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
//  ТекстСообщ - текст добавляемого сообщения;
//
Процедура Сообщения_УстановитьТекст(Сообщ, ИдСообщ, ТекстСообщ) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) И
		 (ТекстСообщ <> Неопределено) И
		 (ТекстСообщ <> Сообщ.ТабЗнач[ИдСообщ].ТипСообщ) Тогда
		// Преобразовать текст
		Если Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ1 Тогда
			
		ИначеЕсли Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ2 Тогда
			ТекстСообщ = "  " + ТекстСообщ
		ИначеЕсли Сообщ.ТабЗнач[ИдСообщ].ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ3 Тогда
			ТекстСообщ = "    " + ТекстСообщ
		КонецЕсли;
		// Установить текст
		Сообщ.ТабЗнач[ИдСообщ].ТекстСообщ = ТекстСообщ;
		Сообщения_Обновить(Сообщ)
	КонецЕсли
	
КонецПроцедуры

// Возвращает комментарий к сообщению с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
// Возврат:
//  Комментарий сообщения или Неопределено, если сообщение не найдено
//
Функция Сообщения_ПолучитьКоммент(Сообщ, ИдСообщ) Экспорт
	
	Рез = Неопределено;
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) Тогда
		Рез = Сообщ.ТабЗнач[ИдСообщ].КомментарийСообщ
	КонецЕсли;
	 
	Возврат Рез
	
КонецФункции

// Устанавливает комментарий к сообщению с идентификатором ИдСообщ
//
// Параметры:
//  Сообщ - ссылка на объект
//  ИдСообщ - идентификатор сообщения (порядковый номер строки)
//  КомментСообщ - комментарий к сообщению
//
Процедура Сообщения_УстановитьКоммент(Сообщ, ИдСообщ, КомментСообщ) Экспорт
	
	Если (Сообщ.ТабЗнач <> Неопределено) И
		 (ИдСообщ <> Неопределено) И
		 (ИдСообщ >= 0) И (ИдСообщ < Сообщ.ТабЗнач.Количество()) И
		 (КомментСообщ <> Сообщ.ТабЗнач[ИдСообщ].КомментарийСообщ) Тогда
		Сообщ.ТабЗнач[ИдСообщ].КомментарийСообщ = КомментСообщ;
		Сообщения_Обновить(Сообщ)
	КонецЕсли
	
КонецПроцедуры

// В зависимости от типа сообщения и статуса определяет цвет текста сообщения
//
// Параметры:
//  ТипСообщ - тип сообщения
//  СтатусСообщ - статус сообщения
// Возврат:
//  Цвет текста сообщения
//
Функция Сообщения_ЦветТекста(ТипСообщ, СтатусСообщ) Экспорт
	
	Рез = Неопределено;
	Если ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ1 Тогда
		Рез = WebЦвета.Черный
	ИначеЕсли ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ2 Тогда
		Рез = WebЦвета.Черный
	ИначеЕсли ТипСообщ = Проч.ТИПСООБЩ_УРОВЕНЬ3 Тогда
		Рез = WebЦвета.Черный
	КонецЕсли;
	
	Возврат Рез
	
КонецФункции

// В зависимости от типа сообщения и статуса определяет цвет текста комментария
//
// Параметры:
//  ТипСообщ - тип сообщения
//  СтатусСообщ - статус сообщения
// Возврат:
//  Цвет текста комментария
//
Функция Сообщения_ЦветКомментария(ТипСообщ, СтатусСообщ) Экспорт
	
	Рез = Неопределено;
	Если СтатусСообщ = Проч.СТАТСООБЩ_ОК Тогда
		Рез = WebЦвета.Зеленый
	ИначеЕсли СтатусСообщ = Проч.СТАТСООБЩ_ПРЕДУПР Тогда
		Рез = WebЦвета.Оранжевый
	ИначеЕсли СтатусСообщ = Проч.СТАТСООБЩ_ОШИБКА Тогда
		Рез = WebЦвета.Красный
	КонецЕсли;
	
	Возврат Рез
	
КонецФункции

// В зависимости от типа сообщения и статуса определяет картинку статуса сообщения
//
// Параметры:
//  ТипСообщ - тип сообщения
//  СтатусСообщ - статус сообщения
// Возврат:
//  Картинка статуса сообщения
//
Функция Сообщения_Картинка(ТипСообщ, СтатусСообщ) Экспорт
	
	Рез = Неопределено;
	Если СтатусСообщ = Проч.СТАТСООБЩ_ОК Тогда
		Рез = КоллекцияКартинок.СообщениеСтатусОК
	ИначеЕсли СтатусСообщ = Проч.СТАТСООБЩ_ПРЕДУПР Тогда
		Рез = КоллекцияКартинок.СообщениеСтатусПредупреждение
	ИначеЕсли СтатусСообщ = Проч.СТАТСООБЩ_ОШИБКА Тогда
		Рез = КоллекцияКартинок.СообщениеСтатусОшибка
	КонецЕсли;
	
	Возврат Рез

КонецФункции

// Запрашивает имя файла и сохраняет набор сообщений в текстовый файл
//
// Параметры:
//  Сообщ - ссылка на объект
// Возврат:
//  Истина, если сохранение выполнено и Ложь - в противном случае
//
Функция Сообщения_СохранитьВФайл(Сообщ) Экспорт
	
	Рез = Ложь;	// Еще ничего не сохранили
	
	Если Сообщ.ТабЗнач = Неопределено  Тогда
		// Неверные параметры
		Возврат Рез
	КонецЕсли;
	
	// Создать диалог и выбрать файл для сохранения
	ДиалогВыбораФайла								=	Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
	
	ДиалогВыбораФайла.Фильтр						=	"Текстовые файлы (*.txt)|*.txt" +
														"|Все файлы (*.*)|*.*";
	ДиалогВыбораФайла.Заголовок						=	"Сохранение сообщений";
	ДиалогВыбораФайла.МножественныйВыбор            =   Ложь;
	ДиалогВыбораФайла.ПредварительныйПросмотр		=	Ложь;
	ДиалогВыбораФайла.Расширение					=	"txt";
	ДиалогВыбораФайла.ИндексФильтра					=	0;
	ДиалогВыбораФайла.ПроверятьСуществованиеФайла	=	Истина;
	
	Если ДиалогВыбораФайла.Выбрать() Тогда
		// Выполнить сохранение
		Попытка
			// Открыть файл
			ЗаписьТекста = Новый ЗаписьТекста(ДиалогВыбораФайла.ПолноеИмяФайла, КодировкаТекста.ANSI);
			
			// Сформировать и записать в файл строки сообщений 
			// Выяснить максимальные длины строк в сообщениях
			ДлинаТекстаМакс = 0;
			ДлинаКомментМакс = 0;
			Для Каждого СтрокаТЗ Из Сообщ.ТабЗнач Цикл
				ДлинаТекстаТек = СтрДлина(СтрокаТЗ.ТекстСообщ);
				Если ДлинаТекстаТек > ДлинаТекстаМакс Тогда
					ДлинаТекстаМакс = ДлинаТекстаТек;
				КонецЕсли;
				ДлинаКомментТек = СтрДлина(СтрокаТЗ.КомментарийСообщ);
				Если ДлинаКомментТек > ДлинаКомментМакс Тогда
					ДлинаКомментМакс = ДлинаКомментТек;
				КонецЕсли;
			КонецЦикла;
			// Выполнить запись строк
			Для Каждого СтрокаТЗ Из Сообщ.ТабЗнач Цикл
				Дист = "  ";  // Разделитель элементов строки
				Стр = "";
				
				// Записать в строку статус сообщения
				Если (СтрокаТЗ.СтатусСообщ <> Неопределено) И
					 (СтрокаТЗ.СтатусСообщ <> Проч.СТАТСООБЩ_НЕОПРЕД) Тогда
					СтатусСтр = "";
					Если СтрокаТЗ.СтатусСообщ = Проч.СТАТСООБЩ_ОК Тогда
						СтатусСтр = "ОК";
					ИначеЕсли СтрокаТЗ.СтатусСообщ = Проч.СТАТСООБЩ_ПРЕДУПР Тогда
						СтатусСтр = "ПРЕДУПРЕЖДЕНИЕ";
					ИначеЕсли СтрокаТЗ.СтатусСообщ = Проч.СТАТСООБЩ_ОШИБКА Тогда
						СтатусСтр = "ОШИБКА";
					КонецЕсли;
					Если Стр <> "" Тогда
						Стр = Стр + Дист
					КонецЕсли;
					Стр = Стр + РасширСтрПрав(СтатусСтр, 14);
				КонецЕсли;
				// Записать в строку текст сообщения
				Если (СтрокаТЗ.ТекстСообщ <> Неопределено) И
					 (СтрокаТЗ.ТекстСообщ <> "") Тогда
					Если Стр <> "" Тогда
						Стр = Стр + Дист
					КонецЕсли;
					Стр = Стр + РасширСтрПрав(СтрокаТЗ.ТекстСообщ, ДлинаТекстаМакс);
				КонецЕсли;
				// Записать в строку текст комментария
				Если (СтрокаТЗ.КомментарийСообщ <> Неопределено) И
					 (СтрокаТЗ.КомментарийСообщ <> "") Тогда
					Если Стр <> "" Тогда
						Стр = Стр + Дист
					КонецЕсли;
					Стр = Стр + РасширСтрПрав(СтрокаТЗ.КомментарийСообщ, ДлинаКомментМакс);
				КонецЕсли;
				// Записать строку в файл
				Если Стр <> "" Тогда
					ЗаписьТекста.ЗаписатьСтроку(Стр)
				КонецЕсли
			КонецЦикла;
			
			// Закрыть файл
			ЗаписьТекста.Закрыть();
			
			Рез = Истина	// Операция выполнена успешно
		Исключение
			// Сообщение об ошибке
			Предупреждение("Ошибка сохранения сообщений:" + Символы.ПС +
							ИнформацияОбОшибке().Описание);
		КонецПопытки
	КонецЕсли;

	Возврат Рез
	
КонецФункции




////////////////////////////////////////////////////////////////////////////////
// ОПЕРАТОРЫ ОСНОВНОЙ ПРОГРАММЫ - НАЧАЛЬНАЯ ИНИЦИАЛИЗАЦИЯ



// ИНИЦИАЛИЗАЦИЯ КОНСТАНТ

// Константы общего назначения
Проч = ИнициализацияПрочихКонстант();

// Коллекция картинок
КоллекцияКартинок = СформироватьКоллекциюКартинок();



// ИНИЦИАЛИЗАЦИЯ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ

// Создать машину JavaScript
JavaScript = СоздатьОбъектJavaScript();


Обратите внимание на то, что при выполнении длительного процесса в цикле, интерфейс пользователя блокируется, и поэтому факт добавления или изменения сообщений не будет виден пользователю – прорисовки не будет. Для принудительного выполнения прорисовки, нужно вызвать метод Обновить() формы, отображающей сообщения. Но частый вызов этого метода затормозит работу, поэтому вызывать его нужно лишь иногда (хоть и с небольшим интервалом, чтобы пользователь не заметил лагов – в данной реализации интервал обновления не чаще чем 1 раз в 0,5сек). Для реализации обновления служит метод класса Сообщения_Обновить.

Пример использования.


В данном примере форма, отображающая набор сообщений выглядит вот так:


Вместо кнопки «Запустить процесс», в реальности должно быть что-то более полезное – какой-то пользовательский интерфейс. Кнопка «Запустить процесс» имитирует заполнение списка сообщений, демонстрируя то, как могли бы выводиться сообщения в процессе реального выполнения какого-либо длительного действия.
В качестве реквизитов формы создана ТаблицаЗначений — «Сообщения». Мы ее никак не инициализируем, а просто передаем в качестве параметров соответствующим методам класса «Сообщения_», а они уже создают нужные колонки:
ОбъектСообщ = Сообщения_Конструктор();
Сообщения_УстАтриб(ОбъектСообщ, ЭтаФорма, ЭлементыФормы.ТабличноеПолеСообщения, Сообщения);
Сообщения_Инициализация(ОбъектСообщ);


Для отображения ТаблицыЗначений «Сообщения», на форме размещен визуальный компонент типа «ТабличноеПоле» — «ТабличноеПолеСообщения». При создании колонок в ТабличномПоле, учитываем следующие колонки ТаблицыЗначений (описывались ранее):
ТипСообщ;
СтатусСообщ;
ТекстСообщ;
КомментарийСообщ.

Текст модуля главной формы обработки (отображающей сообщения):
Текст модуля формы
////////////////////////////////////////////////////////////////////////////////
// ОСНОВНОЕ ОКНО ПРОГРАММЫ

Перем ОбъектСообщ;          // Объект для вывода сообщений в таблицу знач. и отобр. в табл. поле


////////////////////////////////////////////////////////////////////////////////
// ОБРАБОТЧИКИ СОБЫТИЙ ФОРМЫ

// Процедура - обработчик события "При открытии" формы. Данное событие
// возникает при открытии формы, до показа окна пользователю.
//
// Параметры:
//  Нет.
//
Процедура ПриОткрытии()
	
	// Создание объекта сообщений и инициализация таблицы сообщений
	ОбъектСообщ = Сообщения_Конструктор();
	Сообщения_УстАтриб(ОбъектСообщ, ЭтаФорма, ЭлементыФормы.ТабличноеПолеСообщения, Сообщения);
	Сообщения_Инициализация(ОбъектСообщ);
	
КонецПроцедуры

// Процедура - обработчик события "При закрытии" формы. Данное событие
// возникает после закрытия формы.
// Освобождение занятых ресурсов.
//
// Параметры:
//  Нет.
//
Процедура ПриЗакрытии()
	
	// Завершить работу с объектом вывода сообщений
	Сообщения_Деструктор(ОбъектСообщ)
	
КонецПроцедуры


// Процедура - обработчик события "Нажатие" кнопки КнопкаЗапуститьПроцесс.
// Имитирует операцию, в процессе выполнения которой выводятся сообщения.
//
// Параметры:
//  Элемент - кнопка.
//
Процедура КнопкаЗапуститьПроцессНажатие(Элемент)
	
	// Подготовка данных к началу операции
	ГСЧ = Новый ГенераторСлучайныхЧисел();
    ВсегоОбъектов = ГСЧ.СлучайноеЧисло(1, 100);
	// Запросить согласие пользователя на выполнение операции
	РезВопр = Вопрос("Выполнить обработку данных?",
			 		 РежимДиалогаВопрос.ДаНет,
				 	 ,
					 ,
				 	 "Обработка данных");
	Если РезВопр = КодВозвратаДиалога.Да Тогда
		// Подготовка интерфейса к запуску процесса
		ErrMsg = "";
		Сообщения_Очистить(ОбъектСообщ);
		// Сообщение о начале операции
		Сообщения_Добавить(ОбъектСообщ, Проч.ТИПСООБЩ_УРОВЕНЬ1, "*** НАЧАЛО ОБРАБОТКИ ДАННЫХ ***");
		Рез = Ложь;
		КолОбрОб = 0;
		Попытка
			// Выполнять обработку объектов
			Для Инд=1 По ВсегоОбъектов Цикл
				// Вывести сообщение о том, что началась обработка очередного объекта
				ИдСообщУр2 = Сообщения_Добавить(ОбъектСообщ, Проч.ТИПСООБЩ_УРОВЕНЬ2, "Обработка объекта " + Формат(Инд, "ЧГ=0; ЧН=''"));
				
				// ******************************
				// Здесь собственно выполняется сама операция над текущим объектом
				Если ГСЧ.СлучайноеЧисло(0, ВсегоОбъектов) > 0 Тогда
					// Считаем, что операция над текущим объектом выполнена успешно
					Рез = Истина
				Иначе
					// Считаем, что операция над текущим объектом выполнена с ошибкой
					Рез = Ложь
				КонецЕсли;
				// ******************************
				
				// Установить результат операции над текущим объектом в окне сообщений
				СтатСообщ = Проч.СТАТСООБЩ_НЕОПРЕД;
				КоммСообщ = "";
				Если Рез Тогда
					// Операция завершена удачно
					СтатСообщ = Проч.СТАТСООБЩ_ОК;
					КоммСообщ = "ОК"
				Иначе
					// Ошибки при выполнении операции
					СтатСообщ = Проч.СТАТСООБЩ_ОШИБКА;
					КоммСообщ = "ОШИБКА"
				КонецЕсли;
				Сообщения_Изменить(ОбъектСообщ, ИдСообщУр2, , , СтатСообщ, КоммСообщ);
								   
				// Анализируем результат текущей итерации														  
				Если НЕ Рез Тогда
					// Ошибка - прервать работу
					Прервать
				КонецЕсли;
				КолОбрОб = КолОбрОб + 1
			КонецЦикла
		Исключение
			// Возникло исключение - ошибка
			ErrMsg = ИнформацияОбОшибке().Описание;
			Рез = Ложь
		КонецПопытки;
		// Сообщение об окончании операции
		Если Рез Тогда
			// Операция завершена успешно
			Сообщения_Добавить(ОбъектСообщ, Проч.ТИПСООБЩ_УРОВЕНЬ1, "*** ОБРАБОТКА ДАННЫХ ЗАВЕРШЕНА ***");
		Иначе
			// Операция прервана
			Сообщения_Добавить(ОбъектСообщ, Проч.ТИПСООБЩ_УРОВЕНЬ1, "*** ОБРАБОТКА ДАННЫХ ПРЕРВАНА ***", Проч.СТАТСООБЩ_ОШИБКА, ErrMsg)
		КонецЕсли;
    	// Установка интерфейса по окончанию загрузки
		ТекстСообщ = "";
		Если Рез Тогда
			ТекстСообщ = "Обработка выполнена успешно"
		Иначе
			ТекстСообщ = "Обработка выполнена с ошибками"
		КонецЕсли;
		ТекстСообщ = ТекстСообщ + Символы.ПС;
		ТекстСообщ = ТекстСообщ + "Обработано " + Формат(КолОбрОб, "ЧГ=0; ЧН=''") + " объектов.";
		Предупреждение(ТекстСообщ, , "Обработка данных")
	КонецЕсли
КонецПроцедуры

// Процедура - обработчик события "ПриВыводеСтроки" табличного поля
// ТабличноеПолеСообщения. Данное событие происходит при выводе каждой строки.
//
// Параметры:
//  Элемент - табличное поле;
//  ОформлениеСтроки - объект настройки визуального оформления строки
//  ДанныеСтроки - набор данных строки табличного поля.
//
Процедура ТабличноеПолеСообщенияПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
	
	// Оформление ячейки "Статус"
	ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.ОтображатьТекст = Ложь;
	КартинкаСтатуса = Сообщения_Картинка(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
	Если КартинкаСтатуса <> Неопределено Тогда
		ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.ОтображатьКартинку = Истина;
		ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.Картинка = КартинкаСтатуса;
	КонецЕсли;
	// Оформление ячейки "Текст"
	Если (ДанныеСтроки.ТекстСообщ <> Неопределено) И
		 (ДанныеСтроки.ТекстСообщ <> "") Тогда
		ЦветТекста = Сообщения_ЦветТекста(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
		Если ЦветТекста <> Неопределено Тогда
			ОформлениеСтроки.Ячейки.КолонкаТекстСообщения.ЦветТекста = ЦветТекста
		КонецЕсли
	КонецЕсли;
	// Оформление ячейки "Комментарий"
	Если (ДанныеСтроки.КомментарийСообщ <> Неопределено) И
		 (ДанныеСтроки.КомментарийСообщ <> "") Тогда
		ЦветКомментария = Сообщения_ЦветКомментария(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
		Если ЦветКомментария <> Неопределено Тогда
			ОформлениеСтроки.Ячейки.КолонкаКомментарийСообщения.ЦветТекста = ЦветКомментария
		КонецЕсли
	КонецЕсли
	
КонецПроцедуры

// Процедура - обработчик события "Нажатие" кнопки КнопкаСообщенияОткрытьВНовомОкне.
// Открывает текущий список сообщений в новом окне.
//
// Параметры:
//  Элемент - кнопка.
//
Процедура КнопкаСообщенияОткрытьВНовомОкнеНажатие(Элемент)

	ФормаПросмотраСообщений = ЭтотОбъект.ПолучитьФорму("ФормаПросмотраСообщений", ЭтаФорма);
	ФормаПросмотраСообщений.ShowMessagesDlg(ОбъектСообщ)
	
КонецПроцедуры

// Процедура - обработчик события "Нажатие" кнопки КнопкаСообщенияСохранить.
// Сохранить текущий набор сообщений в файл.
//
// Параметры:
//  Элемент - кнопка.
//
Процедура КнопкаСообщенияСохранитьНажатие(Элемент)

	Сообщения_СохранитьВФайл(ОбъектСообщ);
	
КонецПроцедуры


Форма просмотра сообщений.


Также имеется отдельное окно просмотра списка сообщений (соответствующей ТаблицыЗначений). Оно предназначено для того, чтобы можно было более удобно проанализировать большой список (так как в основном диалоговом окне ТабличноеПоле сообщений сильно растянуть по высоте не удастся – будет мешать другие элементы интерфейса).

Выглядит форма примерно следующим образом:


Код модуля формы совсем несложный. Вот такой:
Текст модуля формы
////////////////////////////////////////////////////////////////////////////////
// ДИАЛОГ ДЛЯ ОТОБРАЖЕНИЯ СПИСКА СООБЩЕНИЙ В ОТДЕЛЬНОМ ОКНЕ

Перем ОбъектСообщ;      // Объект для работы с сообщениями



////////////////////////////////////////////////////////////////////////////////
// ОБРАБОТЧИКИ СОБЫТИЙ ФОРМЫ

// Процедура - обработчик события "Действие" элемента меню КоманднаяПанельМенюСохранитьВФайл.
// Сохранить текущий набор сообщений в файл.
//
// Параметры:
//  Кнопка - элемент меню.
//
Процедура КоманднаяПанельМенюСохранитьВФайл(Кнопка)
	
	Сообщения_СохранитьВФайл(ОбъектСообщ);

КонецПроцедуры

// Процедура - обработчик события "ПриВыводеСтроки" табличного поля
// ТабличноеПолеСообщения. Данное событие происходит при выводе каждой строки.
//
// Параметры:
//  Элемент - табличное поле;
//  ОформлениеСтроки - объект настройки визуального оформления строки
//  ДанныеСтроки - набор данных строки табличного поля.
//
Процедура ТабличноеПолеСообщенияПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
	
	// Оформление ячейки "Статус"
	ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.ОтображатьТекст = Ложь;
	КартинкаСтатуса = Сообщения_Картинка(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
	Если КартинкаСтатуса <> Неопределено Тогда
		ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.ОтображатьКартинку = Истина;
		ОформлениеСтроки.Ячейки.КолонкаСтатусСообщения.Картинка = КартинкаСтатуса;
	КонецЕсли;
	// Оформление ячейки "Текст"
	Если (ДанныеСтроки.ТекстСообщ <> Неопределено) И
		 (ДанныеСтроки.ТекстСообщ <> "") Тогда
		ЦветТекста = Сообщения_ЦветТекста(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
		Если ЦветТекста <> Неопределено Тогда
			ОформлениеСтроки.Ячейки.КолонкаТекстСообщения.ЦветТекста = ЦветТекста
		КонецЕсли
	КонецЕсли;
	// Оформление ячейки "Комментарий"
	Если (ДанныеСтроки.КомментарийСообщ <> Неопределено) И
		 (ДанныеСтроки.КомментарийСообщ <> "") Тогда
		ЦветКомментария = Сообщения_ЦветКомментария(ДанныеСтроки.ТипСообщ, ДанныеСтроки.СтатусСообщ);
		Если ЦветКомментария <> Неопределено Тогда
			ОформлениеСтроки.Ячейки.КолонкаКомментарийСообщения.ЦветТекста = ЦветКомментария
		КонецЕсли
	КонецЕсли
	
КонецПроцедуры


////////////////////////////////////////////////////////////////////////////////
// ПОДПРОГРАММЫ ВНЕШНЕГО ИНТЕРФЕЙСА


// Вызов диалога просмотра списка сообщений
//
// Параметры:
//  ОбъектСообщения - объект для работы с сообщениями.
//
Процедура ShowMessagesDlg(ОбъектСообщений) Экспорт;

	Если ОбъектСообщений <> Неопределено Тогда
		// Выполнить начальную установку параметров
		ОбъектСообщ = ОбъектСообщений;
		ЭлементыФормы.ТабличноеПолеСообщения.Значение = ОбъектСообщ.ТабЗнач;
		// Вызвать диалог
		ЭтаФорма.ОткрытьМодально()
	КонецЕсли
	
КонецПроцедуры


Скачать исходные тексты подсистемы сообщений и рассмотренный пример (обработка для 1С 8.2) можно здесь.

На этом все. Всем удачи. До встречи.

P.S.

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

Мы создадим обособленные и переносимые наборы подпрограмм («классов»):
— для подключения к базе (сможем подключаться к серверу, базе и выполнять запросы);
— для работы с параметрами запроса (можно будет задавать и устанавливать параметры почти как в Delphi);
А также:
— для работы с множествами целых чисел и строк (удобно задавать множественные параметры для запросов);
— удобное хранилище профилей подключений к различным базам FireBird, а также интерфейс пользователя (форма для редактирования набора профилей подключений)

В общем, думаю, что будет интересно. Еще раз, удачи…

Комментарии (3)


  1. Melex
    03.03.2016 22:23
    +3

    Скучно, откуда в тегах 8.2? Тут смело можно оставлять 8.0, актуальность статьи где то на 2005 год тянет.
    Без обид, просто если бы вы это показали на управляемых формах, где надо данные гонять между клиентом и сервером, работать с функциями оповещения пользователя и т.д. и т.п., то это было бы позновательно многим.

    И дайте я угадаю — к базе FireBird вы будете подключаться через ком?
    Т.к. я сомневаюсь что вы покажете принципы работы с кубами и внешними источниками, что в принципе было бы интересно многим :)


    1. malex
      04.03.2016 08:48

      Поддреживаю


  1. boffart
    04.03.2016 09:21

    Это не жизненно и точно не удобно в использовании. Не увидел новизны.
    Стандартный метод "Сообщить" довольно функционален, есть возможность выделения сообщений.
    Какой либо расширенный лог следует выводить в журнал регистрации 1С.
    Для "ресурсоемких" и длительных операций принято использовать фоновые задачи.