Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
AppWindow и его связанные API упрощают создание приложений с несколькими окнами, позволяя отображать содержимое приложения в дополнительных окнах, а также работать над тем же потоком пользовательского интерфейса в каждом окне.
Замечание
AppWindow в настоящее время находится в предварительной версии. Это означает, что вы можете отправлять приложения, использующие AppWindow, в Магазин, но некоторые компоненты платформы и фреймворка, как известно, не работают с AppWindow (см. Ограничения).
Здесь показаны некоторые сценарии для нескольких окон с приложением HelloAppWindow. Пример приложения демонстрирует следующие функции:
- Открепите элемент управления с главной страницы и откройте его в новом окне.
- Откройте новые экземпляры страницы в новых окнах.
- Программно задавайте размер и положение новых окон в приложении.
- Свяжите ContentDialog с соответствующим окном в приложении.
Пример приложения с одним окном
Пример приложения с незакрепленным цветовым элементом выбора и дополнительным окном
Важные API: пространство имен Windows.UI.WindowManagement, класс AppWindow
Обзор API
Класс AppWindow и другие API в пространстве имен WindowManagement доступны начиная с Windows 10, версии 1903 (SDK 18362). Если приложение предназначено для более ранних версий Windows 10, необходимо использовать ApplicationView для создания дополнительных окон. API WindowManagement по-прежнему находятся в процессе разработки и имеют ограничения , как описано в справочной документации по API.
Ниже приведены некоторые важные API, которые вы используете для отображения содержимого в AppWindow.
Окно приложения
Класс AppWindow можно использовать для отображения части приложения UWP в дополнительном окне. Это похоже на концепцию ApplicationView, но не то же самое в поведении и времени существования. Основной особенностью AppWindow является то, что каждый экземпляр использует один и тот же поток обработки пользовательского интерфейса (включая диспетчер событий), из которого они были созданы, что упрощает многоэкционные приложения.
Содержимое XAML можно подключить только к AppWindow. Поддержка собственного содержимого DirectX или Holographic отсутствует. Однако можно показать панель XAML SwapChainPanel, на которой размещено содержимое DirectX.
окружение оконного интерфейса
API WindowingEnvironment позволяет узнать о среде, в которой представлено приложение, чтобы вы могли адаптировать приложение по мере необходимости. В нём описывается тип окна, поддерживаемый средой; например, Overlapped если приложение запущено на компьютере, или Tiled если приложение запущено на Xbox. Он также предоставляет набор объектов DisplayRegion, описывающих области, в которых приложение может отображаться на логическом дисплее.
Область отображения
API DisplayRegion описывает регион, в котором представление может отображаться пользователю на логическом экране; Например, на компьютере с настольным компьютером это полный дисплей минус область панели задач. Это не обязательно совпадает на 1:1 с физической областью отображения основного монитора. В одном мониторе может быть несколько регионов отображения, или регион отображения может быть настроен на несколько мониторов, если эти мониторы однородны во всех аспектах.
AppWindowPresenter
API AppWindowPresenter позволяет легко переключать окна в предварительно определенные конфигурации, например FullScreen или CompactOverlay. Эти конфигурации предоставляют пользователю согласованный интерфейс на любом устройстве, поддерживающем конфигурацию.
Контекст пользовательского интерфейса
UIContext — это уникальный идентификатор окна приложения или представления. Он создается автоматически, и вы можете использовать свойство UIElement.UIContext для получения UIContext. Каждый UIElement в дереве XAML имеет один и тот же UIContext.
UIContext важен, так как api, такие как Window.Current и шаблон GetForCurrentView, полагаются на наличие одного дерева ApplicationView/CoreWindow с одним деревом XAML для работы с потоком. Это не так, если вы используете AppWindow, поэтому вместо этого используйте UIContext для идентификации определенного окна.
XamlRoot
Класс XamlRoot содержит дерево элементов XAML, подключает его к объекту узла окна (например, AppWindow или ApplicationView), а также предоставляет такие сведения, как размер и видимость. Объект XamlRoot не создается напрямую. Вместо этого он создается при присоединении элемента XAML к AppWindow. Затем можно использовать свойство UIElement.XamlRoot для получения XamlRoot.
Дополнительные сведения о UIContext и XamlRoot см. в статье "Создание кода переносимого между узлами окон".
Отображение нового окна
Давайте рассмотрим шаги для отображения содержимого в новом AppWindow.
Чтобы показать новое окно
Вызовите статический метод AppWindow.TryCreateAsync , чтобы создать приложение AppWindow.
AppWindow appWindow = await AppWindow.TryCreateAsync();Создайте содержимое окна.
Как правило, вы создаете XAML фрейм, а затем осуществляете его навигацию к странице XAML , где определено содержимое вашего приложения. Дополнительные сведения о кадрах и страницах см. в разделе одноранговой навигации между двумя страницами.
Frame appWindowContentFrame = new Frame(); appWindowContentFrame.Navigate(typeof(AppWindowMainPage));Однако вы можете отобразить любое содержимое XAML в AppWindow, а не только рамку и страницу. Например, можно отобразить только один элемент управления, например ColorPicker, или показать SwapChainPanel, который размещает содержимое DirectX.
Вызовите метод ElementCompositionPreview.SetAppWindowContent , чтобы подключить содержимое XAML к AppWindow.
ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);Вызов этого метода создает объект XamlRoot и задает его как свойство XamlRoot для указанного UIElement.
Этот метод можно вызывать только один раз на экземпляр AppWindow. После установки содержимого дальнейшие вызовы SetAppWindowContent для этого экземпляра AppWindow завершаются ошибкой. Кроме того, если вы пытаетесь отключить содержимое AppWindow, передав объект NULL UIElement, вызов завершится ошибкой.
Вызовите метод AppWindow.TryShowAsync , чтобы отобразить новое окно.
await appWindow.TryShowAsync();
Выпуск ресурсов при закрытии окна
Всегда следует обрабатывать событие AppWindow.Closed для освобождения ресурсов XAML (содержимого AppWindow) и ссылок на него.
appWindow.Closed += delegate
{
appWindowContentFrame.Content = null;
appWindow = null;
};
Подсказка
Чтобы избежать непредвиденных проблем, необходимо свести объем кода в обработчике событий Closed к минимуму.
Отслеживать экземпляры AppWindow
В зависимости от того, как вы используете несколько окон в приложении, вы можете или не должны отслеживать создаваемые экземпляры AppWindow. В примере HelloAppWindow демонстрируются некоторые различные способы, которыми вы обычно можете использовать AppWindow. Здесь мы рассмотрим, почему эти окна должны отслеживаться, и как это сделать.
Простое отслеживание
Окно выбора цветов размещает один элемент управления XAML, а код для взаимодействия с средство выбора цвета находится в файле MainPage.xaml.cs. Окно выбора цвета позволяет только один экземпляр и, по сути, является расширением MainWindow. Чтобы обеспечить создание только одного экземпляра, окно выбора цветов отслеживается с помощью переменной уровня страницы. Перед созданием нового окна выбора цвета проверьте, существует ли экземпляр окна, и если он существует, пропустите шаги по созданию нового окна и просто вызовите TryShowAsync для существующего окна.
AppWindow colorPickerAppWindow;
// ...
private async void DetachColorPickerButton_Click(object sender, RoutedEventArgs e)
{
// Create the color picker window.
if (colorPickerAppWindow == null)
{
// ...
// Create a new window
colorPickerAppWindow = await AppWindow.TryCreateAsync();
// ...
}
// Show the window.
await colorPickerAppWindow.TryShowAsync();
}
Отслеживание экземпляра AppWindow в его размещенном содержимом
В AppWindowPage окне размещена полная страница XAML, а код для взаимодействия со страницей находится в AppWindowPage.xaml.cs. Он позволяет использовать несколько экземпляров, каждый из которых работает независимо.
Функциональные возможности страницы позволяют управлять окном, устанавливая его на FullScreen или CompactOverlay, а также прослушивать события AppWindow.Changed для отображения сведений об окне. Чтобы вызвать эти API, AppWindowPage требуется ссылка на экземпляр AppWindow, на котором он размещен.
Если это все необходимо, вы можете создать свойство в AppWindowPage и назначить экземпляру AppWindow при его создании.
AppWindowPage.xaml.cs
В AppWindowPage создайте свойство для хранения ссылки на AppWindow.
public sealed partial class AppWindowPage : Page
{
public AppWindow MyAppWindow { get; set; }
// ...
}
MainPage.xaml.cs
Получите в MainPageссылку на экземпляр страницы и назначьте новосозданный AppWindow свойству в AppWindowPage.
private async void ShowNewWindowButton_Click(object sender, RoutedEventArgs e)
{
// Create a new window.
AppWindow appWindow = await AppWindow.TryCreateAsync();
// Create a Frame and navigate to the Page you want to show in the new window.
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(AppWindowPage));
// Get a reference to the page instance and assign the
// newly created AppWindow to the MyAppWindow property.
AppWindowPage page = (AppWindowPage)appWindowContentFrame.Content;
page.MyAppWindow = appWindow;
// ...
}
Отслеживание окон приложений с помощью UIContext
Вы также можете получить доступ к экземплярам AppWindow из других частей вашего приложения. Например, может быть кнопка "закрыть все", MainPage которая закрывает все отслеживаемые экземпляры AppWindow.
В этом случае следует использовать UIContext уникальный идентификатор для отслеживания экземпляров окна в словаре.
MainPage.xaml.cs
Создайте MainPageсловарь как статическое свойство. Затем добавьте страницу в словарь при его создании и удалите ее при закрытии страницы. После вызова ElementCompositionPreview.SetAppWindowContentможно получить UIContext из содержимого appWindowContentFrame.UIContext ().
public sealed partial class MainPage : Page
{
// Track open app windows in a Dictionary.
public static Dictionary<UIContext, AppWindow> AppWindows { get; set; }
= new Dictionary<UIContext, AppWindow>();
// ...
private async void ShowNewWindowButton_Click(object sender, RoutedEventArgs e)
{
// Create a new window.
AppWindow appWindow = await AppWindow.TryCreateAsync();
// Create a Frame and navigate to the Page you want to show in the new window.
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(AppWindowPage));
// Attach the XAML content to the window.
ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
// Add the new page to the Dictionary using the UIContext as the Key.
AppWindows.Add(appWindowContentFrame.UIContext, appWindow);
appWindow.Title = "App Window " + AppWindows.Count.ToString();
// When the window is closed, be sure to release
// XAML resources and the reference to the window.
appWindow.Closed += delegate
{
MainPage.AppWindows.Remove(appWindowContentFrame.UIContext);
appWindowContentFrame.Content = null;
appWindow = null;
};
// Show the window.
await appWindow.TryShowAsync();
}
private async void CloseAllButton_Click(object sender, RoutedEventArgs e)
{
while (AppWindows.Count > 0)
{
await AppWindows.Values.First().CloseAsync();
}
}
// ...
}
AppWindowPage.xaml.cs
Чтобы использовать экземпляр AppWindow в коде AppWindowPage, используйте UIContext страницы, чтобы извлечь его из статического словаря в MainPage. Это следует сделать в обработчике события загружено страницы, а не в конструкторе, чтобы UIContext не был null. Вы можете получить контекст пользовательского интерфейса (UIContext) из страницы: this.UIContext.
public sealed partial class AppWindowPage : Page
{
AppWindow window;
// ...
public AppWindowPage()
{
this.InitializeComponent();
Loaded += AppWindowPage_Loaded;
}
private void AppWindowPage_Loaded(object sender, RoutedEventArgs e)
{
// Get the reference to this AppWindow that was stored when it was created.
window = MainPage.AppWindows[this.UIContext];
// Set up event handlers for the window.
window.Changed += Window_Changed;
}
// ...
}
Замечание
В HelloAppWindow примере показаны оба способа отслеживания окна AppWindowPage, но обычно используется один или другой, а не оба.
Размер окна запроса и размещение
Класс AppWindow имеет несколько методов, которые можно использовать для управления размером и размещением окна. Как подразумевалось именами методов, система может или не учитывать запрошенные изменения в зависимости от факторов окружающей среды.
Вызов RequestSize , чтобы указать требуемый размер окна, как показано ниже.
colorPickerAppWindow.RequestSize(new Size(300, 428));
Методы управления размещением окон называются RequestMove*: RequestMoveAdjacentToCurrentView, RequestMoveAdjacentToWindow, RequestMoveRelativeToDisplayRegion, RequestMoveToDisplayRegion.
В этом примере этот код перемещает окно рядом с основным представлением, из которому создается окно.
colorPickerAppWindow.RequestMoveAdjacentToCurrentView();
Чтобы получить сведения о текущем размере и размещении окна, вызовите GetPlacement. Возвращает объект AppWindowPlacement, который предоставляет текущий DisplayRegion, Offsetи Size окна.
Например, можно вызвать этот код, чтобы переместить окно в правый верхний угол дисплея. Этот код должен вызываться после отображения окна; В противном случае размер окна, возвращаемый вызовом GetPlacement, будет 0,0, а смещение будет неверным.
DisplayRegion displayRegion = window.GetPlacement().DisplayRegion;
double displayRegionWidth = displayRegion.WorkAreaSize.Width;
double windowWidth = window.GetPlacement().Size.Width;
int horizontalOffset = (int)(displayRegionWidth - windowWidth);
window.RequestMoveRelativeToDisplayRegion(displayRegion, new Point(horizontalOffset, 0));
Запрос конфигурации презентации
Класс AppWindowPresenter позволяет отображать AppWindow с помощью предварительно определенной конфигурации, подходящей для устройства, на котором он показан. Значение AppWindowPresentationConfiguration можно использовать для размещения окна в режиме FullScreen или CompactOverlay.
В этом примере показано, как выполнить следующие действия:
- Используйте событие AppWindow.Change , чтобы получать уведомления, если доступные презентации окна изменяются.
- Используйте свойство AppWindow.Presenter, чтобы получить текущий AppWindowPresenter.
- Вызовите IsPresentationSupported, чтобы узнать, поддерживается ли конкретный тип представления окна приложения AppWindowPresentationKind.
- Вызовите GetConfiguration, чтобы проверить, какой тип конфигурации используется в данный момент.
- Вызов RequestPresentation для изменения текущей конфигурации.
private void Window_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidAvailableWindowPresentationsChange)
{
EnablePresentationButtons(sender);
}
if (args.DidWindowPresentationChange)
{
ConfigText.Text = window.Presenter.GetConfiguration().Kind.ToString();
}
if (args.DidSizeChange)
{
SizeText.Text = window.GetPlacement().Size.ToString();
}
}
private void EnablePresentationButtons(AppWindow window)
{
// Check whether the current AppWindowPresenter supports CompactOverlay.
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.CompactOverlay))
{
// Show the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Visible;
}
else
{
// Hide the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Collapsed;
}
// Check whether the current AppWindowPresenter supports FullScreen?
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.FullScreen))
{
// Show the FullScreen button...
fullScreenButton.Visibility = Visibility.Visible;
}
else
{
// Hide the FullScreen button...
fullScreenButton.Visibility = Visibility.Collapsed;
}
}
private void CompactOverlayButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.CompactOverlay)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.CompactOverlay);
fullScreenButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.FullScreen)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.FullScreen);
compactOverlayButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
Повторное использование элементов XAML
AppWindow позволяет иметь несколько деревьев XAML с одинаковым потоком пользовательского интерфейса. Однако элемент XAML можно добавить только в дерево XAML один раз. Если вы хотите переместить часть пользовательского интерфейса из одного окна в другое, необходимо управлять его размещением в дереве XAML.
В этом примере показано, как повторно использовать элемент управления ColorPicker при перемещении его между основным и дополнительным окнами.
Средство выбора цветов объявляется в XAML для MainPage, что помещает его в XAML-дерево MainPage.
<StackPanel x:Name="colorPickerContainer" Grid.Column="1" Background="WhiteSmoke">
<Button Click="DetachColorPickerButton_Click" HorizontalAlignment="Right">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" />
</Button>
<ColorPicker x:Name="colorPicker" Margin="12" Width="288"
IsColorChannelTextInputVisible="False"
ColorChanged="ColorPicker_ColorChanged"/>
</StackPanel>
Когда средство выбора цветов отсоединяется для размещения в новом AppWindow, сначала необходимо удалить его из дерева XAML MainPage, удалив его из родительского контейнера. Хотя это не обязательно, этот пример также скрывает родительский контейнер.
colorPickerContainer.Children.Remove(colorPicker);
colorPickerContainer.Visibility = Visibility.Collapsed;
Затем его можно добавить в новое дерево XAML. Здесь вы сначала создадите элемент Grid, который будет родительским контейнером для ColorPicker, и добавьте ColorPicker в качестве дочернего элемента Grid. (Это позволяет легко удалить ColorPicker из этого дерева XAML позже.) Затем сетку можно задать в качестве корневого элемента дерева XAML в новом окне.
Grid appWindowRootGrid = new Grid();
appWindowRootGrid.Children.Add(colorPicker);
// Create a new window
colorPickerAppWindow = await AppWindow.TryCreateAsync();
// Attach the XAML content to our window
ElementCompositionPreview.SetAppWindowContent(colorPickerAppWindow, appWindowRootGrid);
Когда AppWindow закрыт, вы обратите процесс. Сначала удалите ColorPicker из Сетка, а затем добавьте его в качестве дочернего элемента StackPanel в MainPage.
// When the window is closed, be sure to release XAML resources
// and the reference to the window.
colorPickerAppWindow.Closed += delegate
{
appWindowRootGrid.Children.Remove(colorPicker);
appWindowRootGrid = null;
colorPickerAppWindow = null;
colorPickerContainer.Children.Add(colorPicker);
colorPickerContainer.Visibility = Visibility.Visible;
};
private async void DetachColorPickerButton_Click(object sender, RoutedEventArgs e)
{
ColorPickerContainer.Visibility = Visibility.Collapsed;
// Create the color picker window.
if (colorPickerAppWindow == null)
{
ColorPickerContainer.Children.Remove(colorPicker);
Grid appWindowRootGrid = new Grid();
appWindowRootGrid.Children.Add(colorPicker);
// Create a new window
colorPickerAppWindow = await AppWindow.TryCreateAsync();
colorPickerAppWindow.RequestMoveAdjacentToCurrentView();
colorPickerAppWindow.RequestSize(new Size(300, 428));
colorPickerAppWindow.Title = "Color picker";
// Attach the XAML content to our window
ElementCompositionPreview.SetAppWindowContent(colorPickerAppWindow, appWindowRootGrid);
// When the window is closed, be sure to release XAML resources
// and the reference to the window.
colorPickerAppWindow.Closed += delegate
{
appWindowRootGrid.Children.Remove(colorPicker);
appWindowRootGrid = null;
colorPickerAppWindow = null;
ColorPickerContainer.Children.Add(colorPicker);
ColorPickerContainer.Visibility = Visibility.Visible;
};
}
// Show the window.
await colorPickerAppWindow.TryShowAsync();
}
Отображение диалогового окна
По умолчанию диалоговые окна содержимого модально связаны с корневым объектом ApplicationView. При использовании ContentDialog внутри AppWindowнеобходимо вручную устанавливать XamlRoot диалога в корень узла XAML-хоста.
Для этого задайте для свойства ContentDialog XamlRoot тот же XamlRoot, что и элемент уже находящийся в AppWindow. Здесь этот код находится внутри обработчика событий кнопки Click, поэтому вы можете использовать sender (нажатую кнопку) для получения XamlRoot.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
Если в дополнение к главному окну (ApplicationView) открыто одно или несколько окон AppWindows, каждое окно может попытаться открыть диалоговое окно, так как модальное диалоговое окно блокирует только то окно, в котором оно открыто. Однако одновременно в каждом потоке может быть открыт только один ContentDialog. Попытка открыть два диалоговых окна ContentDialog породит исключение, даже если они пытаются открыться в отдельных окнах AppWindow.
Чтобы управлять этим, необходимо по крайней мере открыть диалоговое окно в блоке try/catch, чтобы поймать исключение в случае, если еще одно диалоговое окно уже открыто.
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
Другим способом управления диалогами является отслеживание открытого диалогового окна и его закрытие перед попыткой открыть новое диалоговое окно. Для этой цели вы создаете статическое свойство в MainPage под названием CurrentDialog.
public sealed partial class MainPage : Page
{
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
// ...
}
Затем вы проверяете, существует ли в настоящее время открытое диалоговое окно и если есть, вызовите метод Hide , чтобы закрыть его. Наконец, назначьте новое диалоговое окно CurrentDialogи попробуйте отобразить его.
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog simpleDialog = new ContentDialog
{
Title = "Content dialog",
Content = "Dialog box for " + window.Title,
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
MainPage.CurrentDialog = simpleDialog;
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already
// present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
Если не желательно, чтобы диалоговое окно закрыто программным способом, не назначайте его как .CurrentDialog Здесь MainPage отображается важное диалоговое окно, которое следует закрыть только при нажатии Ok. Поскольку он не назначен в качестве CurrentDialog, попытка закрыть её программным способом не предпринимается.
public sealed partial class MainPage : Page
{
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
// ...
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog importantDialog = new ContentDialog
{
Title = "Important dialog",
Content = "This dialog can only be dismissed by clicking Ok.",
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
// Do not track this dialog as the MainPage.CurrentDialog.
// It should only be closed by clicking the Ok button.
MainPage.CurrentDialog = null;
try
{
ContentDialogResult result = await importantDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
// ...
}
Полный код
MainPage.xaml
<Page
x:Class="HelloAppWindow.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HelloAppWindow"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button x:Name="NewWindowButton" Content="Open new window"
Click="ShowNewWindowButton_Click" Margin="0,12"/>
<Button Content="Open dialog" Click="DialogButton_Click"
HorizontalAlignment="Stretch"/>
<Button Content="Close all" Click="CloseAllButton_Click"
Margin="0,12" HorizontalAlignment="Stretch"/>
</StackPanel>
<StackPanel x:Name="colorPickerContainer" Grid.Column="1" Background="WhiteSmoke">
<Button Click="DetachColorPickerButton_Click" HorizontalAlignment="Right">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" />
</Button>
<ColorPicker x:Name="colorPicker" Margin="12" Width="288"
IsColorChannelTextInputVisible="False"
ColorChanged="ColorPicker_ColorChanged"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace HelloAppWindow
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
AppWindow colorPickerAppWindow;
// Track open app windows in a Dictionary.
public static Dictionary<UIContext, AppWindow> AppWindows { get; set; }
= new Dictionary<UIContext, AppWindow>();
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
public MainPage()
{
this.InitializeComponent();
}
private async void ShowNewWindowButton_Click(object sender, RoutedEventArgs e)
{
// Create a new window.
AppWindow appWindow = await AppWindow.TryCreateAsync();
// Create a Frame and navigate to the Page you want to show in the new window.
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(AppWindowPage));
// Get a reference to the page instance and assign the
// newly created AppWindow to the MyAppWindow property.
AppWindowPage page = (AppWindowPage)appWindowContentFrame.Content;
page.MyAppWindow = appWindow;
page.TextColorBrush = new SolidColorBrush(colorPicker.Color);
// Attach the XAML content to the window.
ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
// Add the new page to the Dictionary using the UIContext as the Key.
AppWindows.Add(appWindowContentFrame.UIContext, appWindow);
appWindow.Title = "App Window " + AppWindows.Count.ToString();
// When the window is closed, be sure to release XAML resources
// and the reference to the window.
appWindow.Closed += delegate
{
MainPage.AppWindows.Remove(appWindowContentFrame.UIContext);
appWindowContentFrame.Content = null;
appWindow = null;
};
// Show the window.
await appWindow.TryShowAsync();
}
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog importantDialog = new ContentDialog
{
Title = "Important dialog",
Content = "This dialog can only be dismissed by clicking Ok.",
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
// Do not track this dialog as the MainPage.CurrentDialog.
// It should only be closed by clicking the Ok button.
MainPage.CurrentDialog = null;
try
{
ContentDialogResult result = await importantDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
private async void DetachColorPickerButton_Click(object sender, RoutedEventArgs e)
{
// Create the color picker window.
if (colorPickerAppWindow == null)
{
colorPickerContainer.Children.Remove(colorPicker);
colorPickerContainer.Visibility = Visibility.Collapsed;
Grid appWindowRootGrid = new Grid();
appWindowRootGrid.Children.Add(colorPicker);
// Create a new window
colorPickerAppWindow = await AppWindow.TryCreateAsync();
colorPickerAppWindow.RequestMoveAdjacentToCurrentView();
colorPickerAppWindow.RequestSize(new Size(300, 428));
colorPickerAppWindow.Title = "Color picker";
// Attach the XAML content to our window
ElementCompositionPreview.SetAppWindowContent(colorPickerAppWindow, appWindowRootGrid);
// Make sure to release the reference to this window,
// and release XAML resources, when it's closed
colorPickerAppWindow.Closed += delegate
{
appWindowRootGrid.Children.Remove(colorPicker);
appWindowRootGrid = null;
colorPickerAppWindow = null;
colorPickerContainer.Children.Add(colorPicker);
colorPickerContainer.Visibility = Visibility.Visible;
};
}
// Show the window.
await colorPickerAppWindow.TryShowAsync();
}
private void ColorPicker_ColorChanged(ColorPicker sender, ColorChangedEventArgs args)
{
NewWindowButton.Background = new SolidColorBrush(args.NewColor);
}
private async void CloseAllButton_Click(object sender, RoutedEventArgs e)
{
while (AppWindows.Count > 0)
{
await AppWindows.Values.First().CloseAsync();
}
}
}
}
AppWindowPage.xaml
<Page
x:Class="HelloAppWindow.AppWindowPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HelloAppWindow"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TextBlock x:Name="TitleTextBlock" Text="Hello AppWindow!" FontSize="24" HorizontalAlignment="Center" Margin="24"/>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button Content="Open dialog" Click="DialogButton_Click"
Width="200" Margin="0,4"/>
<Button Content="Move window" Click="MoveWindowButton_Click"
Width="200" Margin="0,4"/>
<ToggleButton Content="Compact Overlay" x:Name="compactOverlayButton" Click="CompactOverlayButton_Click"
Width="200" Margin="0,4"/>
<ToggleButton Content="Full Screen" x:Name="fullScreenButton" Click="FullScreenButton_Click"
Width="200" Margin="0,4"/>
<Grid>
<TextBlock Text="Size:"/>
<TextBlock x:Name="SizeText" HorizontalAlignment="Right"/>
</Grid>
<Grid>
<TextBlock Text="Presentation:"/>
<TextBlock x:Name="ConfigText" HorizontalAlignment="Right"/>
</Grid>
</StackPanel>
</Grid>
</Page>
AppWindowPage.xaml.cs
using System;
using Windows.Foundation;
using Windows.Foundation.Metadata;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace HelloAppWindow
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class AppWindowPage : Page
{
AppWindow window;
public AppWindow MyAppWindow { get; set; }
public SolidColorBrush TextColorBrush { get; set; } = new SolidColorBrush(Colors.Black);
public AppWindowPage()
{
this.InitializeComponent();
Loaded += AppWindowPage_Loaded;
}
private void AppWindowPage_Loaded(object sender, RoutedEventArgs e)
{
// Get the reference to this AppWindow that was stored when it was created.
window = MainPage.AppWindows[this.UIContext];
// Set up event handlers for the window.
window.Changed += Window_Changed;
TitleTextBlock.Foreground = TextColorBrush;
}
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog simpleDialog = new ContentDialog
{
Title = "Content dialog",
Content = "Dialog box for " + window.Title,
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
MainPage.CurrentDialog = simpleDialog;
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already
// present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
private void Window_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidAvailableWindowPresentationsChange)
{
EnablePresentationButtons(sender);
}
if (args.DidWindowPresentationChange)
{
ConfigText.Text = window.Presenter.GetConfiguration().Kind.ToString();
}
if (args.DidSizeChange)
{
SizeText.Text = window.GetPlacement().Size.ToString();
}
}
private void EnablePresentationButtons(AppWindow window)
{
// Check whether the current AppWindowPresenter supports CompactOverlay.
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.CompactOverlay))
{
// Show the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Visible;
}
else
{
// Hide the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Collapsed;
}
// Check whether the current AppWindowPresenter supports FullScreen?
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.FullScreen))
{
// Show the FullScreen button...
fullScreenButton.Visibility = Visibility.Visible;
}
else
{
// Hide the FullScreen button...
fullScreenButton.Visibility = Visibility.Collapsed;
}
}
private void CompactOverlayButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.CompactOverlay)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.CompactOverlay);
fullScreenButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.FullScreen)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.FullScreen);
compactOverlayButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void MoveWindowButton_Click(object sender, RoutedEventArgs e)
{
DisplayRegion displayRegion = window.GetPlacement().DisplayRegion;
double displayRegionWidth = displayRegion.WorkAreaSize.Width;
double windowWidth = window.GetPlacement().Size.Width;
int horizontalOffset = (int)(displayRegionWidth - windowWidth);
window.RequestMoveRelativeToDisplayRegion(displayRegion, new Point(horizontalOffset, 0));
}
}
}