Afficher plusieurs vues avec AppWindow

AppWindow et les API associées simplifient la création d’applications à plusieurs fenêtres. En effet, elles vous permettent d’afficher le contenu de votre application dans des fenêtres secondaires, tout en continuant à travailler sur le même thread d’interface utilisateur dans chaque fenêtre.

Remarque

AppWindow est actuellement disponible en préversion. Dès lors, vous pouvez soumettre des applications utilisant AppWindow sur le Microsoft Store, mais certains composants de plateforme et d'infrastructure peuvent ne pas fonctionner avec AppWindow (voir Limitations).

Ici, nous abordons certains scénarios impliquant l’affichage de plusieurs fenêtres avec un exemple d’application appelé HelloAppWindow. Il illustre les fonctionnalités suivantes :

  • Détacher un contrôle de la page principale et l’ouvrir dans une nouvelle fenêtre
  • Ouvrir de nouvelles instances d’une page dans de nouvelles fenêtres
  • Dimensionner et placer de nouvelles fenêtres dans l’application par programmation
  • Associer une ContentDialog à la fenêtre appropriée dans l’application

Sample app with a single window

Exemple d’application avec une seule fenêtre

Sample app with un-docked color picker and secondary window

Exemple d’application avec un sélecteur de couleurs détaché et une fenêtre secondaire

API importantes : Espace de noms Windows.UI.WindowManagement, Classe AppWindow

Présentation de l’API

La classe AppWindow et les autres API de l’espace de noms WindowManagement sont disponibles à partir de Windows 10, version 1903 (SDK 18362). Si votre application cible des versions antérieures de Windows 10, vous devez utiliser ApplicationView pour créer des fenêtres secondaires. Les API WindowManagement sont toujours en cours de développement et présentent des limitations décrites dans les documents de référence sur les API.

Voici certaines API importantes à utiliser pour afficher du contenu dans une AppWindow.

AppWindow

La classe AppWindow peut être utilisée pour afficher une partie d’une application UWP dans une fenêtre secondaire. Elle est similaire à une classe ApplicationView d’un point de vue conceptuel, mais pas en ce qui concerne le comportement et la durée de vie. Avec la classe AppWindow, toutes les instances partagent le thread de traitement de l’interface utilisateur (y compris le répartiteur d’événements) à partir duquel elles ont été créées, ce qui simplifie les applications à plusieurs fenêtres. Il s’agit là de l’une de ses principales caractéristiques.

Vous pouvez connecter uniquement du contenu XAML à votre AppWindow. Le contenu holographique ou DirectX natif n’est pas pris en charge. Toutefois, vous pouvez afficher un SwapChainPanel XAML qui héberge du contenu DirectX.

WindowingEnvironment

L’API WindowingEnvironment vous informe de l’environnement dans lequel votre application est présentée et vous permet ainsi de l’adapter selon les besoins. Elle décrit le type de fenêtre pris en charge par l’environnement (par exemple, Overlapped si l’application est exécutée sur un PC ou Tiled si elle est exécutée sur une Xbox). Elle fournit également un ensemble d’objets DisplayRegion qui décrivent les zones d’un affichage logique dans lesquelles une application peut être présentée.

DisplayRegion

L’API DisplayRegion décrit la zone d’un affichage logique dans laquelle une vue peut être présentée à un utilisateur. Par exemple, sur un PC de bureau, il s’agit de l’écran complet moins la zone de la barre des tâches. Il ne s’agit pas nécessairement d’une correspondance exacte avec la zone d’affichage physique du moniteur utilisé. Plusieurs zones d’affichage peuvent être présentes sur un même moniteur, ou une DisplayRegion peut être configurée pour s’étendre sur plusieurs moniteurs si ceux-ci sont parfaitement homogènes.

AppWindowPresenter

L’API AppWindowPresenter permet de changer facilement le mode d’affichage des fenêtres selon des configurations prédéfinies comme FullScreen ou CompactOverlay. Ces configurations offrent à l’utilisateur une expérience cohérente sur tous les appareils qui prennent en charge la configuration.

UIContext

L’UIContext est un identificateur unique pour une vue ou une fenêtre d’application. Il est créé automatiquement et vous pouvez utiliser la propriété UIElement.UIContext pour le récupérer. Chaque UIElement de l’arborescence XAML a le même UIContext.

