Статьи

Приложения Магазина Windows, SVG и HTML-совместимость

Существует несколько причин, по которым вы можете захотеть показывать контент HTML в своем приложении Магазина Windows. У вас может быть информация, которая часто обновляется и имеет смысл использовать в виде HTML-данных. Ваше приложение может собирать фиды, содержащие контент HTML. В некоторых случаях вы можете создавать собственный клиент, который обращается к существующему веб-приложению, с которым вам нужно взаимодействовать. К счастью, WinRT имеет элемент управления, который отвечает этим требованиям: элемент управления WebView.

Важно понимать, какова цель элемента управления и как это может повлиять на то, как вы разрабатываете свое приложение. Сам элемент управления не предназначен для полной замены браузера. Действительно, Microsoft ясно дала понять, что если вы попытаетесь одобрить приложение в магазине, которое пытается взаимодействовать исключительно с веб-приложением или просто действовать как веб-браузер, оно будет отклонено [1-десять вещей, которые вам нужны Знать о WebView . Есть несколько ограничений, встроенных в элемент управления, который предотвращает многие сценарии.

Элемент управления использует движок Internet Explorer 10. Он не поддерживает расширенные функции в HTML5, включая кеширование, индексированные базы данных, программный доступ к буферу обмена или географическое местоположение. Он поддерживает ту же версию объектной модели документов (DOM), которая используется приложениями Магазина Windows, написанными на JavaScript [2 — объектная модель документов (DOM) (Windows)] . Не поддерживается никакой тип плагина или расширения ActiveX, включая Flash, Silverlight или встроенные документы PDF.

Существует полная поддержка расширенной обработки протокола [3 — Автоматический запуск с сопоставлениями файлов и URI] . Это обеспечивает поддержку таких вещей, как пользовательские протоколы, которые позволяют вам получать доступ к ресурсам, встроенным в ваше приложение, а также ссылки, которые автоматически запускают связанную с ними программу. Вы можете создать веб-страницу, которая при просмотре не только предоставляет контент, обслуживаемый из вашего приложения, но и позволяет пользователю открывать эти ресурсы с помощью предпочтительного приложения (например, ресурс с расширением .pdf откроется с предпочтительным приложение для чтения).

Элемент управления также уникален, потому что он не может быть покрыт другими элементами управления. Он полностью принимает пиксели, определенные для его области. Все элементы управления, которые вы пытаетесь разместить перед элементом управления WebView, будут обрезаны. Если вам необходимо наложить страницу на элементы управления, вы можете захватить текущий вид с помощью специального WebViewBrush и использовать эту кисть для рендеринга поверхности.

Последняя и, пожалуй, самая важная особенность элемента управления WebView — возможность взаимодействия с приложением Windows Store. Ваше приложение может взаимодействовать с веб-страницей, вызывая функции JavaScript и передавая параметры. Страница также может взаимодействовать с вашим приложением, вызывая метод объекта, который WinRT создает в DOM. Это позволяет создавать действительно интерактивные взаимодействия между веб-контентом и приложением Windows Store.

Проект WebViewExamples содержит несколько примеров работы с HTML и JavaScript. MainPage.xaml определяет элемент управления WebView с именем WebViewControl и кисть с именем WebBrush. Когда страница загружена, обработчик регистрируется в событии LoadCompleted в WebView. Это будет запускаться каждый раз, когда загружается новый ресурс (будь то строковый литерал или веб-страница). Это заставит кисть обновиться и покажет диалоговое окно с указанием загруженного содержимого.

private async void WebViewControlLoadCompleted(object sender, NavigationEventArgs e)
{     
     WebBrush.Redraw();
     var url = e.Uri != null ? e.Uri.ToString() : "Text Content";
     var popup = new MessageDialog(url, "Content Loaded.");
     await popup.ShowAsync();

}

Первая кнопка запускает прямую загрузку страницы.

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
     this.WebViewControl.Navigate(new Uri(JeremyBlog));
}

Эта страница — моя главная страница блога. Вы найдете, что он загружается и отдает хорошо. Вы также можете нажимать на ссылки и перемещаться так же, как если бы вы открывали страницу в веб-браузере. Нет кнопок «вперед» или «назад», и нет места для ввода нового адреса, поэтому вы застряли по ссылкам, предоставленным элементом управления. Обратите внимание, что диалоговое окно запускается каждый раз, когда вы переходите на новую страницу — вы можете легко использовать эту функцию, чтобы проверить, где оказался пользователь, и скрыть элемент управления или сбросить его, если это необходимо.

