Можно сказать, по многочисленным просьбам зрителей. Итак, спрашивали, как устроено в наших программах окошко «О программе» с «эффектом титров». Т.е. список разработчиков и прочее медленно выплывает снизу, проползает по форме и исчезает наверху, часть всплывающих строк останавливается в центре формы, в конце выплывает слоган, так же останавливающийся на некоторое время в центре.
Смотреть анимацию
На самом деле, форма была устроена ужасно, написана совсем другими людьми для другого проекта, а у нас кочевала из программы в программу без особых изменений, строки добавлялись в 3 ArrayList
в коде формы, в функции рисования были какие-то непонятные жестко заданные поправочные коэффициенты, подобранные на глаз, и т.д.
Решили немного поправить и все переделать. Не знаю, насколько получилось лучше, но понятнее и универсальней точно. Далее будет не столько код с пояснениями, сколько попытка показать, как мы рассуждали, переделывая форму.
Определение сущностей
Сначала надо определить с какими данными мы будем работать. В первую очередь, мы выводим набор строк, т.е. первая сущность это строка, у строки может быть задан шрифт и цвет текста, и, собственно, содержимое строки — это будут явные параметры сущности. В процессе добавился еще один, неявный параметр — номер сцены
Шрифт целесообразно выделить в отдельную сущность. Во-первых, все используемые шрифты желательно загрузить и сформировать до вывода строк, во-вторых, у шрифта куча своих параметров, и задавать их для каждой отдельной строки неудобно.
Параметры шрифта это FontFamily (Arial, Times New Roman и т.д.), размер и начертание или стиль (жирный, курсив, подчеркнутый, зачеркнутый, или все вместе). Для того, чтобы связать шрифт со строкой, пришлось добавить еще один параметр внутреннее имя, по которому программа будет узнавать, какой шрифт к какой строке применить.
Последняя сущность это группа строк, или сцена, поскольку строки выводятся группами (см. анимацию). К сцене мы приписали следующие параметры: цвет фона, скорость прокрутки строк (технически задается как таймаут таймера прорисовки) и время паузы (для эффекта остановки группы строк посередине формы, см. анимацию). Неявным параметром будет номер, его зададим позже автоматически, а также высота всех строк сцены и их количество.
Формат данных
Негоже задавать эту кучу параметров непосредственно в коде, поэтому пришлось подумать над форматом данных. Решено было хранить описание строк, шрифтов и сцен в виде простого текстового файла следующего формата:
...
команда;параметр1;параметр2;параметр3 [...]
команда;параметр1;параметр2;параметр3 [...]
команда;параметр1;параметр2;параметр3 [...]
...
Формат команд следующий (в квадратных скобках необязательные параметры):
Шрифт:
addfont; FontFamily; Размер (pt, float); [Стиль]; Внутреннее_имя
Например:
addfont; Arial;9;Bold;Group;
addfont; Microsoft Sans Serif;8;;Names;
addfont; Arial;14;Bold italic;Slogan;
Строка:
addstring; Строка; [Имя_шрифта]; [цвет_текста];
Например:
addstring;Make code, not war! C# like you.;Slogan;FF FF 00;
Сцена:
scene; [цвет_фона];[скорость_таймера_прорисовки ms];[пауза ms]
Например:
scene;20;3000;
Цвет текста задается в шестнадцатеричном формате через пробел(ы): R G B [Alpha]
Например:
FF FF 00
— желтый
FF 00 00 80
— красный с прозрачностью
Начертания (стили) шрифта перечисляются через пробел(ы), от регистра не зависят.
Структуры для хранения данных
Шрифты:
Генерируются при анализе «скрипта», и помещаются в приватный Dictionary
, где строковый ключ — заданное «внутреннее имя» шрифта.
Строка:
Текст и параметры хранятся в специальной структуре:
private struct AboutString
{
public string Text;
public string FontName;
public Color TextColor;
public int SceneNumber;
}
А все строки в private List Strings
Сцена:
Для описания сцены так же создана структура:
private struct AboutScene
{
public int PauseTimeout;
public int StringsHeight;
public int StringsCount;
public Color BackColor;
public int DrawTimeout;
}
И описания сцен так же хранятся в private List Scenes
Заметки про парсинг
— последняя точка с запятой является необязательной
— пустые строки, или строки, содержащие только пробельные символы, пропускаются
— команды приводятся к нижнему регистру (т.е. они регистронезависимы)
— есть возможность оставить однострочный комментарий, начинающейся с ~ (тильды). Все, что после знака ~ считается комментарием и пропускается при анализе «скрипта».
— если первая (нулевая) сцена явно не определена, то создается сцена с параметрами по умолчанию
Команды, благо их немного, последовательно анализируются с помощью конструкции switch/case
и передаются в функции добавления шрифта, строк, или сцены, где анализируются далее.
Параметры и команда разбираются с помощью string.split(';');
Некоторые вспомогательные функции для парсинга
Размер шрифта должен быть float
, поэтому надо сделать функцию-обертку над Convert, так, как я писал об этом ранее:
private float ConvertToFloat(string Number)
{
NumberFormatInfo format = new NumberFormatInfo();
format.NumberDecimalSeparator = ".";
return (float)Convert.ToDouble(Number, format);
}
Некоторые параметры передаются через пробел (значения RGBA цвета, или стили шрифта). Нужна функция, которая будет избавляться от дублирующих пробелов, поскольку лишний пробел поставить легко, а вот вылавливать его потом неприятно:
private string RemoveDupSpaces(string s)
{
while (s.Contains(" "))
{
s = s.Replace(" ", "");
}
return s;
}
Заметки о реализации
Естественно, весь функционал не стоит реализовывать прямо на форме, потому вся работа вынесена в отдельный класс AboutDrawer
, который парсит переданный скрипт и занимается отрисовкой строк на форме.
На форме строки отображаются в компоненте PictureBox
, который передается в конструктор класса. Конструктор также устанавливает некоторые параметры по умолчанию.
Поскольку, класс все равно работает с компонентами формы, то спокойно подключаем пространство имен System.Windows.Forms
, и пользуемся всеми его возможностями. Например, в классе создаются два таймера (Timer
). Внутри обработчика события Tick
одного из них, происходит отрисовка, а другой используется для эффекта паузы.
В классе созданы открытые свойства для установки цвета фона, цвета текста по умолчанию, и размера расстояния между строк.
Подробнее ознакомиться с кодом примера можно на Github:
Код на GitHub