L’UIContext est important car les API telles que Window.Current et le modèle GetForCurrentView s’appuient sur l’utilisation d’une ApplicationView/CoreWindow unique avec une seule arborescence XAML par thread à utiliser. Ce n’est pas le cas quand vous utilisez une AppWindow. Vous utilisez alors l’UIContext pour identifier une fenêtre particulière.

XamlRoot

La classe XamlRoot contient une arborescence d’éléments XAML, la connecte à l’objet hôte de fenêtre (par exemple, AppWindow ou ApplicationView) et fournit des informations telles que la taille et la visibilité. Vous ne créez pas d’objet XamlRoot directement. En fait, un objet XamlRoot est créé quand vous attachez un élément XAML à une AppWindow. Vous pouvez ensuite utiliser la propriété UIElement.XamlRoot pour récupérer le XamlRoot.

Pour plus d’informations sur l’UIContext et la classe XamlRoot, consultez Portabilité du code entre les hôtes de fenêtrage.

Afficher une nouvelle fenêtre

Abordons les étapes permettant d’afficher du contenu dans une nouvelle AppWindow.

Pour afficher une nouvelle fenêtre

  1. Appelez la méthode statique AppWindow.TryCreateAsync pour créer une AppWindow.

    AppWindow appWindow = await AppWindow.TryCreateAsync();
    
  2. Créez le contenu de la fenêtre.

    En général, vous créez un frame XAML et vous naviguez dans le frame pour accéder à une page XAML, où vous avez défini le contenu de votre application. Pour plus d’informations sur les frames et les pages, consultez Navigation pair à pair entre deux pages.

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

    Toutefois, vous pouvez afficher tout contenu XAML dans l’AppWindow (pas uniquement un frame et une page). Par exemple, vous pouvez afficher un seul contrôle comme ColorPicker ou un SwapChainPanel qui héberge du contenu DirectX.

  3. Appelez la méthode ElementCompositionPreview.SetAppWindowContent pour attacher le contenu XAML à l’AppWindow.

    ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
    

    L’appel à cette méthode crée un objet XamlRoot et le définit comme propriété XamlRoot de l’UIElement spécifié.

    Vous pouvez appeler cette méthode une fois par instance AppWindow uniquement. Dès lors que le contenu a été défini, les appels suivants à SetAppWindowContent pour cette instance AppWindow échouent. De plus, si vous tentez de déconnecter le contenu AppWindow en passant un objet UIElement null, l’appel échoue.

  4. Appelez la méthode AppWindow.TryShowAsync pour afficher la nouvelle fenêtre.

    await appWindow.TryShowAsync();
    

Libérer des ressources à la fermeture d’une fenêtre

Vous devez toujours gérer l’événement AppWindow.Closed pour libérer les ressources XAML (contenu de l’AppWindow) et les références à l’AppWindow.

appWindow.Closed += delegate
{
    appWindowContentFrame.Content = null;
    appWindow = null;
};

Conseil

Vous devez conserver la quantité de code dans votre gestionnaire d’événements Closed au minimum possible pour éviter des problèmes inattendus.

Suivre les instances d’AppWindow

Selon la façon dont vous utilisez plusieurs fenêtres dans votre application, vous serez peut-être amené à suivre les instances d’AppWindow que vous créez. L’exemple HelloAppWindow illustre plusieurs modes d’utilisation courants d’une AppWindow. Ici, nous allons examiner la raison pour laquelle ces fenêtres doivent être suivies et voir comment procéder.

Suivi simple

La fenêtre du sélecteur de couleurs héberge un seul contrôle XAML, et le code permettant d’interagir avec le sélecteur de couleurs se trouve entièrement dans le fichier MainPage.xaml.cs. La fenêtre du sélecteur de couleurs n’autorise qu’une seule instance et est essentiellement une extension de MainWindow. Pour garantir qu’une seule instance est créée, la fenêtre du sélecteur de couleurs est suivie avec une variable de niveau de page. Avant de créer une fenêtre de sélecteur de couleurs, vous allez vérifier si une instance existe. Si c’est le cas, ignorez les étapes de création de fenêtre et appelez simplement TryShowAsync sur la fenêtre existante.

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();
}

Suivre une instance AppWindow dans son contenu hébergé

