Статьи

Поведение Windows Phone 7 для отображения фона изображения для строки поиска

Это очень просто, довольно весело и демонстрирует небольшое использование Rx, довольно необычно использует API Bing Image Search — и это сыграет вспомогательную роль в моем новом приложении для Windows Phone 7. Базовая структура поведения настраивается с использованием библиотеки SafeBehavior # wp7nl, которую я описал ранее:

using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Phone.Reactive;
 
namespace Wp7nl.Behaviors
{
  /// <summary>
  /// A behavior that puts an image on the background of the Attched object
  /// using Bing Image Search
  /// </summary>
  public class DynamicBackgroundBehavior : SafeBehavior<Panel>
  {
    private ImageBrush backgroundBrush;
    
    public DynamicBackgroundBehavior()
    {
      Opacity = 1.0;
    }

    #region SearchString
    public const string SearchStringPropertyName = "SearchString";

    /// <summary>
    /// The search string to be used on Bing Maps
    /// </summary>
    public string SearchString
    {
      get { return (string)GetValue(SearchStringProperty); }
      set { SetValue(SearchStringProperty, value); }
    }

    public static readonly DependencyProperty SearchStringProperty = 
        DependencyProperty.Register(
        SearchStringPropertyName,
        typeof(string),
        typeof(DynamicBackgroundBehavior),
        new PropertyMetadata(String.Empty, SearchStringChanged));

    public static void SearchStringChanged(DependencyObject d, 
                                           DependencyPropertyChangedEventArgs e)
    {
      var behavior = d as DynamicBackgroundBehavior;
      if (behavior != null)
      {
        behavior.StartGetFirstImage((string)e.NewValue);
      }
    }
    #endregion

    /// <summary>
    /// Bing search key
    /// </summary>
    public string BingSearchKey { get; set; }

    /// <summary>
    /// Stretch used for the image
    /// </summary>
    public Stretch Stretch { get; set; }

    /// <summary>
    /// Opacity used for the image
    /// </summary>
    public double Opacity { get; set; }
  }
} 

Короче говоря, это поведение имеет четыре свойства:

  • помутнение
  • Протяжение
  • BingSearchKey
  • SearchString

BingSearchApiOpacity и Stretch — это просто простые свойства изображений, которые будут отображаться. SearchString — это строка, которая будет использоваться для поиска изображения. Это свойство зависимости, поэтому его можно использовать в привязке данных. Обратите внимание на метод SearchStringChanged — он вызывается при изменении свойства SearchString. Это, в свою очередь, запускает метод StartGetFirstImage, который начнет фактический поиск.

BingSearchKey — это строка длиной 40 символов, идентифицирующая ваше приложение. Вы должны создать ключ для своего приложения на портале Bing Developer . Нажмите левую кнопку («Войти — API поиска Bing») и заполните форму, изображенную справа (нажмите для увеличения изображения).

Примечание: поведение ссылается на Microsoft.Phone.Reactive — поэтому проект, в котором это происходит, должен называться Microsoft.Phone.Reactive.dll и System.Observable.dll.

Перейдем к настройке поведения, которая теперь очень проста, так как мы опираемся на SafeBehavior:

/// <summary>
/// Setup the behavior
/// </summary>
protected override void OnSetup()
{
  backgroundBrush = new ImageBrush
  {
    Stretch = Stretch,
    Opacity = Opacity
  };

  // Set the image brush to the background of the Panel 
  AssociatedObject.Background = backgroundBrush;
}

Проще говоря: создайте кисть Image, используя настройки свойств, и поместите ее в качестве фона для элемента GUI, к которому привязано поведение.

Метод, который вызывается из SearchStringChanged (который запускается при изменении свойства зависимости SearchString), реализован, как показано ниже:

/// <summary>
/// Start the image request using Bing Serach
/// </summary>
/// <param name="searchString"></param>
protected void StartGetFirstImage(string searchString)
{
  var queryUri = 
    string.Format(
      "http://api.bing.net/xml.aspx?Appid={0}&query={1}&sources=image",
      BingSearchKey, searchString);
  var request = WebRequest.Create(queryUri) as HttpWebRequest;
  var response = 
    Observable.FromAsyncPattern<WebResponse>(
      request.BeginGetResponse, request.EndGetResponse)();
  response.Subscribe(WebClientOpenReadCompleted, WebClientOpenReadError);
}

Uri формируется с использованием BingSearchKey и фактической строки поиска, а также в разделе «sources = image», сообщающем Bing, что нужно возвращать изображения. Это Ури подается на стандартный веб-запрос. И тогда в игру вступает инфраструктура Rx, чтобы легко обработать процесс асинхронного чтения. Использование Observable.FromAsyncPattern и Subscribe имеет явное преимущество, заключающееся в том, что нет необходимости подключать все виды обработчиков событий, перехватывать ошибки с помощью блоков try-catch и не забывать отключать обработчики событий, когда чтение завершено. Платформа Rx обрабатывает все это, поэтому мне не нужно беспокоиться об этом.

Последний фрагмент поведения — фактическая обработка изображения:

/// <summary>
/// Called when image search returns
/// </summary>
/// <param name="result"></param>
private void WebClientOpenReadCompleted(WebResponse result)
{
  using (var stream = result.GetResponseStream())
  {
    using (var reader = XmlReader.Create(stream,
       new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
    {
      var doc = XDocument.Load(reader);

      // Get the first image from the result
      XNamespace ns = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/multimedia";
      if (doc.Root != null)
      {
        var firstImage = doc.Root.Descendants(ns + "MediaUrl").FirstOrDefault();
        if (firstImage != null)
        {
          Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
              var bi = new BitmapImage
                {
                  UriSource = new Uri(firstImage.Value),
                  CreateOptions = BitmapCreateOptions.BackgroundCreation
                };
              backgroundBrush.ImageSource = bi;
            });
        }
      }
    }
  }
}

/// <summary>
/// Called upon a search error (not used)
/// </summary>
/// <param name="ex"></param>
private void WebClientOpenReadError(Exception ex)
{
}

Поскольку на страницу xml.aspx ссылаются, Bing возвращает результат в виде документа xml. Если вас интересуют подробности результатов поиска Bing, не стесняйтесь исследовать документ XML — этот код в основном просто находит первый тег «MediaUrl», создает из него BitMapImage и помещает результат в backgroundBrush. И мы сделали.

carrotdemoПоместите это поведение в потомок Panel (например, в Grid), заполните свойство BingSearchKey действительным ключом, свяжите свойство «SearchString» с строкой в ​​ViewModel и, как только изменится значение SearchString, поведение будет показать первое доступное изображение, возвращаемое поиском Bing Image в качестве фона на этой сетке.

Я собрал небольшое демонстрационное приложение, содержащее и демонстрирующее поведение . Это отклоняется двумя способами от моего обычного режима работы. Во-первых, не работает из коробки — сначала вам нужно получить собственный ключ API поиска Bing. Второе: он не использует MVVM — у меня есть данные, привязывающие свойство SearchString поведения непосредственно к свойству TextBox Text, которое заставляет поведение начинать поиск фона сразу же после начала набора текста, как показано слева. Так что, если вы наберете «морковь» в текстовое поле, то получите — изображение моркови ?

 

Ну, это все на 2011 год. Это особенный для меня год с некоторыми взлетами и падениями, когда награда MVP для Windows Phone Development определенно стала главным событием в категории «взлеты». Теперь до 2012 года, который, я думаю, окажется действительно очень интересным. Я надеюсь, что вы все продолжите получать удовольствие от этого блога, как и я — и вы, очевидно, тоже в 2011 году.