AppWindow 및 관련 API는 각 창에서 동일한 UI 스레드에서 작업하는 동안 보조 창에 앱 콘텐츠를 표시하도록 하여 다중 창 앱 만들기를 간소화합니다.
비고
AppWindow는 현재 미리 보기로 제공됩니다. 즉, AppWindow를 사용하는 앱을 스토어에 제출할 수 있지만 일부 플랫폼 및 프레임워크 구성 요소는 AppWindow에서 작동하지 않는 것으로 알려져 있습니다(제한 사항 참조).
여기에서는 HelloAppWindow
라는 샘플 앱을 이용한 여러 창의 몇 가지 시나리오를 보여 줍니다. 샘플 앱은 다음 기능을 보여 줍니다.
- 기본 페이지에서 컨트롤의 도킹을 해제하고 새 창에서 엽니다.
- 새 창에서 페이지의 새 인스턴스를 엽니다.
- 프로그래밍 방식으로 앱에서 새 창의 크기를 조정하고 배치합니다.
- ContentDialog를 앱의 적절한 창과 연결합니다.
단일 창 있는 샘플 앱
샘플 앱 는 단일 창을 사용합니다.
도킹되지 않은 색 선택기 및 보조 창 있는
샘플 앱
API 개요
WindowManagement 네임스페이스의 AppWindow 클래스 및 기타 API는 Windows 10 버전 1903(SDK 18362)부터 사용할 수 있습니다. 앱이 이전 버전의 Windows 10을 대상으로 하는 경우 ApplicationView를 사용하여 보조 창을 만들어야 합니다. WindowManagement API는 아직 개발 중이며 API 참조 문서에 설명된 대로 제한 사항이 있습니다.
AppWindow에서 콘텐츠를 표시하는 데 사용하는 몇 가지 중요한 API는 다음과 같습니다.
앱 창
AppWindow 클래스를 사용하여 보조 창에 UWP 앱의 일부를 표시할 수 있습니다. ApplicationView와 개념이 비슷하지만 동작 및 수명에서는 동일하지 않습니다. AppWindow의 주요 기능은 각 인스턴스가 생성된 동일한 UI 처리 스레드(이벤트 디스패처 포함)를 공유하여 다중 창 앱을 간소화한다는 것입니다.
XAML 콘텐츠를 AppWindow에만 연결할 수 있으며 네이티브 DirectX 또는 홀로그램 콘텐츠는 지원되지 않습니다. 그러나 DirectX 콘텐츠를 호스팅하는 XAML SwapChainPanel을 표시할 수 있습니다.
윈도우 환경
WindowingEnvironment API를 사용하면 필요에 따라 앱을 조정할 수 있도록 앱이 제공되는 환경에 대해 알 수 있습니다. 환경이 지원하는 창의 종류를 설명합니다. 예를 들어 앱 Overlapped
이 PC에서 실행 중이거나 Tiled
앱이 Xbox에서 실행 중인 경우입니다. 또한 앱이 논리 디스플레이에 표시될 수 있는 영역을 설명하는 DisplayRegion 개체 집합을 제공합니다.
디스플레이 영역
DisplayRegion API는 논리 디스플레이에서 사용자에게 뷰를 표시할 수 있는 지역을 설명합니다. 예를 들어 데스크톱 PC에서는 작업 표시줄의 영역을 뺀 전체 디스플레이입니다. 반드시 백업 모니터의 실제 표시 영역과 1:1 매핑은 아닙니다. 동일한 모니터 내에 여러 디스플레이 영역이 있을 수 있으며, 모든 면에서 해당 모니터가 동질적인 경우 여러 모니터에 걸쳐 표시Region을 구성할 수 있습니다.
AppWindowPresenter
AppWindowPresenter API를 사용하면 창을 미리 정의된 구성(예: FullScreen
또는 CompactOverlay
.)으로 쉽게 전환할 수 있습니다. 이러한 구성은 구성을 지원하는 모든 디바이스에서 사용자에게 일관된 환경을 제공합니다.
UIContext
UIContext는 앱 창 또는 보기를 위한 고유한 식별자입니다. 자동으로 만들어지고 UIElement.UIContext 속성을 사용하여 UIContext를 검색할 수 있습니다. XAML 트리의 모든 UIElement에는 동일한 UIContext가 있습니다.
Window.Current 및 패턴과 GetForCurrentView
같은 API는 단일 ApplicationView/CoreWindow를 스레드당 하나의 XAML 트리로 사용하여 작업해야 하므로 UIContext는 중요합니다. AppWindow를 사용하는 경우에는 그렇지 않으므로 UIContext를 사용하여 특정 창을 대신 식별합니다.
XamlRoot
XamlRoot 클래스는 XAML 요소 트리를 보유하고, 창 호스트 개체(예: AppWindow 또는 ApplicationView)에 연결하고, 크기 및 표시 유형과 같은 정보를 제공합니다. XamlRoot 개체를 직접 만들지 않습니다. 대신 AppWindow에 XAML 요소를 연결할 때 생성됩니다. 그런 다음 UIElement.XamlRoot 속성을 사용하여 XamlRoot를 검색할 수 있습니다.
UIContext 및 XamlRoot에 대한 자세한 내용은 창 호스트에서 코드 이식 가능 만들기를 참조하세요.
새 창 표시
새 AppWindow에서 콘텐츠를 표시하는 단계를 살펴보겠습니다.
새 창을 표시하려면
정적 AppWindow.TryCreateAsync 메서드를 호출하여 새 AppWindow를 생성합니다.
AppWindow appWindow = await AppWindow.TryCreateAsync();
창 콘텐츠를 만듭니다.
일반적으로 XAML Frame를 만든 다음, 프레임을 XAML 페이지로 이동시켜 앱 콘텐츠를 정의합니다. 프레임 및 페이지에 대한 자세한 내용은 두 페이지 간의 피어 투 피어 탐색을 참조하세요.
Frame appWindowContentFrame = new Frame(); appWindowContentFrame.Navigate(typeof(AppWindowMainPage));
그러나 프레임 및 페이지뿐만 아니라 AppWindow에 XAML 콘텐츠를 표시할 수 있습니다. 예를 들어 ColorPicker와 같은 단일 컨트롤만 표시하거나 DirectX 콘텐츠를 호스트하는 SwapChainPanel 을 표시할 수 있습니다.
ElementCompositionPreview.SetAppWindowContent 메서드를 호출하여 XAML 콘텐츠를 AppWindow에 연결합니다.
ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
이 메서드에 대한 호출은 XamlRoot 개체를 만들고 지정된 UIElement에 대한 XamlRoot 속성으로 설정합니다.
AppWindow 인스턴스당 한 번만 이 메서드를 호출할 수 있습니다. 콘텐츠가 설정되면 이 AppWindow 인스턴스에 대한 SetAppWindowContent에 대한 추가 호출이 실패합니다. 또한 null UIElement 개체를 전달하여 AppWindow 콘텐츠의 연결을 끊으려면 호출이 실패합니다.
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
에 있습니다. 여러 인스턴스를 허용하며, 각 인스턴스는 독립적으로 작동합니다.
페이지의 기능을 사용하면 창을 조작하여 FullScreen
또는 CompactOverlay
설정하고, AppWindow.Changed 이벤트를 수신 대기하여 창에 대한 정보를 표시할 수 있습니다. 이러한 API AppWindowPage
를 호출하려면 호스팅하는 AppWindow 인스턴스에 대한 참조가 필요합니다.
필요한 것이 그것뿐이라면, AppWindowPage
속성을 만들고 생성할 때 AppWindow 인스턴스를 이에 할당할 수 있습니다.
AppWindowPage.xaml.cs
AppWindowPage
에서 AppWindow 참조를 저장할 속성을 생성합니다.
public sealed partial class AppWindowPage : Page
{
public AppWindow MyAppWindow { get; set; }
// ...
}
MainPage.xaml.cs
MainPage
에서 페이지 인스턴스에 대한 참조를 가져온 다음, 새로 생성한 AppWindow를 AppWindowPage
속성에 할당합니다.
private async void ShowNewWindowButton_Click(object sender, RoutedEventArgs e)
{
// Create a new window.
AppWindow appWindow = await AppWindow.TryCreateAsync();
// Create a Frame and navigate to the Page you want to show in the new window.
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(AppWindowPage));
// Get a reference to the page instance and assign the
// newly created AppWindow to the MyAppWindow property.
AppWindowPage page = (AppWindowPage)appWindowContentFrame.Content;
page.MyAppWindow = appWindow;
// ...
}
UIContext를 사용하여 앱 창 추적
앱의 다른 부분에서 AppWindow 인스턴스에 액세스할 수도 있습니다. 예를 들어, MainPage
에는 AppWindow의 모든 추적된 인스턴스를 닫는 '모두 닫기' 버튼이 있을 수 있습니다.
이 경우 UIContext 고유 식별자를 사용하여 사전창 인스턴스를 추적해야 합니다.
MainPage.xaml.cs
MainPage
사전을 정적 속성으로 만듭니다. 그런 다음, 페이지를 만들 때 사전에 페이지를 추가하고 페이지를 닫을 때 제거합니다. 콘텐츠 Frame(appWindowContentFrame.UIContext
)에서 UIContext를 가져올 수 있으며, 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
코드에서 AppWindowPage
인스턴스를 사용하려면 페이지의 UIContext를 사용하여, MainPage
의 정적 사전에서 이를 검색합니다. UIContext가 null이 아니도록 생성자가 아닌 페이지의 Loaded 이벤트 처리기에서 이 작업을 수행해야 합니다. 페이지 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: RequestMoveAdjacentToCurrentView, RequestMoveAdjacentToWindow, RequestMoveRelativeToDisplayRegion, RequestMoveToDisplayRegion입니다.
이 예제에서 이 코드는 창이 생성되는 기본 보기 옆에 있는 창으로 이동합니다.
colorPickerAppWindow.RequestMoveAdjacentToCurrentView();
창의 현재 크기 및 배치에 대한 정보를 얻으려면 GetPlacement를 호출합니다. 현재 창의 DisplayRegion, Offset, 및 크기를 제공하는 AppWindowPlacement 객체가 반환됩니다.
예를 들어 이 코드를 호출하여 창을 디스플레이의 오른쪽 위 모서리로 이동할 수 있습니다. 이 코드는 창이 표시된 후 호출해야 합니다. 그렇지 않으면 GetPlacement 호출에서 반환된 창 크기가 0,0이고 오프셋이 올바르지 않습니다.
DisplayRegion displayRegion = window.GetPlacement().DisplayRegion;
double displayRegionWidth = displayRegion.WorkAreaSize.Width;
double windowWidth = window.GetPlacement().Size.Width;
int horizontalOffset = (int)(displayRegionWidth - windowWidth);
window.RequestMoveRelativeToDisplayRegion(displayRegion, new Point(horizontalOffset, 0));
프레젠테이션 구성 요청
AppWindowPresenter 클래스를 사용하면 표시된 디바이스에 적합한 미리 정의된 구성을 사용하여 AppWindow를 표시할 수 있습니다.
AppWindowPresentationConfiguration 값을 사용하여 창을 FullScreen
모드 또는 CompactOverlay
모드로 배치할 수 있습니다.
이 예제에서는 다음을 수행하는 방법을 보여줍니다.
- 사용 가능한 창 프레젠테이션이 변경되면 AppWindow.Changed 이벤트를 사용하여 알림을 받습니다.
- AppWindow.Presenter 속성을 사용하여 현재 AppWindowPresenter을 가져옵니다.
- IsPresentationSupported를 호출하여 특정 AppWindowPresentationKind이 지원되는지 확인합니다.
- GetConfiguration을 호출하여 현재 사용되는 구성 종류를 확인합니다.
- RequestPresentation을 호출하여 현재 구성을 변경합니다.
private void Window_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidAvailableWindowPresentationsChange)
{
EnablePresentationButtons(sender);
}
if (args.DidWindowPresentationChange)
{
ConfigText.Text = window.Presenter.GetConfiguration().Kind.ToString();
}
if (args.DidSizeChange)
{
SizeText.Text = window.GetPlacement().Size.ToString();
}
}
private void EnablePresentationButtons(AppWindow window)
{
// Check whether the current AppWindowPresenter supports CompactOverlay.
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.CompactOverlay))
{
// Show the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Visible;
}
else
{
// Hide the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Collapsed;
}
// Check whether the current AppWindowPresenter supports FullScreen?
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.FullScreen))
{
// Show the FullScreen button...
fullScreenButton.Visibility = Visibility.Visible;
}
else
{
// Hide the FullScreen button...
fullScreenButton.Visibility = Visibility.Collapsed;
}
}
private void CompactOverlayButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.CompactOverlay)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.CompactOverlay);
fullScreenButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.FullScreen)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.FullScreen);
compactOverlayButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
XAML 요소 다시 사용
AppWindow를 사용하면 동일한 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="" />
</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 트리에 추가할 수 있습니다. 여기서는 먼저 ColorPicker의 부모 컨테이너가 될 Grid 를 만들고 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를 닫을 때 작업을 되돌립니다. 먼저 ColorPicker을 Grid에서 제거한 다음, 의 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 이벤트 처리기 내에 있으므로 sender(클릭한 버튼)를 사용하여 XamlRoot를 가져올 수 있습니다.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
주 창(main window)(ApplicationView) 외에 하나 이상의 AppWindows가 열려 있는 경우, 각 창은 대화 상자를 열려고 시도할 수 있습니다. 이는 모달 대화 상자가 루트에 있는 해당 창만 차단하기 때문입니다. 그러나 동시에 하나의 스레드에는 하나의 ContentDialog만 열 수 있습니다. 두 개의 ContentDialog를 열려고 시도하면, 별도의 AppWindow에서 열려고 하더라도 예외가 발생합니다.
이를 관리하려면 최소한 try/catch
블록에서 대화 상자를 열어, 다른 대화 상자가 이미 열려 있는 경우 예외를 처리해야 합니다.
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
대화 상자를 관리하는 또 다른 방법은 현재 열려 있는 대화 상자를 추적하고 새 대화 상자를 열기 전에 닫는 것입니다. 여기서는 이 목적을 위해 MainPage
에 'CurrentDialog
'이라는 정적 속성을 생성합니다.
public sealed partial class MainPage : Page
{
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
// ...
}
현재 열려 있는 대화 상자가 있는지 확인하고, 있을 경우 Hide 메서드를 호출하여 닫습니다. 마지막으로 새 대화 상자를 CurrentDialog
에 할당하고, 표시해 보세요.
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog simpleDialog = new ContentDialog
{
Title = "Content dialog",
Content = "Dialog box for " + window.Title,
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
MainPage.CurrentDialog = simpleDialog;
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already
// present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
대화 상자를 프로그래밍 방식으로 닫는 것이 바람직하지 않다면 그 대화 상자를 CurrentDialog
로 할당하지 마세요. 여기서는 사용자에게 MainPage
을 클릭해야만 해제할 수 있는 중요한 대화 상자인 Ok
을(를) 보여줍니다.
CurrentDialog
으로 할당되지 않았기 때문에, 프로그래밍 방식으로 닫으려고 시도하지 않습니다.
public sealed partial class MainPage : Page
{
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
// ...
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog importantDialog = new ContentDialog
{
Title = "Important dialog",
Content = "This dialog can only be dismissed by clicking Ok.",
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
// Do not track this dialog as the MainPage.CurrentDialog.
// It should only be closed by clicking the Ok button.
MainPage.CurrentDialog = null;
try
{
ContentDialogResult result = await importantDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
// ...
}
전체 코드
MainPage.xaml
<Page
x:Class="HelloAppWindow.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HelloAppWindow"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button x:Name="NewWindowButton" Content="Open new window"
Click="ShowNewWindowButton_Click" Margin="0,12"/>
<Button Content="Open dialog" Click="DialogButton_Click"
HorizontalAlignment="Stretch"/>
<Button Content="Close all" Click="CloseAllButton_Click"
Margin="0,12" HorizontalAlignment="Stretch"/>
</StackPanel>
<StackPanel x:Name="colorPickerContainer" Grid.Column="1" Background="WhiteSmoke">
<Button Click="DetachColorPickerButton_Click" HorizontalAlignment="Right">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" />
</Button>
<ColorPicker x:Name="colorPicker" Margin="12" Width="288"
IsColorChannelTextInputVisible="False"
ColorChanged="ColorPicker_ColorChanged"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace HelloAppWindow
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
AppWindow colorPickerAppWindow;
// Track open app windows in a Dictionary.
public static Dictionary<UIContext, AppWindow> AppWindows { get; set; }
= new Dictionary<UIContext, AppWindow>();
// Track the last opened dialog so you can close it if another dialog tries to open.
public static ContentDialog CurrentDialog { get; set; } = null;
public MainPage()
{
this.InitializeComponent();
}
private async void ShowNewWindowButton_Click(object sender, RoutedEventArgs e)
{
// Create a new window.
AppWindow appWindow = await AppWindow.TryCreateAsync();
// Create a Frame and navigate to the Page you want to show in the new window.
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(AppWindowPage));
// Get a reference to the page instance and assign the
// newly created AppWindow to the MyAppWindow property.
AppWindowPage page = (AppWindowPage)appWindowContentFrame.Content;
page.MyAppWindow = appWindow;
page.TextColorBrush = new SolidColorBrush(colorPicker.Color);
// Attach the XAML content to the window.
ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowContentFrame);
// Add the new page to the Dictionary using the UIContext as the Key.
AppWindows.Add(appWindowContentFrame.UIContext, appWindow);
appWindow.Title = "App Window " + AppWindows.Count.ToString();
// When the window is closed, be sure to release XAML resources
// and the reference to the window.
appWindow.Closed += delegate
{
MainPage.AppWindows.Remove(appWindowContentFrame.UIContext);
appWindowContentFrame.Content = null;
appWindow = null;
};
// Show the window.
await appWindow.TryShowAsync();
}
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog importantDialog = new ContentDialog
{
Title = "Important dialog",
Content = "This dialog can only be dismissed by clicking Ok.",
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
// Do not track this dialog as the MainPage.CurrentDialog.
// It should only be closed by clicking the Ok button.
MainPage.CurrentDialog = null;
try
{
ContentDialogResult result = await importantDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
private async void DetachColorPickerButton_Click(object sender, RoutedEventArgs e)
{
// Create the color picker window.
if (colorPickerAppWindow == null)
{
colorPickerContainer.Children.Remove(colorPicker);
colorPickerContainer.Visibility = Visibility.Collapsed;
Grid appWindowRootGrid = new Grid();
appWindowRootGrid.Children.Add(colorPicker);
// Create a new window
colorPickerAppWindow = await AppWindow.TryCreateAsync();
colorPickerAppWindow.RequestMoveAdjacentToCurrentView();
colorPickerAppWindow.RequestSize(new Size(300, 428));
colorPickerAppWindow.Title = "Color picker";
// Attach the XAML content to our window
ElementCompositionPreview.SetAppWindowContent(colorPickerAppWindow, appWindowRootGrid);
// Make sure to release the reference to this window,
// and release XAML resources, when it's closed
colorPickerAppWindow.Closed += delegate
{
appWindowRootGrid.Children.Remove(colorPicker);
appWindowRootGrid = null;
colorPickerAppWindow = null;
colorPickerContainer.Children.Add(colorPicker);
colorPickerContainer.Visibility = Visibility.Visible;
};
}
// Show the window.
await colorPickerAppWindow.TryShowAsync();
}
private void ColorPicker_ColorChanged(ColorPicker sender, ColorChangedEventArgs args)
{
NewWindowButton.Background = new SolidColorBrush(args.NewColor);
}
private async void CloseAllButton_Click(object sender, RoutedEventArgs e)
{
while (AppWindows.Count > 0)
{
await AppWindows.Values.First().CloseAsync();
}
}
}
}
AppWindowPage.xaml
<Page
x:Class="HelloAppWindow.AppWindowPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HelloAppWindow"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TextBlock x:Name="TitleTextBlock" Text="Hello AppWindow!" FontSize="24" HorizontalAlignment="Center" Margin="24"/>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button Content="Open dialog" Click="DialogButton_Click"
Width="200" Margin="0,4"/>
<Button Content="Move window" Click="MoveWindowButton_Click"
Width="200" Margin="0,4"/>
<ToggleButton Content="Compact Overlay" x:Name="compactOverlayButton" Click="CompactOverlayButton_Click"
Width="200" Margin="0,4"/>
<ToggleButton Content="Full Screen" x:Name="fullScreenButton" Click="FullScreenButton_Click"
Width="200" Margin="0,4"/>
<Grid>
<TextBlock Text="Size:"/>
<TextBlock x:Name="SizeText" HorizontalAlignment="Right"/>
</Grid>
<Grid>
<TextBlock Text="Presentation:"/>
<TextBlock x:Name="ConfigText" HorizontalAlignment="Right"/>
</Grid>
</StackPanel>
</Grid>
</Page>
AppWindowPage.xaml.cs
using System;
using Windows.Foundation;
using Windows.Foundation.Metadata;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace HelloAppWindow
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class AppWindowPage : Page
{
AppWindow window;
public AppWindow MyAppWindow { get; set; }
public SolidColorBrush TextColorBrush { get; set; } = new SolidColorBrush(Colors.Black);
public AppWindowPage()
{
this.InitializeComponent();
Loaded += AppWindowPage_Loaded;
}
private void AppWindowPage_Loaded(object sender, RoutedEventArgs e)
{
// Get the reference to this AppWindow that was stored when it was created.
window = MainPage.AppWindows[this.UIContext];
// Set up event handlers for the window.
window.Changed += Window_Changed;
TitleTextBlock.Foreground = TextColorBrush;
}
private async void DialogButton_Click(object sender, RoutedEventArgs e)
{
ContentDialog simpleDialog = new ContentDialog
{
Title = "Content dialog",
Content = "Dialog box for " + window.Title,
CloseButtonText = "Ok"
};
if (MainPage.CurrentDialog != null)
{
MainPage.CurrentDialog.Hide();
}
MainPage.CurrentDialog = simpleDialog;
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already
// present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
simpleDialog.XamlRoot = ((Button)sender).XamlRoot;
}
try
{
ContentDialogResult result = await simpleDialog.ShowAsync();
}
catch (Exception)
{
// The dialog didn't open, probably because another dialog is already open.
}
}
private void Window_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidAvailableWindowPresentationsChange)
{
EnablePresentationButtons(sender);
}
if (args.DidWindowPresentationChange)
{
ConfigText.Text = window.Presenter.GetConfiguration().Kind.ToString();
}
if (args.DidSizeChange)
{
SizeText.Text = window.GetPlacement().Size.ToString();
}
}
private void EnablePresentationButtons(AppWindow window)
{
// Check whether the current AppWindowPresenter supports CompactOverlay.
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.CompactOverlay))
{
// Show the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Visible;
}
else
{
// Hide the CompactOverlay button...
compactOverlayButton.Visibility = Visibility.Collapsed;
}
// Check whether the current AppWindowPresenter supports FullScreen?
if (window.Presenter.IsPresentationSupported(AppWindowPresentationKind.FullScreen))
{
// Show the FullScreen button...
fullScreenButton.Visibility = Visibility.Visible;
}
else
{
// Hide the FullScreen button...
fullScreenButton.Visibility = Visibility.Collapsed;
}
}
private void CompactOverlayButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.CompactOverlay)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.CompactOverlay);
fullScreenButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
if (window.Presenter.GetConfiguration().Kind != AppWindowPresentationKind.FullScreen)
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.FullScreen);
compactOverlayButton.IsChecked = false;
}
else
{
window.Presenter.RequestPresentation(AppWindowPresentationKind.Default);
}
}
private void MoveWindowButton_Click(object sender, RoutedEventArgs e)
{
DisplayRegion displayRegion = window.GetPlacement().DisplayRegion;
double displayRegionWidth = displayRegion.WorkAreaSize.Width;
double windowWidth = window.GetPlacement().Size.Width;
int horizontalOffset = (int)(displayRegionWidth - windowWidth);
window.RequestMoveRelativeToDisplayRegion(displayRegion, new Point(horizontalOffset, 0));
}
}
}