Coding 4 Fun – вы больше никогда не пропустите ни одной записки

Опубликовано 28 февраля 2010 г. 20:09 | Coding4Fun

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

Автор: Ариан Т. Кулп (Arian T. Kulp)

Исходный код: загрузить

Сложность: средняя

Необходимое время: 3 часа

Затраты: бесплатно! Необязательно: Expression Blend – $249

ПО: Visual Basic или Visual C# Express (или выше), Windows API Code Pack, Expression Blend (необязательно)

Введение

В этой статье вы узнаете, как создать экранную заставку с впечатляющими визуальными эффектами, используя WPF. На вашу экранную заставку будет интересно смотреть, она будет практичной, и вам не понадобится прибегать к написанию низкоуровневого кода для управления графикой.

Я и раньше создавал экранные заставки, но с WPF имел дело не слишком часто. Моя последняя экранная заставка выглядела как снимок с Полароида и отлично работала, но была основана на функциях GDI+. Зато теперь я точно знаю, что WPF – это единственное, чем стоит пользоваться в таких случаях!

Для этого проекта вам понадобится Visual C# или Visual Basic Express Edition 2008. Если у вас есть Expression Blend, то и он весьма пригодится. Существует его пробная версия, но покупать такой продукт просто для развлечений немного дороговато.

Планирование

Поначалу я решил создать «виртуальный центр сообщений», где посетители могли бы оставлять аудио- и видеозаписи или написанные от руки записки, которые выглядели бы как стикеры, приклеенные на экран. Но в конечном счете я отказался от записи медийной информации и заменил стикеры более традиционным ListBox.

Основная разметка окна включает элемент управления сообщениями (для их набора и чтения), область «I'm away from my desk» («Меня нет на рабочем месте») и кнопку, которая сообщает, когда я вернусь. В фоне крутятся фотографии из папки изображений (это легко делается в XAML, но очень тяжело с помощью GDI+).

Сообщение об отсутствии

Для начала я взялся за сообщение об отсутствии (away message). В зависимости от заполняемых вами полей оно позволяет посетителям узнать, что вас нет на месте, где вы сейчас находитесь и когда вы вернетесь. Таймер ведет обратный отсчет, а не просто показывает время. Если вы не хотите сообщать слишком много информации, просто не заполняйте всю форму.

clip_image002

Все изменяемые поля задаются через механизм связывания с данными. За исключением обратного счетчика таймера все привязки являются статическими. Поле обратного отсчета (countdown) связано со свойством TimeSpanReturnTimeRemaining. Все свойства, используемые для связывания с данными, содержатся в UserInfo, который реализует INotifyPropertyChanged . Объекты, реализующие этот интерфейс, могут генерировать событие при изменении свойства, и, поскольку WPF обеспечивает автоматическую подписку на эти события, UI будет оставаться в актуальном состоянии.

XAML

 <StackPanel x:Name="TopMessagePanel"> 
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> 
        <TextBlock FontSize="28" VerticalAlignment="Top" Text="{Binding UserName}" HorizontalAlignment="Center" /> 
        <TextBlock FontSize="28" VerticalAlignment="Top" Text=" is away right now" HorizontalAlignment="Center" /> 
    </StackPanel> 
    <TextBlock FontSize="28" Text="{Binding Message}" TextAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Center" /> 
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> 
        <TextBlock FontSize="20" VerticalAlignment="Top" Text="Expected return: " HorizontalAlignment="Center" /> 
        <TextBlock x:Name="ReturnCountdownText" Text="{Binding ReturnTimeRemaining, Converter={StaticResource EventCountdownConverter}, Mode=Default}" FontSize="20" VerticalAlignment="Top" HorizontalAlignment="Center" /> 
    </StackPanel> 
</StackPanel>

Окно сообщения

clip_image004

Это окно – простой интерфейс, с помощью которого вы можете вводить свое имя и текст сообщения. Вы также можете щелкнуть флажок, чтобы указать, является ли ваше сообщение приватным. Отображаемые сообщения показываются в ListBox.

Поля для ввода имени и сообщения связаны через механизм привязки данных с объектом AwayMessage. Так как WPF/XAML поддерживает декларативную привязку данных, вы можете сопоставить свойства Text элементов управления со свойствами объекта данных. Связанный объект называют контекстом данных – DataContext, и он независим от самих привязок. Если вы присваиваете контексту новую объектную ссылку, поля мгновенно обновляются значениями свойств, на которые они ссылаются.

ListBox связан через механизм привязки данных с набором объектов AwayMessage. Поскольку класс AwayMessageCollection наследует от ObservableCollection, ListBox распознает добавления и удаления в наборе через события. Писать самостоятельно какой-либо код для обновления UI при изменении объектов не требуется. По сути, так как класс AwayMessasge реализует INotifyPropertyChanged, в списке будут отображаться даже изменения в элементах набора. Мощная штука!

Visual C#

 public class AwayMessageCollection : ObservableCollection<AwayMessage>

Visual Basic

 Public Class AwayMessageCollection
    Inherits ObservableCollection(Of AwayMessage)

Фотоэффекты

Эффекты, позволяющие вращать фотографии и увеличивать их в размерах, а также отображать канцелярскую кнопку, – все это осуществляется декларативно с использованием XAML. Это дает возможность определять такие изменения, как поворот на 0–360 градусов и время, в течение которого должно быть закончено вращение. После этого все изменения обрабатывает WPF, учитывая производительность графической подсистемы, быстродействие процессора и т. д. Все это выполняется в фоновом потоке и делается настолько плавно, насколько позволяет ваше оборудование без создания каждого кадра в основном коде. Даже появление канцелярской кнопки осуществляется присваиванием соответствующего значения свойству Visibility на основе заданного временного графика.

