Статьи

Windows 8: приложения Магазина Windows и элемент управления WebView


Недавно я начал изучать создание простого приложения Windows 8 для Магазина Windows, которое будет содержать окно браузера и параметр, позволяющий изменять URI.

Само приложение довольно простое по своей концепции, поэтому я подумал, что реализовать его для Windows 8 будет очень просто.

Чтобы отобразить веб-страницу, мне нужно было использовать элемент управления WebView, и я просто установил для свойства Source нужный Uri.

Всплывающая панель настроек

После успешного построения моего решения и просмотра веб-страницы я решил, что пришло время перейти к созданию опции, позволяющей пользователю изменять URI.

Чтобы попытаться привести вещи в соответствие с другими приложениями Магазина Windows, я решил, что лучше всего попытаться интегрировать окно настроек в окно настроек Windows.

Я провел некоторое исследование, и интеграция в чудо-настройки настроек показалась мне довольно простой: вы просто добавляете обработчик события для события CommandsRequested очарования параметров и, когда оно вызывается, создаете обратный вызов SettingsCommand.

Показ выпадающего меню настроек казался простым, но я не мог заставить его отображаться.

При поиске в Интернете, поиске статей или сообщений на форуме, которые могли бы пролить свет на мою проблему, я наткнулся на набор инструментов с открытым исходным кодом Callisto, созданный Тимом Хойером, который предлагает стандартный код для приложений Магазина Windows.


Вы можете найти блог Тима Каллисто здесь:
http://timheuer.com/blog/archive/2012/05/31/introduction-callisto-a-xaml-toolkit-for-metro-apps.aspx

Репозиторий GitHub для инструментария Callisto можно найти здесь:
https://github.com/timheuer/callisto

Чтобы установить пакет Callisto в свой проект через NuGet (
https://nuget.org/packages/Callisto ), вы можете использовать следующую команду:


PM> Install-Package Callisto

К сожалению, даже с инструментарием Callisto всплывающее окно с моими настройками все еще не отображалось.

Когда я пытался создать всплывающую подсказку с настройками самостоятельно, перед тем, как я обнаружил Callisto, мне нужно было установить высоту всплывающей подсказки, поэтому я начал задаваться вопросом, возможно ли, проблема заключается в том, что настройка Stretch элемента управления WebView VerticalAlignment была проблемой, поскольку я не устанавливал явную значение высоты

Я установил высоту элемента управления WebView равным 700 пикселям, и когда я запустил свое приложение, я обнаружил, в чем на самом деле была проблема.

Оказывается, всплывающее окно настроек отображалось все время, но элемент управления WebView скрывал его, потому что теперь я вижу, как он отображается за элементом управления WebView.

WebViewBrush

После нескольких дополнительных исследований я пришел к выводу, что при отображении всплывающего окна вам нужно обойтись с элементом управления WebView (к сожалению, это напоминает мне IE 6 и то, как объекты Select будут отображаться через плавающий div).

Далее предлагается обходной путь для отображения всплывающего окна над элементом управления WebView:

  • Добавьте объект Rectangle в XMAL вашего представления с теми же размерами, что и элемент управления WebView
  • Непосредственно перед отображением всплывающей подсказки создайте WebViewBrush с текущим содержимым элемента управления WebView (в основном снимок экрана)
  • Примените кисть к элементу управления Rectangle
  • Скрыть элемент управления WebView
  • Показать всплывающее окно
  • Когда выпадающее меню закроется, снова отобразите элемент управления WebView.
  • Очистить кисть от элемента управления Rectangle

Я изменил свое представление, чтобы использовать обходной путь, и теперь во всплывающем окне настроек отображаются данные, которыми я очень доволен

Основная проблема, которую я вижу сейчас, заключается в том, что при переключении на WebViewBrush наблюдается заметное мерцание.

Моя первоначальная мысль, поскольку метод перерисовки кисти является асинхронным, заключался в том, что элемент управления WebView скрывался за долю секунды до того, как кисть успела завершить загрузку, в результате чего фон представления был кратковременно видимым, вызывая мерцание.

Я перепробовал все, что мог придумать, чтобы избавиться от мерцания, включая создание переменной-члена WebViewBrush и конструирование ее во время конструктора представления.

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

async Task.Delay

Что-то, что мне было любопытно, это то, что может помочь заставить приложение спать, между вызовом перерисовки кисти и скрытием WebView.

Как оказалось, в приложении Магазина Windows нет метода Thread.Sleep, но я нашел обходной путь, используя асинхронный вызов Task.Delay (5000) (я использовал 5000 миллисекунд, чтобы было очевидно, что вызов сна работает)

К моему приятному удивлению, мерцание исчезло!

Когда я посмотрел ближе на код, мысль пришла мне в голову …


У меня был метод Redraw WebViewBrush, а также свойство Fill Rectangle, устанавливаемое до задержки.

Что, если мерцание не из-за метода перерисовки кисти?

Что если проблема связана со свойством Fill объекта Rectangle?

Я переместил вызов Fill прямоугольника после задержки, и вернулся мерцание, которое указывает мне, что проблема не в кисти, а в свойстве Fill Rectangle.

Когда я начал думать о том, в чем проблема, мне в голову пришла мысль …


Что, если это ведет себя подобно тому, как поток пользовательского интерфейса ведет себя в веб-браузере?

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

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

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

Чтобы обойти эту проблему обновления пользовательского интерфейса, в веб-браузере вы указываете элементу управления пользовательского интерфейса для отображения, который добавляет этот элемент в конец очереди окна, а затем вы устанавливаете тайм-аут для вызова функции, которая будет выполнять фактическую обработку, которая помещает функция обработки в очереди сразу после обновления пользовательского интерфейса.

Когда исходная функция завершается, пользовательский интерфейс обновляется, а затем вызывается функция обработки.

Исходя из поведения, которое я вижу, я полагаю, что приложение Магазина Windows использует технику, аналогичную потоку пользовательского интерфейса в окне веб-браузера.

Если я установлю свойство Rectangle Fill с помощью WebViewBrush, а затем добавлю асинхронную задержку в 1 миллисекунду, прежде чем продолжить для отображения всплывающих окон настроек мерцания нет!

Я по-прежнему видел мерцание время от времени при закрытии выпадающего меню «Настройки» с помощью кнопки «Назад», а затем очень быстрого повторного отображения всплывающего окна. Увеличение задержки примерно до 100 миллисекунд, кажется, улучшает этот сценарий.

Пример кода

Ниже приведен пример необходимого XAML:

<Grid>
<WebView x:Name="wvBrowser" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />

<Rectangle x:Name="rectWebViewBrush" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Rectangle>
</Grid> 

Ниже приведен пример исходного кода, необходимого для отображения всплывающего окна «Параметры» при работе с элементом управления WebView:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
SettingsPane.GetForCurrentView().CommandsRequested += MainPage_CommandsRequested;
}

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
SettingsPane.GetForCurrentView().CommandsRequested -= MainPage_CommandsRequested;