Вторая кнопка вызывает определенную страницу в качестве мобильного клиента, написав пользовательский агент, который имитирует iPhone. Мой блог обнаружит, что мобильное устройство получает доступ к странице, и перенаправит на мобильную страницу. В этом режиме вы можете увидеть несколько ошибок, выданных отладчиком. Это связано с тем, что элемент управления WebView выдает ошибку каждый раз, когда обнаруживает ошибку JavaScript или проблему с анализом DOM. Вы можете отключить ошибки JavaScript, зайдя в настройки отладчика и отменив проверку скрипта на вкладке Just-In-Time . Другие ошибки, с которыми вам придется столкнуться, и просто выберите Нет— на моей машине мобильная страница генерирует четыре ошибки, которые я должен подтвердить перед доступом к странице. Если вы запустите приложение вне отладчика, оно поглотит эти ошибки и будет работать нормально.

Единственный способ обновить пользовательский агент, который использует элемент управления WebView, — это использовать некоторые хаки, которые не позволят вашему приложению попасть в магазин приложений. Для тех хакеров в аудитории вам нужно будет p / Invoke urlmon.dll и вызвать UrlMkSetSessionOption с опцией 0x10000001 и строку вашего пользовательского агента, прежде чем переходить с помощью элемента управления. Более прямой подход — загрузить страницу самостоятельно и передать ее в виде текста. Шаги показаны ниже.

private async void ViewMobile_OnClick(object sender, RoutedEventArgs e)
{
     var handler = new HttpClientHandler { AllowAutoRedirect = true };
     var client = new HttpClient(handler);
     client.DefaultRequestHeaders.Add("user-agent", MobileUserAgent);
     var response = await client.GetAsync(new Uri(JeremyYogaPost));    
     response.EnsureSuccessStatusCode();
     var html = await response.Content.ReadAsStringAsync();
     this.WebViewControl.NavigateToString(html);
}

В этом примере настраивается клиент, который принимает перенаправления (в случае ссылки на сообщение в блоге он будет перенаправлен на мобильную версию), затем создается клиент и настраивается пользовательский агент для имитации мобильного устройства. Страница запрашивается, и код обеспечивает успешный ответ до загрузки содержимого ответа и отправки его в элемент управления WebView.

Этот метод является общим для запроса контента в формате, который подходит для просмотра в вашем приложении (мобильный контент имеет тенденцию иметь меньше хрома, поэтому его легче обрабатывать). Вы также можете проанализировать и проанализировать контент, прежде чем представить его конечному пользователю. Это позволяет вам удалять ненужные теги или даже вставлять свой собственный JavaScript и контент по мере необходимости до отображения страницы.

В некоторых приложениях вы можете включить встроенные HTML-ресурсы для отображения своих приложений. Третья кнопка демонстрирует эту технику. Это также показывает, что механизм рендеринга полностью поддерживает Scalable Vector Graphics (SVG) [4 — W3C Рабочая группа SVG] , что может быть полезно, если вам нужно интегрировать диаграммы или векторную графику в ваше приложение и иметь существующие реализации с использованием SVG , В примере приложения есть встроенный HTML-файл с именем Ellipse.html в папке Data.

Ссылка на этот ресурс проста и понятна. Специальный протокол ms-appx-web предоставляет путь к элементу управления WebView, встроенному в приложение магазина. Вы заметите, что он использует три косых черты, а затем предоставляет путь к ресурсу в приложении из корня.

private void ViewSvg_OnClick(object sender, RoutedEventArgs e)
{
     this.WebViewControl.Navigate(new
          Uri("ms-appx-web:///Data/Ellipse.html"));
}

Вы также можете встраивать статический текст (или динамический текст, который вы создаете и генерируете до передачи в элемент управления). Четвертая кнопка запускает загрузку строкового литерала. Литерал содержит текст для документа HTML и включает в себя различные теги заголовка, встроенное изображение (логотип самого приложения) и гиперссылку, демонстрирующую, что вы можете перейти к встроенному контенту или внешнему контенту. Если вы перейдете по ссылке на внешний веб-сайт, вы увидите, что базовый URL-адрес постоянно отображается как статический текст, поскольку именно так был загружен исходный документ.

private void ViewString_OnClick(object sender, RoutedEventArgs e)
{
     this.WebViewControl.NavigateToString(HtmlFragment);
}

