Udostępnij przez


Pokaż wiele widoków za pomocą usługi AppWindow

AppWindow oraz powiązane z nim interfejsy API upraszczają tworzenie aplikacji z wieloma oknami, umożliwiając wyświetlanie zawartości aplikacji w oknach pomocniczych przy użyciu tego samego wątku interfejsu użytkownika dla każdego z nich.

Uwaga / Notatka

Aplikacja AppWindow jest obecnie dostępna w wersji zapoznawczej. Oznacza to, że można przesyłać aplikacje korzystające z AppWindow do sklepu, ale wiadomo, że niektóre składniki platformy i frameworku nie działają z AppWindow (zobacz Ograniczenia).

W tym miejscu przedstawiono kilka scenariuszy dla wielu okien z przykładową aplikacją o nazwie HelloAppWindow. Przykładowa aplikacja demonstruje następujące funkcje:

  • Odłącz element sterujący ze strony głównej i otwórz go w nowym oknie.
  • Otwórz nowe wystąpienia strony w nowych oknach.
  • Programowo ustawiaj rozmiar i położenie nowych okien w aplikacji.
  • Skojarz plik ContentDialog z odpowiednim oknem w aplikacji.

Przykładowa aplikacja z jednym oknem

Przykładowa aplikacja z jednym oknem

przykładowa aplikacja z selektorem kolorów bez zadokowania i okna pomocniczego

Przykładowa aplikacja z odłączonym selektorem kolorów i dodatkowym oknem

ważne interfejsy API: przestrzeń nazw Windows.UI.WindowManagement, klasa AppWindow

Przegląd interfejsu API

Klasa AppWindow i inne interfejsy API w przestrzeni nazw WindowManagement są dostępne od wersji 1903 systemu Windows 10 (SDK 18362). Jeśli aplikacja jest przeznaczona dla starszych wersji systemu Windows 10, musisz użyć elementu ApplicationView do utworzenia okien pomocniczych. Interfejsy API WindowManagement są nadal opracowywane i mają ograniczenia zgodnie z opisem w dokumentacji referencyjnej API.

Poniżej przedstawiono niektóre ważne interfejsy API używane do wyświetlania zawartości w systemie AppWindow.

Okno aplikacji

Klasa AppWindow może służyć do wyświetlania części aplikacji UWP w drugim oknie. Jest ona podobna do elementu ApplicationView, ale nie jest taka sama w zachowaniu i okresie istnienia. Główną cechą AppWindow jest to, że każde wystąpienie dzieli ten sam wątek przetwarzania interfejsu użytkownika (w tym dyspozytor zdarzeń), z którego zostało utworzone, co upraszcza tworzenie aplikacji z wieloma oknami.

Zawartość XAML można połączyć tylko z aplikacją AppWindow, nie ma obsługi natywnej zawartości DirectX ani Holographic. Można jednak wyświetlić XAML SwapChainPanel hostującego zawartość DirectX.

Środowisko Okienkowe

Interfejs API WindowingEnvironment informuje o środowisku, w którym aplikacja jest prezentowana, dzięki czemu możesz dostosować aplikację zgodnie z potrzebami. Opisuje rodzaj okna obsługiwanego przez środowisko; na przykład jeśli Overlapped aplikacja jest uruchomiona na komputerze lub Tiled czy aplikacja jest uruchomiona na konsoli Xbox. Udostępnia również zestaw obiektów DisplayRegion opisujących obszary, w których aplikacja może być wyświetlana na ekranie logicznym.

Obszar Wyświetlania

Interfejs API DisplayRegion opisuje region, w którym można wyświetlić użytkownikowi widok na ekranie logicznym; na przykład na komputerze stacjonarnym jest to pełny ekran minus obszar paska zadań. Niekoniecznie jest to mapowanie 1:1 z fizycznym obszarem wyświetlania głównego monitora. W ramach tego samego monitora może istnieć wiele regionów wyświetlania lub można skonfigurować region wyświetlania tak, aby obejmował wiele monitorów, jeśli te monitory są homogeniczne we wszystkich aspektach.

Prezentator Okna Aplikacji

Interfejs API AppWindowPresenter umożliwia łatwe przełączanie okien do wstępnie zdefiniowanych konfiguracji, takich jak FullScreen lub CompactOverlay. Te konfiguracje zapewniają użytkownikowi spójne środowisko pracy na dowolnym urządzeniu, które obsługuje konfigurację.