La fenêtre AppWindowPage héberge une page XAML complète et le code permettant d’interagir avec la page réside dans AppWindowPage.xaml.cs. Elle autorise plusieurs instances, qui fonctionnent de manière indépendante.

La fonctionnalité de la page vous permet de manipuler la fenêtre en la définissant sur FullScreen ou CompactOverlay. Elle écoute également les événements AppWindow.Changed pour afficher des informations sur la fenêtre. Pour appeler ces API, AppWindowPage a besoin d’une référence à l’instance AppWindow qui l’héberge.

Si c’est tout ce dont vous avez besoin, vous pouvez créer une propriété dans AppWindowPage et lui affecter l’instance AppWindow au moment de sa création.

AppWindowPage.xaml.cs

Dans AppWindowPage, créez une propriété pour contenir la référence AppWindow.

public sealed partial class AppWindowPage : Page
{
    public AppWindow MyAppWindow { get; set; }

    // ...
}

MainPage.xaml.cs

Dans MainPage, récupérez une référence à l’instance de page et affectez la nouvelle AppWindow à la propriété dans 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;

    // ...
}

Suivi des fenêtres d’application avec UIContext

Vous pouvez également avoir besoin d’accéder aux instances AppWindow à partir d’autres parties de votre application. Par exemple, MainPage peut contenir un bouton « Close all » qui ferme toutes les instances d’AppWindow suivies.

Dans ce cas, vous devez utiliser l’identificateur unique UIContext pour suivre les instances de fenêtre dans un dictionnaire (classe Dictionary).

MainPage.xaml.cs

Dans MainPage, créez le dictionnaire comme propriété statique (Dictionary). Ensuite, ajoutez la page au dictionnaire quand vous la créez, puis retirez-la quand elle est fermée. Vous pouvez récupérer l’UIContext à partir du frame de contenu (appWindowContentFrame.UIContext) après avoir appelé ElementCompositionPreview.SetAppWindowContent.

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

Pour utiliser l’instance AppWindow dans votre code AppWindowPage, utilisez l’UIContext de la page pour la récupérer à partir du dictionnaire statique dans MainPage. Vous devez le faire dans le gestionnaire d’événements Loaded de la page plutôt que dans le constructeur pour éviter que l’UIContext soit null. Vous pouvez récupérer l’UIContext à partir de la page : 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;
    }
    // ...
}

Remarque

L’exemple HelloAppWindow illustre les deux façons de suivre la fenêtre dans AppWindowPage, mais vous n’en utiliserez généralement qu’une.

Demander à définir la taille et l’emplacement de la fenêtre

La classe AppWindow offre plusieurs méthodes permettant de contrôler la taille et l’emplacement de la fenêtre. Comme l’indique le nom des méthodes, le système peut ou non appliquer les modifications demandées selon les facteurs environnementaux.

Appelez RequestSize pour spécifier la taille de fenêtre souhaitée comme suit.

colorPickerAppWindow.RequestSize(new Size(300, 428));

Les méthodes pour gérer le placement des fenêtres sont nommées RequestMove * : RequestMoveAdjacentToCurrentView, RequestMoveAdjacentToWindow, RequestMoveRelativeToDisplayRegion, RequestMoveToDisplayRegion.

Dans cet exemple, ce code place la fenêtre en regard de la vue principale dont elle est issue.

colorPickerAppWindow.RequestMoveAdjacentToCurrentView();

Pour obtenir des informations sur la taille et l’emplacement actuels de la fenêtre, appelez GetPlacement. Ceci retourne un objet AppWindowPlacement qui indique la zone d’affichage (DisplayRegion), le décalage (Offset) et la taille (Size) de la fenêtre.

Par exemple, vous pouvez appeler ce code pour déplacer la fenêtre vers l’angle supérieur droit de l’écran. Ce code doit être appelé après l’affichage de la fenêtre. Sinon, la taille de la fenêtre retournée par l’appel à GetPlacement sera 0,0 et le décalage sera incorrect.

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));

Demander une configuration de présentation

La classe AppWindowPresenter vous permet d’afficher une AppWindow selon une configuration prédéfinie adaptée à l’appareil sur lequel elle s’affiche. Vous pouvez utiliser une valeur AppWindowPresentationConfiguration pour placer la fenêtre en mode FullScreen ou CompactOverlay.

