Немного о рисовании. Часть первая — Фигуры и линии, манипуляции с объектами.

Posted on Март 13, 2011

5


В последнее время, обращаю внимание на, то что много где спрашивают, как же рисовать пальцем на Windows Phone 7, как приделать возможность передвигать объекты, как их масштабировать и поворачивать? Причем это делают как разработчики, так и пользователи. Хочу начать небольшой цикл, в котором мы обсудим, как делать все эти вещи на Silverlight на Windows Phone 7.

Итак, сегодня вводная статья, в которой мы покроем из чего же можно строить рисунки.

Как и в обычном Silverlight, у нас есть доступ к множеству объектов, которое можно нарисовать.  Вот список, со ссылкой на документацию по каждому:

Названия говорят сами за себя, так что думаю, что нет необходимости переводить. Самое важное, что следует помнить, так это то, что к каждому из этих элементов мы можем цепляться на события, тем самым осуществлять перемещение объектов, изменение их размера и т.п.
У всех этих фигур есть общие свойства:
  • Stroke — объект Brush, определяющий способ рисования контура объекта.
  • StrokeThickness — толщина контура.
  • Fill — объект Brush, определяющий способ закрашивания внутренней части фигуры.

Обычно мы располагаем наши фигуры внутри контейнеров, например Grid или Canvas. Для рисования лучше всего использовать Canvas, так как тут мы имеем поддержку абсолютного позиционирования. Все дети контейнера Canvas могут быть расположены с помощью свойств Canvas.Left и Canvas.Top.

Давайте рассмотрим небольшой пример

Так наша вводная статья перетекла в первую, или о том, как можно прицепить передвижение, масштабирование и т.п. к динамически созданным объектам.
Вся прелесть примеров обычно состоит в том, что нигде невозможно найти именно того, что нужно. Практически все, что я находил, показывало, как создать фигуры в XAML. Сейчас я покажу как добавить на Canvas квадратик, который будет уметь расширяться и уменьшаться с помощью MultiTouch. Для этого подключим Microsoft.Phone.Controls.GestureService о котором я немного говорил тут.

Итак, для начала нам нужен Canvas, на котором мы будем располагать объекты.

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Canvas x:Name="ContentPanel" MouseLeftButtonDown="Canv_MouseLeftButtonDown" Grid.Row="1" Background="Transparent" Margin="0"></Canvas>
    </Grid>

Мы подписались на событие нажатия на Canvas, где и будем осуществлять динамическое добавление элемента и его передвижение.
Переходим в код к новому методу и начинаем писать код. Давайте ограничим количество наших объектов одним.

		bool flag = true;
		double initialFrameWidth = 200;
		double initialFrameHeight = 200;
		Rectangle Shape;
            if (flag)
			{
				Shape = new Rectangle()
				{
					Width = initialFrameWidth,
					Height = initialFrameHeight,
					Stroke = new SolidColorBrush(Colors.White),
					StrokeThickness = 8,
					Fill = new SolidColorBrush(Color.FromArgb(10, 255, 0, 0)),
				};

				Shape.SetValue(Canvas.TopProperty, 0d);
				Shape.SetValue(Canvas.LeftProperty, 0d);
				ContentPanel.Children.Add(Shape);
				flag = false;
			}

Итак, после этого, у нас появится квадрат. Он появится в левом верхнем углу, так как мы поставили 0 у Canvas.LeftProperty и Canvas.TopProperty.

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

		bool flag = true;
		double initialFrameWidth = 200;
		double initialFrameHeight = 200;
		private double realDragCoordinateX;
		private double realDragCoordinateY;
		CompositeTransform ShapeTransform = new CompositeTransform();
		Rectangle Shape;
            if (flag)
			{
				Shape = new Rectangle()
				{
					Width = initialFrameWidth,
					Height = initialFrameHeight,
					Stroke = new SolidColorBrush(Colors.White),
					StrokeThickness = 8,
					Fill = new SolidColorBrush(Color.FromArgb(10, 255, 0, 0)),
				};

				Shape.SetValue(Canvas.TopProperty, 0d);
				Shape.SetValue(Canvas.LeftProperty, 0d);
				Shape.RenderTransform = ShapeTransform;
				var touchListener = GestureService.GetGestureListener(Shape);
				touchListener.DragDelta += OnDragDelta;
				ContentPanel.Children.Add(Shape);
				flag = false;
			}

		private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
		{
			realDragCoordinateX = e.HorizontalChange + realDragCoordinateX;
			realDragCoordinateY = e.VerticalChange + realDragCoordinateY;
			if (realDragCoordinateX > 480 - Shape.ActualWidth)
			{
				ShapeTransform.TranslateX = 480 - Shape.ActualWidth;
			}
			else if (realDragCoordinateX < 0) 			{ 				ShapeTransform.TranslateX = 0; 			} 			else 			{ 				ShapeTransform.TranslateX = realDragCoordinateX; 			} 			if (realDragCoordinateY > 800 - Shape.ActualHeight)
			{
				ShapeTransform.TranslateY = 800 - Shape.ActualHeight;
			}
			else if (realDragCoordinateY < 0)
			{
				ShapeTransform.TranslateY = 0;
			}
			else
			{
				ShapeTransform.TranslateY = realDragCoordinateY;
			}
		}