base.OnNavigatingFrom(e);
}


// Called by the Settings charm to find out what Settings links
// to display and the code to call when clicked.
void MainPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
// Set up the Link for the Settings charm
SettingsCommand cmdSettingsOptions = new SettingsCommand("cmdSettingsOptionsLabel", "Options", (x) =>
{
// Get a brush from the WebView's content (basically,
// a screen shot)
WebViewBrush wvbBrush = new WebViewBrush();
wvbBrush.SourceName = "wvBrowser";
wvbBrush.Redraw();

// Fill the Rectangle object with the brush
rectWebViewBrush.Fill = wvbBrush;

// Show the settings flyout
ShowSettingsFlyout();
});

// Add our Setting link to our applications list of settings
args.Request.ApplicationCommands.Add(cmdSettingsOptions);
}


// Creates and displays the Settings flyout:
async void ShowSettingsFlyout()
{
// Give the Rectangle a chance to refresh so that we
// don't have flicker
await Task.Delay(100);

// Create the Settings flyout and display it (this is using
// the Callisto open-source toolkit). Additional attributes
// can be set like the background color and header brush
// to better represent your app's look and feel
SettingsFlyout settings = new SettingsFlyout();
settings.FlyoutWidth = Callisto.Controls.SettingsFlyout.SettingsFlyoutWidth.Narrow;
settings.HeaderText = "Options";

// Intercept the setting's closed event so that we can
// switch back to the WebView control
settings.Closed += settings_Closed;

// Our UserControl derived class that holds the controls
// for our settings
settings.Content = new SettingsOptionsContent();

// Switch to the WebViewBrush and then show the
// settings flyout
SwitchToWebViewScreenShot(true);
settings.IsOpen = true;
}


void SwitchToWebViewScreenShot(bool bSwitchToScreenShot)
{
// If we're to show the screen shot then...
if (bSwitchToScreenShot)
{
// Hide the WebView control (MainPage_CommandsRequested
// has already set the rectangle's fill with a screen shot of the
// contents of the WebView control)
wvBrowser.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else // We're to show the WebView again...
{
// Show the WebView control and remove the WebViewBrush
// from the Rectangle
wvBrowser.Visibility = Windows.UI.Xaml.Visibility.Visible;
rectWebViewBrush.Fill = new SolidColorBrush(Colors.Transparent);
}
}


// Called when the Settings flyout closes
void settings_Closed(object sender, object e)
{
// Switch back to the WebView control rather than the screen shot
SwitchToWebViewScreenShot(false);
} 

В итоге

Элементы управления WebView не позволяют всплывающему элементу появляться над ним и требуют использования WebViewBrush для отображения экрана во время отображения всплывающего окна.

Заполнение объекта Rectangle объектом WebViewBrush и немедленное скрытие элемента управления WebView создает мерцание, которое можно обойти, прерывая поток обработки пользовательского интерфейса путем добавления асинхронной задержки перед скрытием элемента управления WebView.

Приложения из Магазина Windows не поддерживают Thread.Sleep, но вы можете добиться аналогичного эффекта с помощью «async Task.Delay».

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

Между тем, я надеюсь, что это поможет.

Скачать исходный код

Заархивированный мой проект (C # и созданный с использованием Visual Studio Express 2012 для Windows 8) можно найти по следующему адресу : https://github.com/downloads/dovicoapi/DOVICOTimerForWindowsStore/DOVICOTimerForWindowsStore.zip

Примечание. Этот проект до сих пор работа в процессе, поэтому он может не соответствовать всем ограничениям Магазина Windows. Я буду обновлять этот проект на GitHub по мере продвижения.