Cet exemple montre comment effectuer les opérations suivantes :

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);
    }
}

Réutiliser des éléments XAML

Une AppWindow vous permet d’avoir plusieurs arborescences XAML avec le même thread d’interface utilisateur. Toutefois, un élément XAML peut être ajouté à une arborescence XAML une fois uniquement. Si vous souhaitez déplacer une partie de votre interface utilisateur d’une fenêtre à une autre, vous devez gérer son emplacement dans l’arborescence XAML.

Cet exemple montre comment réutiliser un contrôle ColorPicker tout en le déplaçant entre la fenêtre principale et une fenêtre secondaire.

Le sélecteur de couleurs est déclaré dans le code XAML pour MainPage, ce qui le place dans l’arborescence XAML MainPage.

<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>

Quand le sélecteur de couleurs est détaché pour être placé dans une nouvelle AppWindow, vous devez d’abord le supprimer de l’arborescence XAML MainPage en le retirant de son conteneur parent. Cet exemple masque également le conteneur parent (ce qui n’est pas obligatoire).

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

Vous pouvez ensuite l’ajouter à la nouvelle arborescence XAML. Ici, vous créez d’abord une grille (classe Grid) qui sera le conteneur parent du ColorPicker, puis vous ajoutez ce dernier comme enfant de la grille. (Cela vous permet de supprimer facilement le composant ColorPicker de cette arborescence XAML ultérieurement.) Vous définissez ensuite la grille en tant que racine de l’arborescence XAML dans la nouvelle fenêtre.

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);

Quand l’AppWindow est fermée, vous inversez le processus. Tout d’abord, supprimez le ColorPicker de la grille (Grid), puis ajoutez-le comme enfant du StackPanel dans 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();
}

Afficher une boîte de dialogue

Par défaut, le contenu de boîtes de dialogue s’affiche de manière modale, en fonction de la racine ApplicationView. Quand vous utilisez une ContentDialog dans une AppWindow, vous devez définir manuellement l’objet XamlRoot dans la boîte de dialogue à la racine de l’hôte XAML.

Pour ce faire, définissez la propriété XamlRoot de la ContentDialog sur le même XamlRoot qu’un élément déjà présent dans l’AppWindow. Ici, ce code est dans le gestionnaire d’événements Click d’un bouton. Vous pouvez ainsi utiliser le sender (le bouton sur lequel l’utilisateur a cliqué) pour obtenir le XamlRoot.

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

Si vous avez une ou plusieurs AppWindows ouvertes en plus de la fenêtre principale (ApplicationView), chaque fenêtre peut tenter d’ouvrir une boîte de dialogue, car la boîte de dialogue modale bloque uniquement la fenêtre dont elle est issue. Cependant, une seule ContentDialog peut être ouverte à la fois sur un même thread. Toute tentative d’ouverture de deux ContentDialog lève une exception, même si la tentative d’ouverture se fait dans des AppWindows distinctes.

Pour gérer cela, vous devez au minimum ouvrir la boîte de dialogue dans un bloc try/catch pour intercepter l’exception au cas où une autre boîte de dialogue serait déjà ouverte.

try
{
    ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
    // The dialog didn't open, probably because another dialog is already open.
}

Une autre façon de gérer les boîtes de dialogue consiste à suivre la boîte de dialogue actuellement ouverte et à la fermer avant d’essayer d’en ouvrir une nouvelle. Ici, vous créez pour cela une propriété statique dans MainPage appelée 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;

   // ...
}

Ensuite, vous vérifiez si une boîte de dialogue est actuellement ouverte et, si c’est le cas, vous appelez la méthode Hide pour la fermer. Enfin, vous affectez la nouvelle boîte de dialogue à CurrentDialoget essayez de l’afficher.

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.
    }
}

S’il n’est pas souhaitable de fermer une boîte de dialogue par programmation, ne l’affectez pas comme CurrentDialog. Ici, MainPage affiche une boîte de dialogue importante qui ne doit être fermée que si l’utilisateur clique sur Ok. Étant donné qu’elle n’est pas affectée comme CurrentDialog, aucune tentative de fermeture n’est effectuée programmatiquement.

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.
        }
    }
    // ...
}

Code complet

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));
        }
    }
}