Это очень просто, довольно весело и демонстрирует небольшое использование 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
Opacity и 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. И мы сделали.
Поместите это поведение в потомок Panel (например, в Grid), заполните свойство BingSearchKey действительным ключом, свяжите свойство «SearchString» с строкой в ViewModel и, как только изменится значение SearchString, поведение будет показать первое доступное изображение, возвращаемое поиском Bing Image в качестве фона на этой сетке.
Я собрал небольшое демонстрационное приложение, содержащее и демонстрирующее поведение . Это отклоняется двумя способами от моего обычного режима работы. Во-первых, не работает из коробки — сначала вам нужно получить собственный ключ API поиска Bing. Второе: он не использует MVVM — у меня есть данные, привязывающие свойство SearchString поведения непосредственно к свойству TextBox Text, которое заставляет поведение начинать поиск фона сразу же после начала набора текста, как показано слева. Так что, если вы наберете «морковь» в текстовое поле, то получите — изображение моркови ?
Ну, это все на 2011 год. Это особенный для меня год с некоторыми взлетами и падениями, когда награда MVP для Windows Phone Development определенно стала главным событием в категории «взлеты». Теперь до 2012 года, который, я думаю, окажется действительно очень интересным. Я надеюсь, что вы все продолжите получать удовольствие от этого блога, как и я — и вы, очевидно, тоже в 2011 году.