Применение элементов 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.
Теперь, когда мы научились разрабатывать интерактивные приложения для браузеров, рассмотрим еще одну задачу, которую приходится решать достаточно часто – добавление видеоплеера на страницу.