Что мы сделали? Мы добавили RenderTransform для того, что бы знать про все трансформации нашего объекта. И мы подключили GestureService, о котором я писал тут. Метод OnDragDelta позволяет нам отслеживать позицию нашего объекта. Условия в методе обеспечивают то, что наш объект не вылезет за рамки экрана. Если убрать все проверки, то мы обязательно увидим вот такую ситуацию:

Теперь после нажатия на экран, в левом верхнем углу появляется наш квадратик, и при перетаскивании мышкой, он двигается. Заметьте, все сделано в коде!

Для того. что бы поставить наш квадратик там, где мы нажимаем, необходимо добавить две строки кода после того, как мы задаем RenderTransform:

				ShapeTransform.TranslateX = (e.GetPosition(ContentPanel).X - initialFrameWidth / 2);
				ShapeTransform.TranslateY = (e.GetPosition(ContentPanel).Y - initialFrameHeight / 2);

Выражение e.GetPosition(ContentPanel) отдаст нам точку, в которой мы нажали на Canvas.
Теперь добавим изменение масштаба. Для этого у GestureService существует PinchDelta на которое мы подписываемся и выполняем там проверку. Не буду приводить по частям код, сразу приведу весь код со страницы:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace CanvasShape
{
	public partial class MainPage
	{
		bool flag = true;
		double initialFrameWidth = 200;
		double initialFrameHeight = 200;
		double realDragCoordinateX;
		double realDragCoordinateY;
		Random random;
		CompositeTransform ShapeTransform = new CompositeTransform();
		Rectangle Shape;

		public MainPage()
		{
			random = new Random();
			InitializeComponent();
		}

		private void Canv_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			if (flag)
			{
				Shape = new Rectangle()
				{
					Width = initialFrameWidth,
					Height = initialFrameHeight,
					Stroke = new SolidColorBrush(Colors.White),
					StrokeThickness = 8,
					Fill = new SolidColorBrush(Color.FromArgb(50, 255, 0, 0)),
				};

				Shape.SetValue(Canvas.TopProperty, 0d);
				Shape.SetValue(Canvas.LeftProperty, 0d);
				Shape.RenderTransform = ShapeTransform;
				ShapeTransform.TranslateX = (e.GetPosition(ContentPanel).X - initialFrameWidth / 2);
				ShapeTransform.TranslateY = (e.GetPosition(ContentPanel).Y - initialFrameHeight / 2);
				realDragCoordinateX = ShapeTransform.TranslateX;
				realDragCoordinateY = ShapeTransform.TranslateY;
				Shape.RenderTransformOrigin = new Point(0.5, 0.5);

				var touchListener = GestureService.GetGestureListener(Shape);
				touchListener.DragDelta += OnDragDelta;
				touchListener.PinchDelta += OnPinchDelta;
				touchListener.DoubleTap += OnDoubleTap;
				ContentPanel.Children.Add(Shape);
				flag = false;
			}

		}

		#region GestureService

		void OnDoubleTap(object sender, GestureEventArgs e)
		{
			Shape.Width = initialFrameWidth;
			Shape.Height = initialFrameHeight;
			Shape.Fill =
				new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, (byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255)));
		}

		private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
		{
			realDragCoordinateX = e.HorizontalChange + realDragCoordinateX;
			realDragCoordinateY = e.VerticalChange + realDragCoordinateY;
			if (realDragCoordinateX > 480 - Shape.ActualWidth)
			{
				ShapeTransform.TranslateX = 480 - Shape.ActualWidth;
			}
			else if (realDragCoordinateX < 0) 			{ 				ShapeTransform.TranslateX = 0; 			} 			else 			{ 				ShapeTransform.TranslateX = realDragCoordinateX; 			} 			if (realDragCoordinateY > 800 - Shape.ActualHeight)
			{
				ShapeTransform.TranslateY = 800 - Shape.ActualHeight;
			}
			else if (realDragCoordinateY < 0) 			{ 				ShapeTransform.TranslateY = 0; 			} 			else 			{ 				ShapeTransform.TranslateY = realDragCoordinateY; 			} 		} 		private void OnPinchDelta(object sender, PinchGestureEventArgs e) 		{ 			Shape.Width = initialFrameWidth * e.DistanceRatio; 			Shape.Height = initialFrameHeight * e.DistanceRatio; 			if (Shape.Height > 800)
			{
				Shape.Height = 800;
			}
			if (Shape.Width > 480)
			{
				Shape.Width = 480;
			}
			if (ShapeTransform.TranslateX > 480 - Shape.Width)
			{
				ShapeTransform.TranslateX = 480 - Shape.Width;
			}
			if (ShapeTransform.TranslateY > 800 - Shape.Height)
			{
				ShapeTransform.TranslateY = 800 - Shape.Height;
			}
		}

		#endregion
	}
}

Итак, мы подписались еще на 2 события, одно будет происходить когда вы масштабируете, второе при двойном нажатии. GestureService не обязательно цеплять к таким объектам. Мы можем прицепить его к чему угодно, причем мы можем сделать это в коде (как я показывал выше) и мы можем все это сделать в XAML:

<controls:GestureService.GestureListener>
			<controls:GestureListener
                        Tap="OnTap" DoubleTap="OnDoubleTap"/>
		</controls:GestureService.GestureListener>

Где controls — «xmlns:controls=»clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit»
В GestureListener есть множество событий, многие из которых мы рассмотрим в следующих постах. Сейчас вернемся к нашей программе.
К сожалению, сейчас не могу показать скриншот с масштабом, выложу его в понедельник, вот что произойдет после двойного нажатия.

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

Реклама