Опубликован: 04.05.2010 | Доступ: свободный | Студентов: 4032 / 454 | Оценка: 4.64 / 4.44 | Длительность: 41:24:00
Практическая работа 5:

Применение элементов RIA в Интернет-магазине

< Лекция 13 || Практическая работа 5: 1234 || Лекция 14 >

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; }
    }
Примечание: если во время работы Deep Zoom Composer на этапе экспорта был выбран вариант экспорта в виде одного изображения то коллекция ZoomImage.SubImages будет пустой.

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

Примечание: подробнее узнать о том, как работают свойства ViewportOrigin и ViewportWidth можно по этой ссылке: http://msdn.microsoft.com/en-us/library/system.windows.controls.multiscaleimage.viewportorigin%28VS.95%29.aspx

В обработчике 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 -приложений.

Примечание: при компиляции проектов, файл SilverlightDeepZoom.xap будет автоматически помещен в папку ClientBin. Однако может потребоваться перенести вручную в эту папку файлы Deep Zoom, иначе приложение не будет работать. В отличие от всего сайта, для которого каталог, в котором располагается сайт, является корневым, для Silverlight -приложения корневой является директория ClientBin и все необходимые для работы ресурсы должны располагаться в этой директории (рис. 18.13).
Структура директории ClientBin

Рис. 18.13. Структура директории ClientBin

Теперь, если открыть страницу ProductBrowser2 нашего Интернет-магазина (необходимо также доработать мастер страниц, чтобы ввести новый пункт меню), то запустится полноценное Silverlight -приложение, как на рис. 18.14 и рис. 18.15.

Приложение SilverlightDeepZoom в действии

увеличить изображение
Рис. 18.14. Приложение SilverlightDeepZoom в действии
Приложение SilverlightDeepZoom в действии

увеличить изображение
Рис. 18.15. Приложение SilverlightDeepZoom в действии

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

< Лекция 13 || Практическая работа 5: 1234 || Лекция 14 >