Kontekst interfejsu użytkownika

UIContext jest unikatowym identyfikatorem okna lub widoku aplikacji. Jest on tworzony automatycznie i można użyć właściwości UIElement.UIContext , aby pobrać element UIContext. Każdy UIElement w drzewie XAML ma ten sam UIContext.

Funkcja UIContext jest ważna, ponieważ interfejsy API, takie jak Window.Current i wzorzec GetForCurrentView polegają na użyciu pojedynczego elementu ApplicationView/CoreWindow z pojedynczym drzewem XAML na wątek do pracy. Nie jest to przypadek, gdy używasz AppWindow, więc zamiast tego używasz UIContext do zidentyfikowania konkretnego okna.

XamlRoot

Klasa XamlRoot zawiera drzewo elementów XAML , łączy je z obiektem hosta okna (na przykład AppWindow lub ApplicationView) i udostępnia informacje, takie jak rozmiar i widoczność. Nie tworzysz obiektu XamlRoot bezpośrednio. Zamiast tego jest tworzony podczas dołączania elementu XAML do AppWindow. Następnie możesz użyć właściwości UIElement.XamlRoot , aby pobrać plik XamlRoot.

Aby uzyskać więcej informacji na temat UIContext i XamlRoot, zobacz Uczyń kod przenośnym między hostami okiennymi.

Pokaż nowe okno

Przyjrzyjmy się krokom wyświetlania zawartości w nowej aplikacji AppWindow.

Aby wyświetlić nowe okno

  1. Wywołaj statyczną metodę AppWindow.TryCreateAsync , aby utworzyć nową AppWindow.

    AppWindow appWindow = await AppWindow.TryCreateAsync();
    
  2. Utwórz zawartość okna.

    Zazwyczaj tworzyszramek XAML, a następnie przechodzisz do strony XAML , w której zdefiniowano zawartość aplikacji. Aby uzyskać więcej informacji na temat ramek i stron, zobacz sekcję o nawigacji między dwiema stronami typu peer-to-peer.

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

    Można jednak wyświetlić dowolną zawartość XAML w usłudze AppWindow, a nie tylko ramkę i stronę. Możesz na przykład wyświetlić tylko jedną kontrolkę, na przykład ColorPicker, lub wyświetlić element SwapChainPanel hostujący zawartość DirectX.

  3. Wywołaj metodę ElementCompositionPreview.SetAppWindowContent , aby dołączyć zawartość XAML do systemu AppWindow.

    ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
    

    Wywołanie tej metody tworzy obiekt XamlRoot i ustawia go jako właściwość XamlRoot dla określonego elementu UIElement.

    Możesz wywołać tę metodę tylko raz dla jednej instancji AppWindow. Po ustawieniu zawartości, kolejne wywołania polecenia SetAppWindowContent dla tego wystąpienia AppWindow zakończą się niepowodzeniem. Ponadto, jeśli spróbujesz rozłączyć zawartość AppWindow, przekazując obiekt UIElement jako null, wywołanie zakończy się niepowodzeniem.

  4. Wywołaj metodę AppWindow.TryShowAsync , aby wyświetlić nowe okno.

    await appWindow.TryShowAsync();
    

Zwalnianie zasobów po zamknięciu okna

Zawsze należy obsługiwać zdarzenie AppWindow.Closed, aby zwolnić zasoby XAML (zawartość AppWindow) i odwołania do systemu AppWindow.

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

Wskazówka

Należy zachować ilość kodu w Closed procedurze obsługi zdarzeń do minimalnej możliwej ilości, aby uniknąć nieoczekiwanych problemów.

Śledzenie wystąpień usługi AppWindow

W zależności od tego, jak korzystasz z wielu okien w swojej aplikacji, może być konieczne lub niekonieczne śledzenie instancji AppWindow, które tworzysz. Przykład HelloAppWindow pokazuje różne sposoby, na które można typowo używać AppWindow. Tutaj omówimy, dlaczego te okna powinny być śledzone i jak to zrobić.

Proste śledzenie

