使用 AppWindow 显示多个视图

AppWindow 及其相关 API 可让你在辅助窗口中显示应用内容,同时在每个窗口中仍处理同一 UI 线程,从而简化多窗口应用的创建过程。

注释

AppWindow 目前为预览版。 这意味着可以将使用 AppWindow 的应用提交到应用商店,但已知某些平台和框架组件不适用于 AppWindow(请参阅 限制)。

在这里,我们演示了多个窗口的一些方案,其中调用了一个示例应用 HelloAppWindow。 示例应用演示了以下功能:

  • 将控件从主页解锁并在新窗口中打开。
  • 在新窗口中打开页面的新实例。
  • 以编程方式调整新窗口在应用中的大小和位置。
  • 将 ContentDialog 与应用中的相应窗口相关联。

示例应用使用单个窗口

示例应用使用单个窗口

示例应用程序,带有未停靠的颜色选取器和辅助窗口

带有未停靠颜色选取器和辅助窗口的示例应用

重要 APIWindows.UI.WindowManagement 命名空间AppWindow 类

API 概述

从 Windows 10 版本 1903(SDK 18362)开始,WindowsManagement 命名空间中的 AppWindow 类和其他 API 可用。 如果应用面向早期版本的 Windows 10,则必须 使用 ApplicationView 创建次要窗口。 WindowManagement API 仍在开发中,并具有 限制,正如 API 参考文档中所述。

下面是用于在 AppWindow 中显示内容的一些重要 API。

应用窗口

AppWindow 类可用于在辅助窗口中显示 UWP 应用的一部分。 它的概念类似于 ApplicationView,但在行为和生存期内并不相同。 AppWindow 的主要功能是,每个实例共享相同的 UI 处理线程(包括创建它们的事件调度程序),这简化了多窗口应用。

您只能将 XAML 内容附加到 AppWindow,不支持原生 DirectX 或全息内容。 但是,可以显示托管 DirectX 内容的 XAML SwapChainPanel

窗口环境

WindowingEnvironment API 可让你了解正在呈现应用的环境,以便根据需要调整应用。 它描述了环境支持的窗口类型;例如,Overlapped 表示应用在电脑上运行,而 Tiled 表示应用在 Xbox 上运行。 它还提供一组 DisplayRegion 对象,用于描述应用可能在逻辑显示器上显示的区域。

显示区域

DisplayRegion API 描述可以在逻辑显示器上向用户显示视图的区域;例如,在台式电脑上,这是完整显示减去任务栏区域。 它不一定与背面监视器的物理显示区域完全一致。 在同一监视器中可以有多个显示区域,或者,如果这些监视器在所有方面都是同质的,则可以将 DisplayRegion 配置为跨多个监视器。

AppWindowPresenter

AppWindowPresenter API 使你可以轻松地将窗口切换到预定义的配置,例如FullScreenCompactOverlay。 这些配置使用户能够跨支持配置的任何设备获得一致的体验。

用户界面上下文

UIContext 是应用窗口或视图的唯一标识符。 它会自动创建,可以使用 UIElement.UIContext 属性来检索 UIContext。 XAML 树中的每个 UIElement 具有相同的 UIContext。

UIContext 很重要,因为像 Window.Current 这样的 API 和 GetForCurrentView 模式都依赖于每个线程拥有一个 XAML 树的单个 ApplicationView/CoreWindow 以便正常工作。 使用 AppWindow 时,情况并非如此,因此改用 UIContext 来标识特定窗口。

XamlRoot

XamlRoot 类保存 XAML 元素树,将其连接到窗口宿主对象(例如 AppWindowApplicationView),并提供大小和可见性等信息。 不要直接创建 XamlRoot 对象。 而是在将 XAML 元素附加到 AppWindow 时创建一个。 然后,可以使用 UIElement.XamlRoot 属性检索 XamlRoot。

有关 UIContext 和 XamlRoot 的详细信息,请参阅 使代码在不同窗口主机间可移植

显示新窗口

让我们看看在新的 AppWindow 中显示内容的步骤。