Последняя кнопка демонстрирует, как ваше приложение может взаимодействовать с Интернетом. Чтобы показать это, я включил функцию JavaScript в шаблон моего блога. Функция представляет собой всего несколько строк кода. Если вы загружаете какую-либо страницу из моего блога и просматриваете источник, вы можете выполнить поиск «supersecret», чтобы найти функцию:

function superSecretBiographyFunction(subPath) {
   window.location.href="http://csharperimage.jeremylikness.com/" + subPath;
}

Функция обычно не вызывается, но она изменится, если вы загрузите одну из страниц блога с помощью примера приложения и нажмете последнюю кнопку. Фрагмент кода ниже показывает реализацию. Метод InvokeScript используется для вызова функции. Вы можете передать значение null для функции, которая не принимает параметров, в противном случае вы можете передать массив значений. В этом случае путь к моей биографии передается, и страница берет его и переходит к пути. Обратите внимание, что вы можете вызывать JavaScript на любой тип страницы, отображаемой элементом управления, включая встроенные ресурсы или литералы, которые вы передали.

private async void CallJavaScript_OnClick(object sender, RoutedEventArgs e)
{
     MessageDialog popup = null;
     var parameters = new[] 
                          {
                                 "p/biography.html"
                          };
     try 
     {
        this.WebViewControl
                .InvokeScript("superSecretBiographyFunction", parameters);
    }
    catch (Exception ex)
     {
        popup = new MessageDialog(ex.Message, "Unable to Call JavaScript.");
     }
     if (popup != null)
     {
         await popup.ShowAsync();
     }
}

Попытка вызвать функцию на странице, которая не имеет этой функции, приведет к исключению. Код фиксирует исключение, недружественное для пользователя, и отображает его для вас. Важно отметить шаблон, потому что невозможно использовать ключевое слово await из блока catch. Обходной путь — собрать всю необходимую информацию, необходимую для перехвата, а затем дождаться диалогового окна для отображения информации после выхода из блока.

Хотя отправка информации на страницу — это хорошо, не было бы замечательно, если бы вы могли получать информацию обратно? Этот сценарий возможен, и вы на самом деле можете создавать приложения, которые ведут двустороннюю беседу с веб-страницами. Если вы искали приведенный выше фрагмент кода, возможно, вы также заметили этот блок кода:

if (document.title==='C#er : IMage: Synchronous to Asynchronous Explained') {
if ((typeof (window.external) !== "undefined") && (typeof (window.external.notify) !== "undefined")) {
      window.external.notify(new Date().toLocaleString());
   }
}

Я бы не стал сравнивать строки с заголовком темы, чтобы вызвать логику приложения, но я могу с этим справиться, потому что у меня есть блог, и я не планирую менять заголовок. Когда пользователь переходит к определенной теме внутри элемента управления WebView, сама веб-страница фактически отправляет сообщение в приложение Магазина Windows, которое содержит локализованную дату и время. Связь между веб-страницей и приложением Магазина Windows показана здесь:

htmlwinstoreinterop

Функция сначала проверяет существование объекта window.external. Это предоставляется среде выполнения JavaScript Internet Explorer для предоставления доступа к объектной модели хоста [5 — Внешний объект (Internet Explorer)] (в данном случае хост является элементом управления WebView). Элемент управления, в свою очередь, предоставляет метод notify среде выполнения Вызов этого с веб-страницы с одним строковым параметром вызовет событие ScriptNotify в элементе управления WebView.

В примере приложения просто хранится значение, которое передается локальной переменной.

private void WebViewControlScriptNotify(object sender, 
    Windows.UI.Xaml.Controls.NotifyEventArgs e)
{
     this.message = e.Value;
}

После загрузки всей веб-страницы событие LoadCompleted проверяет это значение и отображает его при его наличии.

if (string.IsNullOrEmpty(this.message))
{
     return;
}
 
popup = new MessageDialog(
     this.message,
      "The Blog Has Spoken!");
this.message = string.Empty;
await popup.ShowAsync();

Чтобы увидеть это в действии, используйте временную шкалу в правой части моего блога, чтобы перейти к 2012 году, августу и названию «Синхронно объяснено асинхронно». В результате должно появиться два всплывающих окна, одно из которых уведомляет вас о загрузке страницы, а второе сообщает, что блог произнес, и отображает дату. Это закрывает цикл и демонстрирует, как приложения Магазина Windows могут загружать, отображать и взаимодействовать с содержимым на основе HTML.

Этот пост лишь поверхностно рассказывает о том, что возможно, но, надеюсь, предоставит вам детальную демонстрацию того, как вы можете встраивать HTML, SVG и даже JavaScript в свои приложения Магазина Windows.