Приложение «Часы со Стрелками» на платформе WPF (Windows Presentation Foundation) .NET Core в качестве знакомства с новой моделью создания графических интерфейсов. Окно часов круглой формы, реалистичны настолько, что просто хочется снять их с экрана компьютера и повесить на стену. Недалеко то будущее, когда так и будет. Красочный интерфейс часов создан с применением только стандартных элементов, включённых в каркас WPF.
В отличие от Windows Forms графика в WPF полностью основана на технологии DirectX. Составляющие интерфейса пользователя, кнопки, диалоговые окна, списки, текстовые блоки и даже сам текст прорисовывается аппаратно-ускоренным способом. Причём нет никаких методов OnPaint(), где мы должны создавать код рисования. На мониторах с разным количеством точек на дюйм DPI, интерфейс приложений WPF сохраняет неизменное качество графики.
Для сравнения графических возможностей Windows Presetation Foundation и C++ MFC программы посмотрите исходник часов на GDI
В приложении имитации стрелочных часов демонстрируется модель компоновки WPF, совершенно отличной от принципа размещения элементов управления Windows Forms. Создаётся первый и единственный контейнер, все остальные контейнеры и элементы управления вкладываются в главный. Каждый контейнер WPF обладает собственной логикой размещения дочерних элементов в пределах своего пространства. В WPF рекомендуется использовать относительные координаты. Для изменения позиции элементов используются величины: Margin, Padding, HorizontalAlignment, VerticalAlignment контейнеров и их вложений.
В WPF легко создавать окна фигурной формы. Разработчики Windows Presentation Foundation добавили свойство Window.AllowsTransparency специально для упрощения создания окон непрямоугольной формы. Если свойству присвоить значение истинно, то окно становится прозрачным, но всё что расположено на окне видимо. Для формирования круглого окна я использовал элемент Border с закруглёнными углами. При изменении свойства Border.CornerRadius можно получать циферблат прямоугольной, с закругленными углами и даже круглой формы.
Чтобы получить окно произвольной формы, необходимо присвоить некоторым свойствам окна определенные значения: удалить или сделать фон окна прозрачным, скрыть заголовок и границу окна, позволить окну быть прозрачным.
Интерфейс программ WPF можно создавать двумя способами: используя расширяемый язык разметки в текстовых файлах XAML и классическим программным способом. В прикрепленных исходниках используются оба способа построения интерфейса. Язык XAML базируется на структуре файлов XML со своими специфическими расширениями. Файлы XAML отделяют графическую часть приложения от программной. Такое разделение даёт преимущество при командной разработке, когда дизайнеры могут сосредоточиться над графикой, а программисты работают с кодом. Индивидуальные разработчики также получают эффект повышения качества и скорости разработки, работая отдельно с графическим и программным модулями.
Для работы с графикой приложения используя язык XAML в комплект MS Visual Studio входит инструмент для дизайнеров Blend. Возможности его гораздо шире, чем редактор встроенный в Visual Studio. Например, Blend позволяет визуализировать создание анимации, осуществлять её предпросмотр и редактирование существенно увеличивая скорость разработки.
Несмотря на то, что интерфейс приложения Windows Presentation Foundation рекомендуется реализовывать в файле XAML, иногда рациональней всё же создавать графику программным способом. Речь идёт о многократно повторяющихся элементах интерфейса. Например, в прикрепленном приложении это метки циферблата: 12 часовых и 60 минутных. В программном коде, в цикле мы их легко реализуем.
// Минутная маркировка циферблата
void MinuteMarks()
{
for (int i = 0; i < 60; i++)
{
// Контейнер для метки.
// Необходим для вращения пространства
// в котором расположена декоративная метка.
var b = new Border()
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Center,
RenderTransformOrigin = new Point(0.5, 0.5),
// Базовая ориентация контейнера метки горизонтальное,
// поэтому высота играет роль толщины метки.
Height = 2
};
// В роли метки выступает элемент Border.
var b1 = new Border()
{
Background = Brushes.Brown,
// Базовая ориентация горизонтальная,
// поэтому ширина визуально является длиной метки.
Width = 10,
HorizontalAlignment = HorizontalAlignment.Right
};
b.Child = b1;
// Вращаем только сам контейнер.
var rotate = new RotateTransform(i * 6);
b.RenderTransform = rotate;
// Исключаем из маркировки метки 0 и
// через каждые 30 градусов.
// В этих местах будут часовые метки.
if (i * 6 % 30 != 0)
{
// Добавляем на циферблат контейнеры с метками.
ClockFace.Children.Add(b);
}
}
}
Анимационная графика стрелок приложения-часов построена так: создаётся контейнер-носитель, в него добавляются декоративные элементы. Так формируется статическая графика стрелок. На рисунке показана секундная стрелка. Корпус стрелки состоит из двух элементов Border, которые создают визуализацию указателя и противовеса. Благодаря графической технологии WPF "запчасти" часов выглядят очень реалистично.
Слаженное вращение всех частей стрелок обеспечивает контейнер-носитель. Функцию контейнера выполняет также элемент управления Border. Относительно контейнера дочерние элементы неподвижны. Контейнер вращается относительно центра циферблата, сохраняя положение своих элементов. Такой способ анимации стрелок значительно упрощает создание целостного вращательного движения.
Для вращения контейнера точно вокруг своего центра определяем свойство RenderTransformOrigin="0.5,0.5". Интересная деталь кода противовеса: свойство Margin имеет отрицательные значения. Такое делается тогда, когда необходимо "раздуть" элемент за пределы пространства родителя.
Для вращения стрелок часам требуется часовой механизм. Электродвигателем в приложении служат события таймера. Во время тика таймера происходит вычисление углов поворота всех стрелок часов. Часы приложения синхронизируются с системным временем компьютера. Таймер работает с разрешением один тик в 100 миллисекунд, и секундная стрелка движется синхронно с секундами цифровых часов операционной системы. Положения стрелок исчисляются из базового угла поворота 6 градусов. Секундная стрелка за 60 шагов проходит полный круг (360°) циферблата часов.
Листинг кода вычисления углов
private void Timer_Tick(object sender, EventArgs e)
{
var rotateSecondArrow = new RotateTransform();
var rotateMinuteArrow = new RotateTransform();
var rotateHourArrow = new RotateTransform();
// Данные текущего времени.
int sec = DateTime.Now.Second;
int min = DateTime.Now.Minute;
int hour = DateTime.Now.Hour;
// Вычисленный угол для секундной стрелки.
rotateSecondArrow.Angle = baseAngleNumberSystem * sec;
// Вращение стрелки на вычисленный угол.
SecondArrow.RenderTransform = rotateSecondArrow;
// Угол минутной стрелки от количества полных минут плюс
// угол секунд приведенный к долям текущей минуты.
rotateMinuteArrow.Angle = (min * baseAngleNumberSystem) + (rotateSecondArrow.Angle / 60.0);
MinuteArrow.RenderTransform = rotateMinuteArrow;
// Данные часа конвертируем в 12-часовой вид,
// вычисляем угол полных часов плюс
// угол минут приведенный к долям текущего часа.
rotateHourArrow.Angle = (hour - 12) * baseAngleHour + rotateMinuteArrow.Angle / 12;
HourArrow.RenderTransform = rotateHourArrow;
}