Разработка компьютерных игр для Windows Phone 7 с использованием технологий Silverlight и XNA

2D-графика и анимация в Silverlight

Разбить на страницы
Показывать лекцию целиком

Цель работы: Научиться выводить и анимировать изображения в Silverlight-приложениях

30.1. Рисование фигур

Создадим новый Silverlight-проект, P23_1, добавим в него страницу с именем Shapes.xaml, используем HyperlinkButton на главной странице для перехода на страницу Shapes.

Мы хотим разместить на нашей новой странице несколько геометрических фигур. На Панели элементов по умолчанию присутствуют некоторые из доступных фигур. Это – элементы управления Ellipse (Эллипс) и Rectangle (Прямоугольник). Это – объекты, принадлежащие пространству имён System.Windows.Shapes, в частности, здесь есть следующие объекты:

  • Ellipse - эллипс
  • Line – прямая линия, проведенная через две точки
  • Path – линия, содержащая прямые и искривлённые участки
  • Polygon – многоугольник, заданный набором точек
  • Polyline – ломаная линия, состоящая из прямых сегментов
  • Rectangle – прямоугольник
  • Для удобства использования элементов, не выведенных в Панель элементов, мы можем расширить набор элементов, выбрав команду Выбрать элементы из меню панели, и в появившемся окне отметить нужные элементы флагами. Например, на рис. 30.1. мы добавили в Панель элементов элемент Polygon.

    (рис 30.1) Выбор элементов

    Фигуры можно добавлять на страницу так же, как мы добавляли элементы управления, у фигур есть свойства, которые можно настраивать, есть события, которые можно обрабатывать.

    Если говорить о таких фигурах, например, как эллипс, прямоугольник, работать с ними довольно просто и интуитивно понятно. Удобно настраивать свойства элементов, пользуясь панелью Свойства. Все изменения, которые мы вносим, отображаются, в итоге, в XAML-коде. Естественно, настраивать свойства фигур можно и непосредственно в XAML.

    Добавим на страницу несколько фигур, так, чтобы в итоге у нас получилось следующее, рис. 30.2.

    (рис 30.2) Фигуры на Silverlight-странице

    Рассмотрим некоторые особенности работы с фигурами, для начала приведем XAML-код этих фигур (для наглядности приведен полный код страницы), листинг 30.1.

    <phone:PhoneApplicationPage 
        x:Class="P23_1.Shapes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
        xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
        shell:SystemTray.IsVisible="True">
    
        <!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!--TitlePanel содержит имя приложения и заголовок страницы-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ" 
                Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="фигуры" Margin="9,-7,0,0" 
                Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <!--ContentPanel – поместите здесь дополнительное содержимое-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <Rectangle Height="149" HorizontalAlignment="Left" Margin="47,309,0,0" Name="rectangle1" 
                Stroke="#FFC1C143" StrokeThickness="15" VerticalAlignment="Top" Width="191" />
                <Ellipse Height="100" HorizontalAlignment="Left" Margin="187,250,0,0" Name="ellipse2"
                Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="100">
                    <Ellipse.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="#E9FFFFFF" Offset="0" />
                            <GradientStop Color="#E304121A" Offset="0.994" />
                            <GradientStop Color="#F106639F" Offset="0.517" />
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <Polygon Height="163" HorizontalAlignment="Left" Margin="143,399,0,0" Name="polygon1" 
                Stroke="#FFA04BC4" StrokeThickness="10" VerticalAlignment="Top" Width="233" Fill="{x:Null}">
                    <Polygon.Points>
                        <Point>0,0</Point>
                        <Point Y="100" />
                        <Point X="100" Y="100" />
                    </Polygon.Points>
                </Polygon>
                <Ellipse Height="222" HorizontalAlignment="Left" Margin="12,49,0,0" Name="ellipse1" 
                Stroke="#FF2B2BA0" StrokeThickness="10" VerticalAlignment="Top" Width="416" Fill="#FF32B132" />
            </Grid>
        </Grid>
     
    
    </phone:PhoneApplicationPage>
    

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

    Если фигуры перекрываются и нам нужно изменить этот порядок, можно либо воспользоваться командами контекстного меню фигуры Порядок (здесь доступны такие варианты, как На передний план, Переместить вперед, Переместить назад, На задний план), рис. 30.3. Можно самостоятельно переместить участок кода в XAML-файле, соответствующий данной фигуре, в нужное место относительно других фигур.

    (рис 30.3) Настройка свойств фигуры

    Размер фигур можно настраивать в визуальном режиме, например, на рис. 30.3 видны маркеры изменения размера вокруг выделенного эллипса. Но, в том, что касается остальных настрое,к нам будет полезна панель Свойства и XAML-редактор.

    Рассмотрим свойства фигур, которыми мы пользовались в этом примере, опираясь на их XAML-разметку. В листинге 30.2 приведен XAML-код эллипса, расположенного в нашем примере на переднем плане, код его описания расположен последним, если ориентироваться на листинг 23.1.

    <Ellipse Height="222" HorizontalAlignment="Left" Margin="12,49,0,0"
    Name="ellipse1" Stroke="#FF2B2BA0" StrokeThickness="10" VerticalAlignment="Top" 
    Width="416" Fill="#FF32B132" />

    <p>Height – высота элемента

    HorizontalAlighment – выравнивание по горизонтали

    Margin – расстояние между элементом и другими элементами. В данном случае задаётся для левой, верхней, правой и нижней сторон объекта и отражает расположение объекта на экране относительно ячейки сетки (она имеет имя ContentPanel), в которой расположены объекты. При изменении положения объекта на странице именно здесь записывается его новое положение.

    Name – имя элемента

    Stroke – цвет обводки в шестнадцатеричном формате, с альфа-каналом, отвечающим за прозрачность. Первое значение в описании цвета (#FF2B2BA0), FF, указывает на то, что объект непрозрачен, три других – 2B, 2B, A0 – задают, соответственно, красную, зеленую и синюю составляющие итогового цвета. Цвета удобно задавать в панели Свойства – при попытке редактирования свойства, подразумевающего установку цвета, появляется окно настройки цвета (рис. 30.4). Причём, здесь возможно не только настраивать цвета, но и задавать, например, тип заливки и некоторые другие параметры.

    (рис 30.4) Настройка цвета обводки

    StrokeThickness – толщина обводки

    VerticalAlignment – выравнивание по вертикали

    Width - ширина

    Fill – заливка

    Рассмотрим свойства следующей фигуры в коде. Это – многоугольник, представленный в нашем проекте в виде треугольника, листинг 23.3.,

                <Polygon Height="163" HorizontalAlignment="Left" Margin="143,399,0,0" Name="polygon1" Stroke="#FFA04BC4"
     StrokeThickness="10" VerticalAlignment="Top" 
    Width="233" Fill="{x:Null}">
                    <Polygon.Points>
                        <Point>0,0</Point>
                        <Point Y="100" />
                        <Point X="100" Y="100" />
                    </Polygon.Points>
                </Polygon>

    Подавляющее большинство его свойств вам уже знакомы. У многоугольника есть одна важная особенность. Для того чтобы задать его, нужно указать координаты точек, которые в него входят. Эти точки будут соединены замкнутой линией. Если обратиться к свойству объекта Points в панели Свойства, откроется окно, рис. 30.5.

    (рис 30.5) Редактор коллекции Points

    Здесь можно добавлять в многоугольник точки (в левой части окна), и настраивать свойства точек (X и Y) в правой части.

    Обратите внимание на то, что координаты точек задаются относительно левого верхнего угла границы фигуры, рис. 30.6.

    (рис 30.6) Границы фигуры, начало координат для элементов многоугольника

    У многоугольника задана граница толщиной в 10 пикселей, она равномерно выводится вокруг линий, задающих стороны фигуры, в итоге, выходит за пределы ограничивающей рамки.

    Наша следующая фигура, листинг 23.4, представляет собой эллипс с градиентной заливкой

    <Ellipse Height="100" HorizontalAlignment="Left" Margin="187,250,0,0" Name="ellipse2" 
    Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="100">
                    <Ellipse.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="#E9FFFFFF" Offset="0" />
                            <GradientStop Color="#E304121A" Offset="0.994" />
                            <GradientStop Color="#F106639F" Offset="0.517" />
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>

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

    Для задания градиентной заливки удобно пользоваться уже упомянутым окном настройки цвета, выбрав в качестве кисти заливки Градиентную кисть, в качестве типа – Радиальный градиент (рис. 30.7) и настроив параметры градиента.

    (рис 30.7) Настройка параметров градиентной заливки

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

    Наш последний элемент в данном примере – прямоугольник – ничем особым не примечателен, вы без труда сами разберетесь в его свойствах на основе того, о чём мы говорили выше.

    Поговорив о стандартных фигурах, остановимся теперь на трансформации объектов.

    30.2. Трансформация объектов

    Трансформация в Silverlight может применяться к объектам, которые наследуются от UIElement. На практике оказывается, что это – текст, изображения, фигуры, элементы управления, другие элементы. В UIElement есть свойство RenderTransform, которому можно назначать объект типа Transform (System.Windows.Media.Transform). В частности, существуют следующие классы (из пространства имён System.Windows.Media), которые можно использовать при настройке данного свойства:

  • CompositeTransform (Составная трансформация) – позволяет задавать несколько операций трансформации в определенной последовательности
  • MatrixTransform (Трансформация с использованием матрицы) – позволяет выполнять трансформации с использованием матрицы
  • RotateTransform (Трансформация вращением) – позволяет вращать объекты
  • ScaleTransform (Трансформация масштабированием) – позволяет управлять размером объекта
  • SkewTransform (Трансформация наклонением) – позволяет смещать одни части объекта относительно других (как бы перекашивает объект, превращая, например, прямоугольник в параллелограмм)
  • TransformGroup (Группа трансформаций) – позволяет комбинировать несколько трансформаций
  • TranslateTransform (Трансформация переносом) – позволяет менять местоположение объекта
  • Задавать трансформации можно как в XAML-описании объектов, так и в коде, меняя параметры трансформации по какому-либо событию.

    Рассмотрим пример описания трансформации в XAML-коде. Создадим новую страницу, назовём её Transformation.XAML, сделаем её доступной с главной страницы по ссылке.

    Добавим на страницу новый TextBlock, введем в него текст "Текст для трансформации", зададим размер шрифта (FontSize) 32 пункта. Создадим две копии текстового блока. Добавим на страницу кнопку (элемент управления Button), её копию и приведем их код в вид, представленный в листинге 30.5.

               <!-- Исходный текст-->
                <TextBlock Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="28,32,0,0" 
                           Name="textBlock1" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           FontSize="32" 
                           Width="393" />
                <!-- Перекошенный текст -->
                <TextBlock FontSize="32" 
                           Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="16,98,0,0" 
                           Name="textBlock2" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           Width="393" >
                    <TextBlock.RenderTransform>
                         <SkewTransform AngleX="30" 
                                        AngleY="10"/>  
                    </TextBlock.RenderTransform>
                </TextBlock>
                <!-- Наклоненный текст -->
                <TextBlock FontSize="32" 
                           Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="30,301,0,0" 
                           Name="textBlock3" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           Width="393" >
                    <TextBlock.RenderTransform>
                         <RotateTransform Angle="-15"/>  
                    </TextBlock.RenderTransform>
                </TextBlock>
                <!--Исходная кнопка -->
                <Button Content="Button" 
                        Height="72"
                        HorizontalAlignment="Left" 
                        Margin="263,313,0,0" 
                        Name="button1" 
                        VerticalAlignment="Top" 
                        Width="160" />
                <!-- Перекошенная и увеличенная кнопка-->
                <Button Content="Button" 
                        Height="72" 
                        HorizontalAlignment="Left"
                        Margin="149,410,0,0" 
                        Name="button2" 
                        VerticalAlignment="Top" 
                        Width="160"> 
                    <Button.RenderTransform>
                        <TransformGroup>
                            <SkewTransform AngleX="-50" 
                                        AngleY="20"/>
                            <ScaleTransform ScaleX="2"/>
                        </TransformGroup>
                     </Button.RenderTransform>
                </Button>

    Внутри описания элемента мы задаем свойства для RenderTransform. Два трансформированных фрагмента текста демонстрируют трансформации SkewTransform и RotateTransform. К кнопке мы применили сразу две трансформации, для этого нам понадобилось использовать тег TransformGroup (фактически, воспользовались группой трансформаций). Сначала мы применили трансформацию перекоса, потом – масштабирования.

    Итог работы вышеприведенного кода вы можете видеть на рис. 30.8.

    (рис 30.8) Итог применения трансформаций

    Обратите внимание на то, что взаимодействие пользователя с трансформированными элементами управления выглядит так же, как с обычными. То есть, например, нажатие на кнопку обрабатывается в соответствии с её новым местоположением.

    Рассмотрим теперь анимацию объектов с использованием трансформаций и события Rendering.

    30.3. Анимация объектов, основанная на событии Rendering

    Добавим в наш проект новую страницу, назовем ее SimpleAnimation. Добавим на страницу эллипс с равной высотой и шириной, приведем его XAML-код к виду, представленному в листинге 30.6.

    <Ellipse Height="100" 
                         HorizontalAlignment="Left" 
                         Margin="134,501,0,0" 
                         Name="ellipse1" 
                         Stroke="Black" 
                         StrokeThickness="1" 
                         VerticalAlignment="Top"
                         Width="100" 
                         Fill="#FF00CB00">
                    <Ellipse.RenderTransform>
                        <TranslateTransform x:Name="translate" />
                    </Ellipse.RenderTransform>
                </Ellipse>

    Обратите внимание, здесь мы задаём в настройках трансформации TranslateTransform, указывая для этой трансформаци имя (x:Name) – translate. По этому имени мы сможем обращаться к данной трансформации из кода, модифицируя в процессе исполнения программы её свойства. Обращение к трансформации можно организовать и без задания имени, через объект ellipse1.

    Мы собираемся организовать автоматическое перемещение окружности по событию Rendering объекта CompositionTarget, который представляет собой отображаемую поверхность приложения. Фактически, перехватив это событие мы получаем таймер, частота срабатывания которого соответствует частоте обновления экрана. Для перехвата события мы воспользуемся конструктором SimpleAnimation() в файле SimpleAnimation.xaml.cs. Добавим на страницу две кнопки.

    Одну, подписанную как Старт (button1), будем нажимать для запуска перемещения объекта по экрану со случайными значениями приращения по X и Y, вторую (Сброс, button2) будем использовать для возврата объекта в исходную позицию и прекращения перемещения.

    Код файла SimpleAnimation.xaml.cs приведен в листинге 30.7

    7using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Phone.Controls;
    
    namespace P23_1
    {
        public partial class SimpleAnimation : PhoneApplicationPage
        {
            double speedAndDirX = 0;
            double speedAndDirY = 0;
            bool isAnimated = false;
            public SimpleAnimation()
            {
                InitializeComponent();
                CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
            }
    
                    
            void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                if (isAnimated)
                {
                    translate.X += speedAndDirX;
                    translate.Y += speedAndDirY;
                }
            }
    
            private void button2_Click(object sender, RoutedEventArgs e)
            {
                isAnimated = false;
                translate.X = 0;
                translate.Y = 0;
            }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                Random r = new Random();
                speedAndDirX = r.Next(5) - r.Next(5);
                speedAndDirY = r.Next(5) - r.Next(5);
                isAnimated = true;
            }
        }
    }

    На рис. 30.9 в можете видеть страницу SimpleAnimation.xaml.

    (рис 30.9) Страница SimpleAnimation.xaml

    Рассмотрим еще один вариант анимации – с использованием инструментов, доступных в Expression Blend.

    30.4. Анимация объектов с применением Expression Blend

    Создадим новую страницу, BlendAnimation, разместим на ней эллипс размером 200х200, зададим заливку. Вызовем контекстное меню проекта, выберем команду Открыть в Expression Blend.

    В Expression Blend перейдем на закладку Projects, двойным щелчком выберем страницу BlendAnimation.xaml, рис. 30.10.

    (рис 30.10) Страница в Expression Blend

    На закладке Objects and Timeline нажмем на кнопку "+" в верхней части панели, появится окно Create Storyboard Resource, зададим в нём имя для нашей анимации – Ellipse Animation, рис. 30.11.

    (рис 30.11) Новая анимация

    Нажмем на кнопку ОК. В панели Objects and Timeline появится временная шкала. Выделим объект LayoutRoot и нажмём кнопку Record Keyframe (на ней изображён овал со знаком +), на шкале времени напротив LayoutRoot появится значок овала, кроме того, в правой части окна будет доступна панель Properties, рис. 30.12.

    (рис 30.12) Запись первого ключевого кадра

    Первый ключевой кадр – это исходной состояние страницы. Создавая новые ключевые кадры и изменяя состояние элементов мы задаём их анимацию.

    Переместим жёлтый указатель на шкале времени, после чего изменим размеры и положение эллипса. Новый ключевой кадр будет создан автоматически, рис. 30.13.

    (рис 30.13) Изменение размеров и положения эллипса

    Теперь, когда у нас есть уже два ключевых кадра, мы можем просмотреть полученную анимацию, для этого нажмём на кнопку Play на панели Objects and Timeline. Мы можем добавлять новые ключевые кадры, работать с существующими, перетаскивая мышью указатель текущего времени на шкале, просматривать промежуточные состояния анимации, пользоваться панелью Properties, добавлять в проект новые элементы и т.д. В итоге наш проект приобрел вид, показанный на рис. 30.14.

    (рис 30.14) Настройка параметров анимации

    Сохраним проект (можно закрыть окно Expression Blend, утвердительно ответив на вопрос о сохранении проекта) и снова окажемся в Visual Studio. Утвердительно ответим на вопрос о перезагрузке измененного файла. В файл BlendAnimation.xaml будет добавлен XAML-код описания анимации, так как в Blender мы создали новый объект (прямоугольник), этот прямоугольник так же будет добавлен на страницу.

    Того, что мы сделали сейчас, недостаточно для запуска анимации. Нам нужно запустить её самостоятельно. Это можно сделать в файле кода страницы, например, в обработчике события DoubleTap (двойное прикосновение) для окружности. В этом обработчике нам достаточно добавить следующую команду, листинг 23.8.

            private void ellipse1_DoubleTap(object sender, GestureEventArgs e)
            {
                EllipseAnimation.Begin();
            }
    

    30.5. Выводы

    В этой лабораторной работе мы рассмотрели вывод двумерных изображений в Silverlight-проектах и их анимацию различными способами. Рассмотрели пример использования Expression Blend для анимации объектов с помощью шкалы времени и ключевых кадров.

    30.6. Задание

    Изучите различные способы трансформации объектов. Поэкспериментируйте с покадровой анимацией, основанной на перехвате события Rendering, используя различные варианты трансформаций. Исследуйте возможности Expression Blend.

    30.7. Дополнительные материалы

    К данной лабораторной работе прилагается видеоурок

    и демонстрационный проект.

    Страницы:

    Цель работы: Научиться выводить и анимировать изображения в Silverlight-приложениях

    30.1. Рисование фигур

    Создадим новый Silverlight-проект, P23_1, добавим в него страницу с именем Shapes.xaml, используем HyperlinkButton на главной странице для перехода на страницу Shapes.

    Мы хотим разместить на нашей новой странице несколько геометрических фигур. На Панели элементов по умолчанию присутствуют некоторые из доступных фигур. Это – элементы управления Ellipse (Эллипс) и Rectangle (Прямоугольник). Это – объекты, принадлежащие пространству имён System.Windows.Shapes, в частности, здесь есть следующие объекты:

  • Ellipse - эллипс
  • Line – прямая линия, проведенная через две точки
  • Path – линия, содержащая прямые и искривлённые участки
  • Polygon – многоугольник, заданный набором точек
  • Polyline – ломаная линия, состоящая из прямых сегментов
  • Rectangle – прямоугольник
  • Для удобства использования элементов, не выведенных в Панель элементов, мы можем расширить набор элементов, выбрав команду Выбрать элементы из меню панели, и в появившемся окне отметить нужные элементы флагами. Например, на рис. 30.1. мы добавили в Панель элементов элемент Polygon.

    (рис 30.1) Выбор элементов

    Фигуры можно добавлять на страницу так же, как мы добавляли элементы управления, у фигур есть свойства, которые можно настраивать, есть события, которые можно обрабатывать.

    Если говорить о таких фигурах, например, как эллипс, прямоугольник, работать с ними довольно просто и интуитивно понятно. Удобно настраивать свойства элементов, пользуясь панелью Свойства. Все изменения, которые мы вносим, отображаются, в итоге, в XAML-коде. Естественно, настраивать свойства фигур можно и непосредственно в XAML.

    Добавим на страницу несколько фигур, так, чтобы в итоге у нас получилось следующее, рис. 30.2.

    (рис 30.2) Фигуры на Silverlight-странице

    Рассмотрим некоторые особенности работы с фигурами, для начала приведем XAML-код этих фигур (для наглядности приведен полный код страницы), листинг 30.1.

    <phone:PhoneApplicationPage 
        x:Class="P23_1.Shapes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
        xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
        shell:SystemTray.IsVisible="True">
    
        <!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
    
            <!--TitlePanel содержит имя приложения и заголовок страницы-->
            <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ" 
                Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="фигуры" Margin="9,-7,0,0" 
                Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
            <!--ContentPanel – поместите здесь дополнительное содержимое-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <Rectangle Height="149" HorizontalAlignment="Left" Margin="47,309,0,0" Name="rectangle1" 
                Stroke="#FFC1C143" StrokeThickness="15" VerticalAlignment="Top" Width="191" />
                <Ellipse Height="100" HorizontalAlignment="Left" Margin="187,250,0,0" Name="ellipse2"
                Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="100">
                    <Ellipse.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="#E9FFFFFF" Offset="0" />
                            <GradientStop Color="#E304121A" Offset="0.994" />
                            <GradientStop Color="#F106639F" Offset="0.517" />
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <Polygon Height="163" HorizontalAlignment="Left" Margin="143,399,0,0" Name="polygon1" 
                Stroke="#FFA04BC4" StrokeThickness="10" VerticalAlignment="Top" Width="233" Fill="{x:Null}">
                    <Polygon.Points>
                        <Point>0,0</Point>
                        <Point Y="100" />
                        <Point X="100" Y="100" />
                    </Polygon.Points>
                </Polygon>
                <Ellipse Height="222" HorizontalAlignment="Left" Margin="12,49,0,0" Name="ellipse1" 
                Stroke="#FF2B2BA0" StrokeThickness="10" VerticalAlignment="Top" Width="416" Fill="#FF32B132" />
            </Grid>
        </Grid>
     
    
    </phone:PhoneApplicationPage>
    

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

    Если фигуры перекрываются и нам нужно изменить этот порядок, можно либо воспользоваться командами контекстного меню фигуры Порядок (здесь доступны такие варианты, как На передний план, Переместить вперед, Переместить назад, На задний план), рис. 30.3. Можно самостоятельно переместить участок кода в XAML-файле, соответствующий данной фигуре, в нужное место относительно других фигур.

    (рис 30.3) Настройка свойств фигуры

    Размер фигур можно настраивать в визуальном режиме, например, на рис. 30.3 видны маркеры изменения размера вокруг выделенного эллипса. Но, в том, что касается остальных настрое,к нам будет полезна панель Свойства и XAML-редактор.

    Рассмотрим свойства фигур, которыми мы пользовались в этом примере, опираясь на их XAML-разметку. В листинге 30.2 приведен XAML-код эллипса, расположенного в нашем примере на переднем плане, код его описания расположен последним, если ориентироваться на листинг 23.1.

    <Ellipse Height="222" HorizontalAlignment="Left" Margin="12,49,0,0"
    Name="ellipse1" Stroke="#FF2B2BA0" StrokeThickness="10" VerticalAlignment="Top" 
    Width="416" Fill="#FF32B132" />

    <p>Height – высота элемента

    HorizontalAlighment – выравнивание по горизонтали

    Margin – расстояние между элементом и другими элементами. В данном случае задаётся для левой, верхней, правой и нижней сторон объекта и отражает расположение объекта на экране относительно ячейки сетки (она имеет имя ContentPanel), в которой расположены объекты. При изменении положения объекта на странице именно здесь записывается его новое положение.

    Name – имя элемента

    Stroke – цвет обводки в шестнадцатеричном формате, с альфа-каналом, отвечающим за прозрачность. Первое значение в описании цвета (#FF2B2BA0), FF, указывает на то, что объект непрозрачен, три других – 2B, 2B, A0 – задают, соответственно, красную, зеленую и синюю составляющие итогового цвета. Цвета удобно задавать в панели Свойства – при попытке редактирования свойства, подразумевающего установку цвета, появляется окно настройки цвета (рис. 30.4). Причём, здесь возможно не только настраивать цвета, но и задавать, например, тип заливки и некоторые другие параметры.

    (рис 30.4) Настройка цвета обводки

    StrokeThickness – толщина обводки

    VerticalAlignment – выравнивание по вертикали

    Width - ширина

    Fill – заливка

    Рассмотрим свойства следующей фигуры в коде. Это – многоугольник, представленный в нашем проекте в виде треугольника, листинг 23.3.,

                <Polygon Height="163" HorizontalAlignment="Left" Margin="143,399,0,0" Name="polygon1" Stroke="#FFA04BC4"
     StrokeThickness="10" VerticalAlignment="Top" 
    Width="233" Fill="{x:Null}">
                    <Polygon.Points>
                        <Point>0,0</Point>
                        <Point Y="100" />
                        <Point X="100" Y="100" />
                    </Polygon.Points>
                </Polygon>

    Подавляющее большинство его свойств вам уже знакомы. У многоугольника есть одна важная особенность. Для того чтобы задать его, нужно указать координаты точек, которые в него входят. Эти точки будут соединены замкнутой линией. Если обратиться к свойству объекта Points в панели Свойства, откроется окно, рис. 30.5.

    (рис 30.5) Редактор коллекции Points

    Здесь можно добавлять в многоугольник точки (в левой части окна), и настраивать свойства точек (X и Y) в правой части.

    Обратите внимание на то, что координаты точек задаются относительно левого верхнего угла границы фигуры, рис. 30.6.

    (рис 30.6) Границы фигуры, начало координат для элементов многоугольника

    У многоугольника задана граница толщиной в 10 пикселей, она равномерно выводится вокруг линий, задающих стороны фигуры, в итоге, выходит за пределы ограничивающей рамки.

    Наша следующая фигура, листинг 23.4, представляет собой эллипс с градиентной заливкой

    <Ellipse Height="100" HorizontalAlignment="Left" Margin="187,250,0,0" Name="ellipse2" 
    Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="100">
                    <Ellipse.Fill>
                        <RadialGradientBrush>
                            <GradientStop Color="#E9FFFFFF" Offset="0" />
                            <GradientStop Color="#E304121A" Offset="0.994" />
                            <GradientStop Color="#F106639F" Offset="0.517" />
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>

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

    Для задания градиентной заливки удобно пользоваться уже упомянутым окном настройки цвета, выбрав в качестве кисти заливки Градиентную кисть, в качестве типа – Радиальный градиент (рис. 30.7) и настроив параметры градиента.

    (рис 30.7) Настройка параметров градиентной заливки

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

    Наш последний элемент в данном примере – прямоугольник – ничем особым не примечателен, вы без труда сами разберетесь в его свойствах на основе того, о чём мы говорили выше.

    Поговорив о стандартных фигурах, остановимся теперь на трансформации объектов.

    30.2. Трансформация объектов

    Трансформация в Silverlight может применяться к объектам, которые наследуются от UIElement. На практике оказывается, что это – текст, изображения, фигуры, элементы управления, другие элементы. В UIElement есть свойство RenderTransform, которому можно назначать объект типа Transform (System.Windows.Media.Transform). В частности, существуют следующие классы (из пространства имён System.Windows.Media), которые можно использовать при настройке данного свойства:

  • CompositeTransform (Составная трансформация) – позволяет задавать несколько операций трансформации в определенной последовательности
  • MatrixTransform (Трансформация с использованием матрицы) – позволяет выполнять трансформации с использованием матрицы
  • RotateTransform (Трансформация вращением) – позволяет вращать объекты
  • ScaleTransform (Трансформация масштабированием) – позволяет управлять размером объекта
  • SkewTransform (Трансформация наклонением) – позволяет смещать одни части объекта относительно других (как бы перекашивает объект, превращая, например, прямоугольник в параллелограмм)
  • TransformGroup (Группа трансформаций) – позволяет комбинировать несколько трансформаций
  • TranslateTransform (Трансформация переносом) – позволяет менять местоположение объекта
  • Задавать трансформации можно как в XAML-описании объектов, так и в коде, меняя параметры трансформации по какому-либо событию.

    Рассмотрим пример описания трансформации в XAML-коде. Создадим новую страницу, назовём её Transformation.XAML, сделаем её доступной с главной страницы по ссылке.

    Добавим на страницу новый TextBlock, введем в него текст "Текст для трансформации", зададим размер шрифта (FontSize) 32 пункта. Создадим две копии текстового блока. Добавим на страницу кнопку (элемент управления Button), её копию и приведем их код в вид, представленный в листинге 30.5.

               <!-- Исходный текст-->
                <TextBlock Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="28,32,0,0" 
                           Name="textBlock1" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           FontSize="32" 
                           Width="393" />
                <!-- Перекошенный текст -->
                <TextBlock FontSize="32" 
                           Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="16,98,0,0" 
                           Name="textBlock2" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           Width="393" >
                    <TextBlock.RenderTransform>
                         <SkewTransform AngleX="30" 
                                        AngleY="10"/>  
                    </TextBlock.RenderTransform>
                </TextBlock>
                <!-- Наклоненный текст -->
                <TextBlock FontSize="32" 
                           Height="58" 
                           HorizontalAlignment="Left" 
                           Margin="30,301,0,0" 
                           Name="textBlock3" 
                           Text="Текст для трансформации" 
                           VerticalAlignment="Top" 
                           Width="393" >
                    <TextBlock.RenderTransform>
                         <RotateTransform Angle="-15"/>  
                    </TextBlock.RenderTransform>
                </TextBlock>
                <!--Исходная кнопка -->
                <Button Content="Button" 
                        Height="72"
                        HorizontalAlignment="Left" 
                        Margin="263,313,0,0" 
                        Name="button1" 
                        VerticalAlignment="Top" 
                        Width="160" />
                <!-- Перекошенная и увеличенная кнопка-->
                <Button Content="Button" 
                        Height="72" 
                        HorizontalAlignment="Left"
                        Margin="149,410,0,0" 
                        Name="button2" 
                        VerticalAlignment="Top" 
                        Width="160"> 
                    <Button.RenderTransform>
                        <TransformGroup>
                            <SkewTransform AngleX="-50" 
                                        AngleY="20"/>
                            <ScaleTransform ScaleX="2"/>
                        </TransformGroup>
                     </Button.RenderTransform>
                </Button>

    Внутри описания элемента мы задаем свойства для RenderTransform. Два трансформированных фрагмента текста демонстрируют трансформации SkewTransform и RotateTransform. К кнопке мы применили сразу две трансформации, для этого нам понадобилось использовать тег TransformGroup (фактически, воспользовались группой трансформаций). Сначала мы применили трансформацию перекоса, потом – масштабирования.

    Итог работы вышеприведенного кода вы можете видеть на рис. 30.8.

    (рис 30.8) Итог применения трансформаций

    Обратите внимание на то, что взаимодействие пользователя с трансформированными элементами управления выглядит так же, как с обычными. То есть, например, нажатие на кнопку обрабатывается в соответствии с её новым местоположением.

    Рассмотрим теперь анимацию объектов с использованием трансформаций и события Rendering.

    30.3. Анимация объектов, основанная на событии Rendering

    Добавим в наш проект новую страницу, назовем ее SimpleAnimation. Добавим на страницу эллипс с равной высотой и шириной, приведем его XAML-код к виду, представленному в листинге 30.6.

    <Ellipse Height="100" 
                         HorizontalAlignment="Left" 
                         Margin="134,501,0,0" 
                         Name="ellipse1" 
                         Stroke="Black" 
                         StrokeThickness="1" 
                         VerticalAlignment="Top"
                         Width="100" 
                         Fill="#FF00CB00">
                    <Ellipse.RenderTransform>
                        <TranslateTransform x:Name="translate" />
                    </Ellipse.RenderTransform>
                </Ellipse>

    Обратите внимание, здесь мы задаём в настройках трансформации TranslateTransform, указывая для этой трансформаци имя (x:Name) – translate. По этому имени мы сможем обращаться к данной трансформации из кода, модифицируя в процессе исполнения программы её свойства. Обращение к трансформации можно организовать и без задания имени, через объект ellipse1.

    Мы собираемся организовать автоматическое перемещение окружности по событию Rendering объекта CompositionTarget, который представляет собой отображаемую поверхность приложения. Фактически, перехватив это событие мы получаем таймер, частота срабатывания которого соответствует частоте обновления экрана. Для перехвата события мы воспользуемся конструктором SimpleAnimation() в файле SimpleAnimation.xaml.cs. Добавим на страницу две кнопки.

    Одну, подписанную как Старт (button1), будем нажимать для запуска перемещения объекта по экрану со случайными значениями приращения по X и Y, вторую (Сброс, button2) будем использовать для возврата объекта в исходную позицию и прекращения перемещения.

    Код файла SimpleAnimation.xaml.cs приведен в листинге 30.7

    7using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Phone.Controls;
    
    namespace P23_1
    {
        public partial class SimpleAnimation : PhoneApplicationPage
        {
            double speedAndDirX = 0;
            double speedAndDirY = 0;
            bool isAnimated = false;
            public SimpleAnimation()
            {
                InitializeComponent();
                CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
            }
    
                    
            void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                if (isAnimated)
                {
                    translate.X += speedAndDirX;
                    translate.Y += speedAndDirY;
                }
            }
    
            private void button2_Click(object sender, RoutedEventArgs e)
            {
                isAnimated = false;
                translate.X = 0;
                translate.Y = 0;
            }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                Random r = new Random();
                speedAndDirX = r.Next(5) - r.Next(5);
                speedAndDirY = r.Next(5) - r.Next(5);
                isAnimated = true;
            }
        }
    }

    На рис. 30.9 в можете видеть страницу SimpleAnimation.xaml.

    (рис 30.9) Страница SimpleAnimation.xaml

    Рассмотрим еще один вариант анимации – с использованием инструментов, доступных в Expression Blend.

    30.4. Анимация объектов с применением Expression Blend

    Создадим новую страницу, BlendAnimation, разместим на ней эллипс размером 200х200, зададим заливку. Вызовем контекстное меню проекта, выберем команду Открыть в Expression Blend.

    В Expression Blend перейдем на закладку Projects, двойным щелчком выберем страницу BlendAnimation.xaml, рис. 30.10.

    (рис 30.10) Страница в Expression Blend

    На закладке Objects and Timeline нажмем на кнопку "+" в верхней части панели, появится окно Create Storyboard Resource, зададим в нём имя для нашей анимации – Ellipse Animation, рис. 30.11.

    (рис 30.11) Новая анимация

    Нажмем на кнопку ОК. В панели Objects and Timeline появится временная шкала. Выделим объект LayoutRoot и нажмём кнопку Record Keyframe (на ней изображён овал со знаком +), на шкале времени напротив LayoutRoot появится значок овала, кроме того, в правой части окна будет доступна панель Properties, рис. 30.12.

    (рис 30.12) Запись первого ключевого кадра

    Первый ключевой кадр – это исходной состояние страницы. Создавая новые ключевые кадры и изменяя состояние элементов мы задаём их анимацию.

    Переместим жёлтый указатель на шкале времени, после чего изменим размеры и положение эллипса. Новый ключевой кадр будет создан автоматически, рис. 30.13.

    (рис 30.13) Изменение размеров и положения эллипса

    Теперь, когда у нас есть уже два ключевых кадра, мы можем просмотреть полученную анимацию, для этого нажмём на кнопку Play на панели Objects and Timeline. Мы можем добавлять новые ключевые кадры, работать с существующими, перетаскивая мышью указатель текущего времени на шкале, просматривать промежуточные состояния анимации, пользоваться панелью Properties, добавлять в проект новые элементы и т.д. В итоге наш проект приобрел вид, показанный на рис. 30.14.

    (рис 30.14) Настройка параметров анимации

    Сохраним проект (можно закрыть окно Expression Blend, утвердительно ответив на вопрос о сохранении проекта) и снова окажемся в Visual Studio. Утвердительно ответим на вопрос о перезагрузке измененного файла. В файл BlendAnimation.xaml будет добавлен XAML-код описания анимации, так как в Blender мы создали новый объект (прямоугольник), этот прямоугольник так же будет добавлен на страницу.

    Того, что мы сделали сейчас, недостаточно для запуска анимации. Нам нужно запустить её самостоятельно. Это можно сделать в файле кода страницы, например, в обработчике события DoubleTap (двойное прикосновение) для окружности. В этом обработчике нам достаточно добавить следующую команду, листинг 23.8.

            private void ellipse1_DoubleTap(object sender, GestureEventArgs e)
            {
                EllipseAnimation.Begin();
            }
    

    30.5. Выводы

    В этой лабораторной работе мы рассмотрели вывод двумерных изображений в Silverlight-проектах и их анимацию различными способами. Рассмотрели пример использования Expression Blend для анимации объектов с помощью шкалы времени и ключевых кадров.

    30.6. Задание

    Изучите различные способы трансформации объектов. Поэкспериментируйте с покадровой анимацией, основанной на перехвате события Rendering, используя различные варианты трансформаций. Исследуйте возможности Expression Blend.

    30.7. Дополнительные материалы

    К данной лабораторной работе прилагается видеоурок

    и демонстрационный проект.

    Вернуться к учебному плану