打开新窗口

  1. 调用静态 AppWindow.TryCreateAsync 方法以创建新的 AppWindow

    AppWindow appWindow = await AppWindow.TryCreateAsync();
    
  2. 创建窗口内容。

    通常,你会创建一个 XAML Frame,然后将该 Frame 导航到一个 XAML Page,在那里你已定义了应用内容。 有关框架和页面的更多信息,请参阅 两个页面之间的点对点导航

    Frame appWindowContentFrame = new Frame();
    appWindowContentFrame.Navigate(typeof(AppWindowMainPage));
    

    但是,可以在 AppWindow 中显示任何 XAML 内容,而不仅仅是框架和页面。 例如,可以仅显示单个控件(如 ColorPicker),也可以显示托管 DirectX 内容的 SwapChainPanel

  3. 调用 ElementCompositionPreview.SetAppWindowContent 方法,将 XAML 内容附加到 AppWindow。

    ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
    

    对此方法的调用将创建一个 XamlRoot 对象,并将其设置为指定 UIElement 的 XamlRoot 属性。

    每个 AppWindow 实例只能调用此方法一次。 设置内容后,对此 AppWindow 实例的 SetAppWindowContent 的进一步调用将失败。 此外,如果尝试通过传入 null UIElement 对象来断开 AppWindow 内容的连接,调用将失败。

  4. 调用 AppWindow.TryShowAsync 方法以显示新窗口。

    await appWindow.TryShowAsync();
    

关闭窗口时释放资源

应始终处理 AppWindow.Closed 事件以释放 XAML 资源(AppWindow 内容)和对 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内。 它允许多个实例,每个实例独立运行。

页面的功能使你能够操控窗口,将其设置为 FullScreenCompactOverlay,并侦听 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 中,将 Dictionary 创建为静态属性。 然后,在创建页面时将页面添加到字典,并在页面关闭时将其删除。 调用 ElementCompositionPreview.SetAppWindowContent后,可以从 内容 Frame)中获取 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

若要在代码中使用 AppWindowPageAppWindow 实例,请使用页面的 UIContext 从静态字典中MainPage检索它。 应在页面的 加载 事件处理程序中执行此操作,而不是在构造函数中执行,以确保 UIContext 不为空。 您可以从this.UIContext页面获取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*RequestMoveAdjacentToCurrentViewRequestMoveAdjacentToWindowRequestMoveRelativeToDisplayRegionRequestMoveToDisplayRegion

在此示例中,此代码将窗口移动到从中生成窗口的主视图旁边。

colorPickerAppWindow.RequestMoveAdjacentToCurrentView();

若要获取有关窗口的当前大小和位置的信息,请调用 GetPlacement。 这会返回一个 AppWindowPlacement 对象,该对象提供窗口的当前 DisplayRegionOffsetSize

例如,可以调用此代码将窗口移动到显示右上角。 必须在显示窗口后调用此代码;否则,调用 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 模式。

此示例演示如何执行以下作:

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 允许你拥有多个具有相同 UI 线程的 XAML 树。 但是,XAML 元素只能添加到 XAML 树一次。 如果要将 UI 的一部分从一个窗口移动到另一个窗口,则必须在 XAML 树中管理它的位置。

此示例演示如何在主窗口和辅助窗口之间移动并重用 ColorPicker 控件。

颜色选取器在为 MainPage准备的 XAML 中声明,它将被放置在 MainPage 的 XAML 树中。

<StackPanel x:Name="colorPickerContainer" Grid.Column="1" Background="WhiteSmoke">
    <Button Click="DetachColorPickerButton_Click" HorizontalAlignment="Right">
        <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE2B4;" />
    </Button>
    <ColorPicker x:Name="colorPicker" Margin="12" Width="288"
                 IsColorChannelTextInputVisible="False"
                 ColorChanged="ColorPicker_ColorChanged"/>
</StackPanel>

将颜色选取器分离到要放置在新的 AppWindow 中时,首先必须将其从 MainPage XAML 树中删除,方法是将其从其父容器中删除。 虽然不需要,但此示例还隐藏父容器。

colorPickerContainer.Children.Remove(colorPicker);
colorPickerContainer.Visibility = Visibility.Collapsed;

然后,可以将其添加到新的 XAML 树。 在这里,首先创建一个 Grid,作为 ColorPicker 的父容器,然后将 ColorPicker 添加为 Grid 的子级。 (这样以后可以轻松地从此 XAML 树中删除 ColorPicker。然后,在新窗口中将网格设置为 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 时,将反转此过程。 首先,从 网格中删除 颜色选择器,然后将其添加为 堆栈面板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以模态方式显示。 在 AppWindow 中使用 ContentDialog 时,需要手动将对话框上的 XamlRoot 设置为 XAML 主机的根目录。

为此,请将 ContentDialog 的 XamlRoot 属性设置为与 AppWindow 中已有的元素相同的 XamlRoot 。 此处,此代码位于按钮的 Click 事件处理程序内,因此可以使用 发送方(单击的按钮)获取 XamlRoot。

if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
    simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}

如果除主窗口(ApplicationView)外还打开了一个或多个应用窗口,那么每个窗口都可以尝试打开一个对话框,因为模式对话框将仅阻止其所属的窗口。 每个线程同时只能打开一个 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="&#xE2B4;" />
    </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));
        }
    }
}