Okno selektora kolorów hostuje pojedynczą kontrolkę XAML, a kod umożliwiający interakcję z selektorem kolorów znajduje się w MainPage.xaml.cs pliku. Okno selektora kolorów zezwala tylko na pojedyncze wystąpienie i jest zasadniczo rozszerzeniem MainWindow. Aby upewnić się, że utworzono tylko jedno wystąpienie, okno narzędzia wyboru kolorów jest śledzone za pomocą zmiennej na poziomie strony. Przed utworzeniem nowego okna selektora kolorów sprawdź, czy istnieje instancja, a jeśli istnieje, pomiń kroki tworzenia nowego okna i wywołaj TryShowAsync na istniejącym oknie.

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

Śledzenie wystąpienia usługi AppWindow w hostowanej zawartości

Okno AppWindowPage hostuje pełną stronę XAML, a kod umożliwiający interakcję ze stroną znajduje się w pliku AppWindowPage.xaml.cs. Umożliwia wiele wystąpień, z których każda działa niezależnie.

Funkcja strony umożliwia manipulowanie oknem, ustawianie go na FullScreen lub CompactOverlay, a także nasłuchiwanie zdarzeń AppWindow.Changed w celu wyświetlenia informacji o oknie. Aby wywołać te interfejsy API, AppWindowPage potrzebuje odwołania do wystąpienia AppWindow, które go hostuje.

Jeśli to wszystko, czego potrzebujesz, możesz utworzyć właściwość w AppWindowPage i przypisać do niego wystąpienie AppWindow podczas jego tworzenia.

AppWindowPage.xaml.cs

W AppWindowPageutwórz właściwość do przechowywania odwołania AppWindow.

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

    // ...
}

MainPage.xaml.cs

W MainPagepobierz odwołanie do instancji strony i przypisz nowo utworzoną instancję AppWindow do właściwości w 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;

    // ...
}

Śledzenie okien aplikacji za pomocą UIContext

Możesz również mieć dostęp do wystąpień AppWindow z innych części Twojej aplikacji. Na przykład MainPage może mieć przycisk "Zamknij wszystko", który zamyka wszystkie śledzone wystąpienia systemu AppWindow.

W takim przypadku należy użyć interfejsu użytkownika Context unikatowy identyfikator do śledzenia wystąpień okien w słowniku .

MainPage.xaml.cs

W MainPagepliku utwórz słownik jako właściwość statyczną. Następnie dodaj stronę do słownika podczas jego tworzenia i usuń ją po zamknięciu strony. Możesz uzyskać element UIContext z zawartości ramki Frame (appWindowContentFrame.UIContext) po wywołaniu 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

Aby użyć wystąpienia AppWindow w kodzie AppWindowPage, użyj kontekstu UI strony , aby pobrać je ze statycznego słownika w MainPage. Należy to zrobić w obsłudze zdarzenia strony Loaded zamiast w konstruktorze, aby UIContext nie był wartością null. Możesz uzyskać UIContext ze strony: 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;
    }
    // ...
}

Uwaga / Notatka

W przykładzie HelloAppWindow przedstawiono oba sposoby śledzenia okna w AppWindowPage, ale zazwyczaj korzysta się z jednego z nich, nie obu.

Żądanie rozmiaru i położenia okna

Klasa AppWindow ma kilka metod, których można użyć do kontrolowania rozmiaru i rozmieszczenia okna. Zgodnie z nazwami metod system może lub nie może przestrzegać żądanych zmian w zależności od czynników środowiskowych.

Wywołaj metodę RequestSize , aby określić żądany rozmiar okna, w następujący sposób.

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

Metody zarządzania umieszczaniem okien mają nazwę RequestMove*: RequestMoveAdjacentToCurrentView, RequestMoveAdjacentToWindow, RequestMoveRelativeToDisplayRegion, RequestMoveToDisplayRegion.

W tym przykładzie ten kod przenosi okno obok głównego widoku, z którego okno zostało utworzone.

colorPickerAppWindow.RequestMoveAdjacentToCurrentView();

Aby uzyskać informacje o bieżącym rozmiarze i położeniu okna, wywołaj GetPlacement. Spowoduje to zwrócenie obiektu AppWindowPlacement, który udostępnia bieżący DisplayRegion, przesunięciei rozmiar okna.

Możesz na przykład wywołać ten kod, aby przenieść okno do prawego górnego rogu ekranu. Ten kod musi być wywoływany po wyświetleniu okna; W przeciwnym razie rozmiar okna zwrócony przez wywołanie metody GetPlacement będzie wynosić 0,0, a przesunięcie będzie niepoprawne.

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

Żądanie konfiguracji prezentacji

