Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Обзор
Создать пользовательский фоновый рисунок для рабочего стола несложно. А как насчет пользовательской заставки? В этой публикации рассказывается о создании пользовательской заставки экрана с использованием Windows Presentation Framework (WPF). В приведенном коде также есть примеры интересных партикль-эффектов, сделанных с помощью анимационного ядра WPF, и пример реализации поддержки нескольких мониторов в .Net.
Эрик Климчак (Erik Klimczak) — Clarity Consulting (EN)
Загрузки:
Введение
Объединенные коммуникации компании Майкрософт (Microsoft Unified Communications, UC) — это активно обсуждаемая в последнее время новая технология. По сути, UC интегрирует обмен текстовыми, голосовыми и видеосообщениями между приложениями и устройствами, применяемыми людьми в повседневной практике. Дополнительные сведения о UC можно найти здесь. Одна из самых крутых возможностей UC — концепция «присутствия» (см. ниже). Индикатор присутствия позволяет пользователям отображать свое присутствие в приложениях, поддерживающих Office Communicator Server (таких, как Outlook, Office communicator, Live Meeting и др.).
Работая над другим проектом (связанным с коммуникациями), я воссоздал эти пузыри-индикаторы присутствия с использованием XAML, так что их размер можно изменять без потери качества изображения (см. ниже). Для воссоздания «пузырей присутствия» я использовал Expression Design и экспортировал их в XAML.
Мне пришла в голову мысль, что эти пузыри – весьма интересный графический материал.
Я переключился на Photoshop и сделал отличный фоновый рисунок для рабочего стола в стиле офисных «пузырей присутствия».
Тут же мне пришла в голову мысль, что можно сделать и аналогичную заставку. В этой статье я пытаюсь продемонстрировать построение пользовательской заставки на базе WPF, в которой реализованы простые партикль-эффекты с использованием анимационного ядра WPF.
Партикль-анимация
Мои познания в графическом дизайне позволяют сказать, что мне требуется плавающий партикль-эффект. Партикль-эффекты применяются для имитации чего угодно – от дождя, снега и огня до пузырей и облаков, – а меняя размер «пузырей присутствия» и их прозрачность можно добиться эффекта трехмерного пространства. В результате некоторых изысканий я обнаружил в WPF обработчик события CompositionTarget.Rendering. Это событие возникает каждый раз, когда WPF решает, что пора очередной раз прорисовать кадр. В самом обработчике этого события вы можете делать что угодно с любыми объектами. Другими словами, событие CompositionTarget.Rendering дает в ваше распоряжение анимационную систему типа «сделай сам». Это событие особенно удобно для реализации анимации, основанной на физических законах, и очень напоминает поход, используемый в ядре визуализации Flash. Описание этого события в MSDN содержится здесь.
Найдя подходящий вариант для реализации анимации, я создал пользовательский элемент управления для партикль-эффектов. В нем содержатся различные партикль-атрибуты для анимации. В приведенном ниже коде различные свойства используются для визуализации весьма интересных эффектов, основанных на физических принципах. Эти свойства могут применяться для выявления столкновений, вычисления силы притяжения и обработки разных кинетических эффектов.
1: 1: public partial class Particle : UserControl
2: 2: {
3: 3:
4: 4: public Particle()
5: 5: {
6: 6:
7: 7: InitializeComponent();
8: 8: }
9: 9:
10: 0: public static readonly DependencyProperty StatusProperty = DependencyProperty.Register("Status", typeof(Status), typeof(Particle), new UIPropertyMetadata(Particle.StatusValueChanged));
11: 1: public Status Status
12: 2: {
13: 3: get { return (Status)GetValue(StatusProperty); }
14: 4: set { SetValue(StatusProperty, value); }
15: 5: }
16: 6:
17: 7: private static void StatusValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
18: 8: {
19: 9: Particle myclass = (Particle)d;
20: 0: myclass.imgStatus.Source = Application.Current.FindResource(((Status)e.NewValue).ToString() + "Png") as BitmapImage;
21: 1: }
22: 2:
23: 3: public double Radius
24: 4: {
25: 5: get { return this.imgStatus.Height / 2; }
26: 6: set { this.imgStatus.Height = this.imgStatus.Width = value * 2; }
27: 7: }
28: 8:
29: 9: private double vy;
30: 0: public double VY
31: 1: {
32: 2: get { return this.vy; }
33: 3: set { this.vy = value; }
34: 4: }
35: 5:
36: 6: private double vx;
37: 7: public double VX
38: 8: {
39: 9: get { return this.vx; }
40: 0: set { this.vx = value; }
41: 1: }
42: 2:
43: 3: private double mass;
44: 4: public double Mass
45: 5: {
46: 6: get { return this.mass; }
47: 7: set { this.mass = value; }
48: 8: }
49: 9: private double x;
50: 0: public double X
51: 1: {
52: 2: get { return this.x; }
53: 3: set { this.x = value; }
54: 4: }
55: 5:
56: 6: private double y;
57: 7: public double Y
58: 8: {
59: 9: get { return this.y; }
60: 0: set { this.y = value; }
61: 1: }
62: 2: }
Чтобы добиться желаемого анимационного эффекта, я решил дополнить свой класс реализацией некоторых функций, описывающих пружинящий эффект. Эти функции заставляют пузыри сближаться, сталкиваться и разлетаться друг от друга, подобно движению мячика на резинке, отскакивающего от ладони. Затем, введя параметры силы и скорости, я успешно (для физика-любителя) реализовал плавающий эффект. Совместное применение события CompositionTarget.Rendering и алгоритмов позиционирования позволяет эффективно распределять и управлять движением пузырей в пределах заданной области. Так выглядит программная реализация алгоритмов:
1: 1: void Spring(Particle a, Particle b)
2: 2: {
3: 3: double dx = b.X - a.X;
4: 4: double dy = b.Y - a.Y;
5: 5:
6: 6: double dist = Math.Sqrt(dx * dx + dy * dy);
7: 7: if (dist < _minDist)
8: 8: {
9: 9: double ax = dx * _spring;
10: 10: double ay = dy * _spring;
11: 11:
12: 12: a.VX += ax;
13: 13: a.VY += ay;
14: 14:
15: 15: b.VX -= ax;
16: 16: b.VY -= ay;
17: 17:
18: 18: Canvas.SetLeft(a, a.X);
19: 19: Canvas.SetTop(a, a.Y);
20: 20:
21: 21: Canvas.SetLeft(b, b.X);
22: 22: Canvas.SetTop(b, b.Y);
23: 23:
24: 24:
25: 25: }
26: 26:
27: 27: Canvas.SetLeft(a, a.X);
28: 28: Canvas.SetTop(a, a.Y);
29: 29:
30: 30: Canvas.SetLeft(b, b.X);
31: 31: Canvas.SetTop(b, b.Y);
32: 32:
33: 33:
34: 34: }
35: 35:
А это основной цикл анимации:
1: 73: void CompositionTarget_Rendering(object sender, EventArgs e)
2: 74: {
3: 75: for (int i = 0; i < _numParticles; i++)
4: 76: {
5: 77: _particles[i].X += _particles[i].VX;
6: 78: _particles[i].Y += _particles[i].VY;
7: 79:
8: 80: Particle p = _particles[i];
9: 81:
10: 82: if (p.X >this.Width)
11: 83: {
12: 84: p.X = 0;
13: 85: }
14: 86:
15: 87: elseif (p.X < 0)
16: 88: {
17: 89: p.X = this.Width;
18: 90: }
19: 91:
20: 92: if (p.Y >this.Height)
21: 93: {
22: 94: p.Y = 0;
23: 95: }
24: 96: elseif (p.Y < 0)
25: 97: {
26: 98: p.Y = this.Height;
27: 99: }
28: 00: }
29: 01:
30: 02:
31: 03: for (int i = 0; i < _numParticles - 1; i++)
32: 04: {
33: 05: var partA = _particles[i];
34: 06: for (int j = i + 1; j < _numParticles; j++)
35: 07: {
36: 08: var partB = _particles[j];
37: 09: Spring(partA, partB);
38: 10:
39: 11: }
40: 12: }
41: 13: }
Поддержка нескольких мониторов
После успешной реализации анимации, я приступил к поддержке нескольких мониторов. Для этого я написал некоторый код в начале программы, получающий описатели активных экранов. В пространстве имен System.Windows.Forms есть класс Screen, в котором хранится коллекция описателей экранов и такие их атрибуты, как рабочая область, ширина и высота. Просматривая в цикле коллекцию экранов, я измеряю границы очередного экрана и динамически устанавливаю размер окна, основываясь на допустимой области отображения.
1: 1: private void Application_Startup(object sender, StartupEventArgs e)
2: 2: {
3: 3:
4: 4: Window1 _window = null;
5: 5:
6: 6: System.Windows.Forms.Cursor.Hide();
7: 7:
8: 8: foreach (Screen screen inScreen.AllScreens)
9: 9: {
10: 0: if (screen.Primary)
11: 1: {
12: 2: Rectangle location = screen.Bounds;
13: 3: _window = newWindow1(location.Height, location.Width);
14: 4: _window.Width = location.Width;
15: 5: _window.Height = location.Height;
16: 6: _window.Left = 0;
17: 7: _window.Top = 0;
18: 8: _window.WindowState = WindowState.Maximized;
19: 9: _window.Show();
20: 0: }
21: 1:
22: 2: elseif (!screen.Primary)
23: 3: {
24: 4:
25: 5: Rectangle location = screen.Bounds;
26: 6: _window = newWindow1(location.Height, location.Width);
27: 7: _window.Left = screen.WorkingArea.Left;
28: 8: _window.Top = screen.WorkingArea.Top;
29: 9: _window.Width = location.Width;
30: 0: _window.Height = location.Height;
31: 1: _window.Show();
32: 2: }
33: 3:
34: 4: }
35: 5:
36: 6: }
Превращение анимации в экранную заставку
Последний шаг — создание заставки из всего уже сделанного. Реализация обработчиков событий, которые поступают от мыши и клавиатуры, завершающих приложение, — это то, что соответствует поведению заставки.
1: 1: MouseMove += newMouseEventHandler(Window2_MouseMove);
2: 2: MouseDown += newMouseButtonEventHandler(Window2_MouseDown);
3: 3: KeyDown += newKeyEventHandler(Window2_KeyDown);
4: 4: void Window2_KeyDown(object sender, KeyEventArgs e)
5: 5: {
6: 6: Application.Current.Shutdown();
7: 7: }
8: 8:
9: 9: void Window2_MouseDown(object sender, MouseButtonEventArgs e)
10: 0: {
11: 1: Application.Current.Shutdown();
12: 2: }
13: 3:
14: 4: void Window2_MouseMove(object sender, MouseEventArgs e)
15: 5: {
16: 6: Point currentPosition = e.MouseDevice.GetPosition(this);
17: 7:
18: 8: if (!isActive)
19: 9: {
20: 0: mousePosition = currentPosition;
21: 1: isActive = true;
22: 2: }
23: 3: else
24: 4: {
25: 5:
26: 6: if ((Math.Abs(mousePosition.X - currentPosition.X) > 10) ||
27: 7: (Math.Abs(mousePosition.Y - currentPosition.Y) > 10))
28: 8: {
29: 9: Application.Current.Shutdown();
30: 0: }
31: 1: }
32: 2: }
В завершение установите режим сборки «Release» и скомпонуйте приложение. В результирующем каталоге найдите созданный исполняемый файл и измените его расширение с .exe на .scr, щелкните его правой кнопкой и выберите в контекстном меню пункт Установить.
Вот и все!
Возможные усовершенствования
Office Communicator Server поставляется со своим SDK, который позволяет создавать собственные приложения Unified Communications.
- Интересно бы сделать так, чтобы «пузыри присутствия» в заставке были привязаны к реальным пользователям и отображали изменения их состояния анимационными эффектами.
- Полезно иметь конфигурационный файл, позволяющий пользователям определять собственные изображения и настраивать другие параметры, такие как скорость.
Завершение
У меня была прекрасная возможность блеснуть своими познаниями в физике и побаловаться с анимационными функциями WPF. Мое приложение также является отличным примером альтернативной визуализации данных. Порой надоедают сплошные таблицы и списки, а мое «присутственное искусство» — классный пример слияния искусства и алгоритмов, когда функционально полезные вещи эстетически приятны. Надеюсь, вам понравится мое творение.