clip_image006

Чтобы увидеть этот временной график, найдите в начале XAML-файла PhotoEffectsStoryboard в разделе <Window.Resources>. Этот объект создает серию элементов DoubleAnimation. Под «DoubleAnimation» подразумевается тот факт, что тип свойства, над которым выполняется анимация, – double. Каждый из таких элементов указывает имя целевого элемента, свойство, начальное и конечное значения, а также длительность выполнения изменения. Если вы опускаете свойство From, то создаете анимацию, называемую «эстафетной» («handoff animation»). То есть анимация начинается с любого текущего значения. Эстафетная анимация может оказаться по-настоящему полезной в интерактивных приложениях, где элементы могут быть в разных начальных точках в разные моменты времени, но в данном случае она не имеет особого смысла.

XAML

 <Storyboard x:Key="PhotoEffectStoryboard" Completed="PhotoEffectStoryboardCompleted" > 
    <DoubleAnimation Storyboard.TargetName="AnimatedImageScaleTranform" 
        Storyboard.TargetProperty="ScaleX" Duration="0:0:2.5" From="0" To=".5" /> 
    <DoubleAnimation Storyboard.TargetName="AnimatedImageScaleTranform" 
        Storyboard.TargetProperty="ScaleY" Duration="0:0:2.5" From="0" To=".5" /> 
    <DoubleAnimation 
        Storyboard.TargetProperty="Angle" 
        Duration="0:0:2.5" From="270" To="0" Storyboard.TargetName="AnimatedImageRotateTransform" /> 
    <DoubleAnimation 
        Storyboard.TargetName="AnimatedImageScaleTranform" 
        Storyboard.TargetProperty="ScaleX" BeginTime="0:0:7.5" Duration="0:0:2.5" From=".5" To="0" /> 
    <DoubleAnimation 
        Storyboard.TargetName="AnimatedImageScaleTranform" 
        Storyboard.TargetProperty="ScaleY" BeginTime="0:0:7.5" Duration="0:0:2.5" From=".5" To="0" /> 
    <DoubleAnimation 
        Storyboard.TargetProperty="Angle" 
        BeginTime="0:0:7.5" Duration="0:0:2.5" From="0" To="270" Storyboard.TargetName="AnimatedImageRotateTransform" /> 
    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Pin" Storyboard.TargetProperty="(UIElement.Visibility)"> 
        <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Hidden}"/> 
        <DiscreteObjectKeyFrame KeyTime="00:00:02.5000000" Value="{x:Static Visibility.Visible}"/> 
        <DiscreteObjectKeyFrame KeyTime="00:00:07.5000000" Value="{x:Static Visibility.Hidden}"/> 
    </ObjectAnimationUsingKeyFrames> 
</Storyboard>

Заметьте, что свойствам TargetName присваиваются именованные элементы ScaleTransform, а не сами элементы управления. Анимировать можно любой элемент с именем, поэтому в данном примере свойству RenderTransform объекта Border присваивается экземпляр TransformGroup, содержащий ScaleTransform и объект RotateTransform (загляните в XAML, если вы запутались!). Присваивая имена этим преобразованиям, вы получаете возможность анимировать их.

XAML

 <Border.RenderTransform> 
    <TransformGroup> 
        <ScaleTransform x:Name="AnimatedImageScaleTranform" 
            ScaleX=".5" ScaleY=".5" /> 
        <RotateTransform x:Name="AnimatedImageRotateTransform" 
            Angle="0" /> 
    </TransformGroup> 
</Border.RenderTransform> 

Возможные улучшения

Создав все это, я решил, что лучше было бы придать запискам вид реальных желтых стикеров. Вы могли бы добиться этого, создав пользовательский элемент управления, представляющий имя, текст записки и другие поля в окрашенном квадрате. Затем экран можно было бы закрывать большим ListBox с шаблоном отображения его элементов и связывать его с пользовательским элементом управления. Наконец, вам понадобилось бы модифицировать шаблон разметки ListBox, чтобы записки можно было бы размещать без всякой структуры для имитации доски объявлений.

Замечания по экранной заставке

Помните, что файл экранной заставки должен иметь расширение «.scr», а не «.exe», поэтому первым делом переименуйте исполняемый файл. Установить экранную заставку очень легко: достаточно скопировать файл .scr и связанные с ним DLL в папку Windows, а затем выбрать ее в диалоге настройки системы Screen Saver Settings (Параметры экранной заставки). Вы могли бы также задавать свою информацию об отсутствии, открывая файл .scr с аргументом /c, но это не слишком-то удобно!

Заметьте, что запуск с аргументом /c – это стандартный способ открытия экранной заставки в режиме настройки, и именно это происходит при щелчке кнопки Settings (Параметры) в диалоговом окне настройки экранной заставки.

clip_image008

Заключение

WPF – огромный шаг вперед. Эта подсистема позволяет создавать UI с динамичным внешним видом, чего почти невозможно было добиться с помощью GDI+ – ну разве что высококвалифицированным экспертам. Хотя работа с XAML, раскадровками (storyboards) и вложенными элементами управления может оказаться весьма утомительной, она стоит потраченного на нее времени. Изучите примеры, поэкспериментируйте с ними и загрузите Blend для упрощения редактирования XAML. Вы получите впечатляющие элементы управления!

Об авторе

Ариан Кулп (Arian Kulp) – разработчик ПО, живет в Западном Орегоне. Создает примеры, демо-ролики, лабораторные занятия и пишет статьи, выступает на различных мероприятиях, посвященных вопросам программирования, а также с удовольствием проводит свободное время со своей семьей.