Klasa AppWindowPresenter umożliwia wyświetlenie AppWindow, używając wstępnie zdefiniowanej konfiguracji dostosowanej do urządzenia, na którym jest wyświetlana. Możesz użyć wartości AppWindowPresentationConfiguration, aby umieścić okno w trybie FullScreen lub CompactOverlay.

W tym przykładzie pokazano, jak wykonać następujące czynności:

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

Ponowne używanie elementów XAML

Element AppWindow umożliwia posiadanie wielu drzew XAML z tym samym wątkiem interfejsu użytkownika. Jednak element XAML można dodać tylko raz do drzewa XAML. Jeśli chcesz przenieść część interfejsu użytkownika z jednego okna do innego, musisz zarządzać jego rozmieszczeniem w drzewie XAML.

W tym przykładzie pokazano, jak ponownie użyć kontrolki ColorPicker podczas przenoszenia jej między głównym oknem a oknem pomocniczym.

Selektor kolorów jest zadeklarowany w XAML dla MainPage, co umieszcza go w drzewie 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>

Gdy selektor kolorów jest odłączony do umieszczenia w nowym oknie aplikacji, należy najpierw usunąć go z drzewa XAML o oznaczeniu MainPage, usuwając go z kontenera nadrzędnego. Mimo że nie jest to wymagane, ten przykład również ukrywa kontener nadrzędny.

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

Następnie możesz dodać go do nowego drzewa XAML. W tym miejscu należy najpierw utworzyć Grid, który będzie kontenerem nadrzędnym dla narzędzia ColorPicker, i dodać ColorPicker jako podrzędny element Grid. (Umożliwia to łatwe usunięcie narzędzia ColorPicker z tego drzewa XAML później.) Następnie należy ustawić siatkę jako korzeń drzewa XAML w nowym oknie.

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

Po zamknięciu AppWindow trzeba odwrócić proces. Najpierw usuń ColorPicker z Grid, a następnie dodaj go jako element podrzędny StackPanel w 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();
}

Pokaż okno dialogowe

Domyślnie okna dialogowe zawartości są wyświetlane modalnie względem korzenia ApplicationView. Jeśli używasz ContentDialog wewnątrz AppWindow, należy ręcznie ustawić XamlRoot w oknie dialogowym na element główny hosta XAML.

W tym celu ustaw właściwość XamlRoot contentDialog na tę samą XamlRoot jako element już w systemie AppWindow. W tym miejscu ten kod znajduje się wewnątrz procedury obsługi zdarzenia kliknięcia przycisku Click, dzięki czemu możesz użyć obiektu sender (czyli klikniętego Buttona), aby uzyskać XamlRoot.

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

Jeśli masz co najmniej jedno otwarte okno aplikacji oprócz okna głównego (ApplicationView), każde z nich może próbować otworzyć okno dialogowe, ponieważ modalne okno dialogowe zablokuje tylko to okno, w którym się znajduje. Jednak w jednym wątku naraz może być otwarty tylko jeden ContentDialog. Próba otwarcia dwóch obiektów ContentDialogs spowoduje zgłoszenie wyjątku, nawet jeśli próbują otworzyć się w osobnych oknach AppWindows.

Aby sobie z tym poradzić, należy przynajmniej otworzyć dialog w ramach bloku try/catch, aby przechwycić wyjątek na wypadek, gdyby inne okno dialogowe było już otwarte.

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

Innym sposobem zarządzania oknami dialogowymi jest śledzenie aktualnie otwartego okna dialogowego i zamknięcie go przed próbą otwarcia nowego okna dialogowego. W tym miejscu tworzysz właściwość statyczną w MainPage zwaną CurrentDialog do tego celu.

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;

   // ...
}

Następnie sprawdzasz, czy jest aktualnie otwarte okno dialogowe, a jeśli istnieje, wywołaj metodę Hide , aby ją zamknąć. Na koniec przypisz nowe okno dialogowe do CurrentDialogelementu i spróbuj go wyświetlić.

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

Jeśli nie jest pożądane, aby okno dialogowe zostało zamknięte programowo, nie przypisz go jako .CurrentDialog W tym miejscu MainPage wyświetla ważne okno dialogowe, które powinno zostać zamknięte tylko wtedy, gdy użytkownik kliknie Ok. Ponieważ nie jest przypisany jako CurrentDialog, nie podejmuje się żadnej próby jego programowego zamknięcia.

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

Kompletny kod

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