Применение элементов RIA в Интернет-магазине
18.4.2. Работа с изображениями
Прежде всего, опишем вспомогательный класс CollectionImage, который будет хранить данные о каждом изображении, используемом в коллаже. Для того чтобы связать объекты этого класса с изображениями, которые будут использоваться в MultiScaleImage, сделаем в классе CollectionImage ссылку MultiScaleSubImage Image, которая будет указывать на соответствующий элемент в коллекции изображений ZoomImage.SubImages. Также в свойствах будет храниться положение изображение, его ширина и высота, порядковый номер и тег.
public class CollectionImage { public MultiScaleSubImage Image { get; set; } public Point Location { get; set; } public double Width { get; set; } public double Height { get; set; } public int ZOrder { get; set; } public string Tag { get; set; } }
Теперь добавим в класс страницы MainPage в файле MainPage.xaml.cs список CollectionImage, в котором будем хранить все данные об используемых изображениях.
ObservableCollection<CollectionImage> _images = new ObservableCollection<CollectionImage>();
При запуске приложения необходимо заполнить коллекцию _images. Это можно сделать, создав обработчик события ImageOpenSucceeded объекта ZoomImage. Когда это событие срабатывает, коллекция ZoomImage.SubImages уже заполнена, однако объекты типа MultiScaleSubImage не хранят данных, конторе были указаны в поле Tag в Deep Zoom Composer. Для того чтобы их загрузить необходимо прочитать сгенерированный файл Metadata.xml. Так как приложение запускается в браузере на клиенте, а файл хранится на сервере, то мы воспользуемся классом WebClient, метод DownloadStringAsync которого позволяет скачивать файлы с сервера. Когда файл скачен, срабатывает событие DownloadStringCompleted. Определив обработчик этого события, мы сможем разобрать скаченный файл. Так как это XML-файл, то имеет смысл воспользоваться классом XDocument чтобы его разобрать.
private void ZoomImage_ImageOpenSucceeded(object sender, RoutedEventArgs e) { duringOpen = true; WebClient wc = new WebClient(); wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted); wc.DownloadStringAsync(new Uri("Metadata.xml", UriKind.Relative)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Cancelled == false && e.Error == null) { string s = e.Result; XDocument doc = XDocument.Parse(s); var images = from a in doc.Element("Metadata").Descendants("Image") select a; foreach (XElement image in images) { CollectionImage ci = new CollectionImage { Height = Convert.ToDouble(image.Element("Height").Value), Width = Convert.ToDouble(image.Element("Width").Value), ZOrder = Convert.ToInt32(image.Element("ZOrder").Value) – 1, Tag = (string) image.Element("Tag").Value, Location = new Point {X = Convert.ToDouble( image.Element("x").Value), Y = Convert.ToDouble( image.Element("y").Value)} } ; ci.Image = ZoomImage.SubImages[ci.ZOrder]; _images.Add(ci); } } }
Стоит обратить внимание, что значение ZOrder изображений в файле Metadata.xml начинается с 1, в то время, как индекс коллекции ZoomImage.SubImages начинается с 0. Поэтому значение свойства ZOrder объектов CollectionImage будет на 1 меньше, чем то, что указано в файле.
18.4.3. Отображение
Если сейчас запустить разработанное приложение в тестовой странице, то оно отобразит набор изображений, но работать с ними не получится. Для того чтобы придать приложению интерактивность, определим код обработчиков нажатия на кнопки "Домой", "Приблизить" и "Удалить", обработчиков вращения колесика мышки, а также нажатия левой кнопки мыши и движения курса мыши по приложению.
Полный код этих обработчиков можно посмотреть в приведенном примере, здесь рассмотрим только обработчики нажатия мышью на кнопки "Домой" и "Приблизить".
private void Home_Click(object sender, MouseButtonEventArgs e) { InformationPanel.Visibility = Visibility.Collapsed; ProductImage.Visibility = Visibility.Collapsed; ZoomImage.ViewportOrigin = new Point(0, 0); ZoomImage.ViewportWidth = 1; zoom = 1; } private void ZoomIn_Click(object sender, MouseButtonEventArgs e) { double newzoom = zoom/1.3; if (newzoom < minzoom) { newzoom = minzoom; } Point logicalPoint = ZoomImage.ElementToLogicalPoint(new Point(ActualWidth/2, ActualHeight/2)); ZoomImage.ZoomAboutLogicalPoint(zoom/newzoom, logicalPoint.X, logicalPoint.Y); zoom = newzoom; var i = GetIntersectImage(); if (i > 0) { InformationPanel.Visibility = System.Windows.Visibility.Visible; ProductImage.Visibility = System.Windows.Visibility.Visible; } else { InformationPanel.Visibility = System.Windows.Visibility.Collapsed; ProductImage.Visibility = System.Windows.Visibility.Collapsed; } }
В обработчике Home_Click мы скрываем панель и изображение ProductImage, после чего строкой ZoomImage.ViewportOrigin = new Point(0, 0) устанавливаем верхний левый угол приложения в начальную точку (в случае использования одной картинки – это также ее левый верхний угол), а строкой ZoomImage.ViewportWidth = 1 устанавливаем текущий масштаб так, чтобы отобразилась целиком вся композиция изображений.
В обработчике ZoomIn_Click определяется новый масштаб. Далее, при помощи метода ElementToLogicalPoint определяются логические координаты центра приложения, после чего вызывается метод ZoomAboutLogicalPoint, который выполняет изменение масштаба изображения относительно указанной точки. В нашем случае масштаб будет уменьшен в 1.3 раза относительно центра.
Далее вызывается метод GetIntersectImage, который определяет, занимает ли какое-либо изображение все видимое пространство приложения, и если такое изображение найдено, то возвращает его порядковый номер и присваивает ProductImage ссылку на файл, имя которого берется из CollectionImage.Tag, то есть из тэга самого изображения. Если подходящее изображение найдено, необходимо сделать видимыми информационную панель с элементом ProductImage.
Ниже приведен код метода GetIntersectImage:
private int GetIntersectImage() { for (int i = 0; i < ZoomImage.SubImages.Count; i++) { MultiScaleSubImage subImage = ZoomImage.SubImages[i]; double scaleBy = 1/subImage.ViewportWidth; Rect rect = new Rect(-subImage.ViewportOrigin.X*scaleBy, -subImage.ViewportOrigin.Y*scaleBy, 1*scaleBy, (1/subImage.AspectRatio)*scaleBy); Point p1 = ZoomImage.ViewportOrigin; Point p2 = ZoomImage.ElementToLogicalPoint(new Point(ZoomImage.ActualWidth, 0.0)); Point p3 = ZoomImage.ElementToLogicalPoint(new Point(0.0, ZoomImage.ActualHeight)); Point p4 = ZoomImage.ElementToLogicalPoint(new Point(ZoomImage.ActualWidth, ZoomImage.ActualHeight)); if (rect.Contains(p1) && rect.Contains(p2) && rect.Contains(p3) && rect.Contains(p4)) { BitmapImage bmi = new BitmapImage (new Uri( "../ProductImages/" +_images[i].Tag+ ".png", UriKind.Relative)) {CreateOptions = BitmapCreateOptions.None}; ProductImage.Source = bmi; return i; } } return -1; }
Этот метод в цикле пробегает по всем изображениям из ZoomImage.SubImages и определяет логические координаты вершин, представляя изображение в виде объекта класса Rect. Затем определяются логические координаты вершин приложения, и если все вершины лежат внутри прямоугольника, представляющего изображение, то метод возвращает его номер.
18.4.4. Установка приложения Silverlight на страницу
Теперь, когда Silverlight -приложение разработано, необходимо добавит его в ASP-сайт. Для этого создадим страницу ProductBrowser2 и добавим на нее следующий код:
<asp:Content ID="Content2" ContentPlaceHolderID="column_l_placeholder" Runat="Server"> <div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" style="width: 640px; height: 480px;"> <param name="source" value="../ClientBin/SilverlightDeepZoom.xap"/> <param name="onError" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="3.0.40818.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0" style="text-decoration:none"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/> </a> </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div> </asp:Content>
Как и раньше, для того, чтобы установить Silverlight на страницу используется тег object. Описание всего кода, кроме параметра source, который указывает на файл ../ClientBin/SilverlightDeepZoom.xap, является стандартным для всех Silverlight -приложений.
Теперь, если открыть страницу ProductBrowser2 нашего Интернет-магазина (необходимо также доработать мастер страниц, чтобы ввести новый пункт меню), то запустится полноценное Silverlight -приложение, как на рис. 18.14 и рис. 18.15.
Теперь, когда мы научились разрабатывать интерактивные приложения для браузеров, рассмотрим еще одну задачу, которую приходится решать достаточно часто – добавление видеоплеера на страницу.