TabView 컨트롤을 사용하여 탭 세트와 각 탭의 콘텐츠를 표시할 수 있습니다. TabView 컨트롤은 사용자가 새 탭을 다시 정렬하거나 닫거나 열 수 있도록 하면서 콘텐츠의 여러 페이지(또는 문서)를 표시하는 데 유용합니다.
올바른 컨트롤인가요?
일반적으로 탭 UI는 함수와 모양이 다른 두 가지 스타일 중 하나로 제공됩니다.
- 정적 탭은 설정 창에서 자주 발견되는 일종의 탭입니다. 이 탭은 일반적으로 미리 정의된 콘텐츠가 들어 있는 여러 페이지를 고정된 순서로 포함합니다.
- 문서 탭은 Microsoft Edge 같은 브라우저에서 볼 수 있는 형태의 탭입니다. 사용자는 탭을 만들고, 제거하고, 다시 정렬하고, 창 간에 탭을 이동하고, 탭의 내용을 변경할 수 있습니다.
기본적으로 TabView 는 문서 탭을 제공하도록 구성됩니다. 사용자가 다음을 수행할 수 있는 경우 TabView를 사용하는 것이 좋습니다.
- 탭을 동적으로 열거나 닫거나 다시 정렬합니다.
- 문서 또는 웹 페이지를 탭으로 직접 엽니다.
- 창 간에 탭을 끌어서 놓습니다.
TabView API를 사용하면 정적 탭에 대한 컨트롤을 구성할 수 있습니다. 그러나 Windows 디자인 지침을 따르고 정적 탐색 항목이 몇 개 이상 있는 경우 NavigationView 컨트롤을 사용하는 것이 좋습니다.
구조
Tabbed UI는 TabView 컨트롤과 하나 이상의 TabViewItem 컨트롤을 사용하여 만들어집니다. TabView는 단일 탭과 해당 콘텐츠를 나타내는 TabViewItem의 인스턴스를 호스트합니다.
TabView 구성 요소
이 이미지는 TabView 컨트롤의 일부를 보여줍니다. 탭 스트립에는 머리글과 바닥글이 있지만 문서와 달리 탭 스트립의 머리글과 바닥글은 각각 스트립의 맨 왼쪽과 맨 오른쪽에 있습니다.
TabViewItem 구성 요소
이 이미지는 TabViewItem 컨트롤의 일부를 보여줍니다. 콘텐츠가 TabView 컨트롤 내부에 표시되지만 콘텐츠는 실제로 TabViewItem의 일부입니다.
권장 사항
탭 선택
대부분의 사용자는 간단하게 웹 브라우저를 사용하여 문서 탭을 경험합니다. 사용자가 앱에서 문서 탭을 사용하는 방식을 살펴보면 사용자가 어떤 탭 동작을 예상하는지 알 수 있습니다.
사용자가 문서 탭 집합과 상호 작용하는 방식에 관계없이 항상 활성 탭이 있어야 합니다. 사용자가 선택한 탭을 닫거나 선택한 탭을 다른 창으로 나누면 다른 탭이 활성 탭이 됩니다. TabView는 이 작업을 자동으로 수행하여 다음 탭을 선택합니다. 앱에서 선택되지 않은 탭이 있는 TabView를 허용해야 하는 충분한 이유가 있는 경우 TabView의 콘텐츠 영역은 비어 있습니다.
키보드 탐색
TabView는 기본적으로 여러 일반적인 키보드 탐색 시나리오를 지원합니다. 이 섹션에서는 기본 제공 기능에 대해 설명하고, 일부 앱에 유용할 수 있는 추가 기능에 대한 권장 사항을 제공합니다.
탭 및 커서 키 동작
포커스가 TabStrip 영역으로 이동하면 선택한 TabViewItem에 포커스가 맞춰집니다. 그런 다음 왼쪽 및 오른쪽 화살표 키를 사용하여 탭 스트립의 다른 탭으로 포커스(선택 안 됨)를 이동할 수 있습니다. 화살표 포커스는 탭 스트립 및 추가 탭(+) 단추(있는 경우) 안쪽에 트래핑됩니다. 탭 스트립 영역에서 포커스를 이동하려면 사용자가 Tab 키를 눌러 포커스를 다음 포커스 가능한 요소로 이동할 수 있습니다.
Tab 키를 통해 포커스 이동
화살표 키는 포커스를 순환하지 않습니다.
탭 선택
TabViewItem에 포커스가 있는 경우 Space 또는 Enter 키를 눌러 해당 TabViewItem을 선택합니다.
화살표 키를 사용하여 포커스를 이동한 다음 스페이스바를 눌러 탭을 선택합니다.
인접한 탭을 선택하는 바로 가기
Ctrl+Tab을 눌러 다음 TabViewItem을 선택합니다. Ctrl+Shift+Tab을 눌러 이전 TabViewItem을 선택합니다. 이러한 용도로 탭 목록은 "루프"되므로 마지막 탭을 선택하는 동안 다음 탭을 선택하면 첫 번째 탭이 선택됩니다.
탭 닫기
Ctrl+F4를 눌러 TabCloseRequested 이벤트를 발생시킵니다. 이벤트를 처리하고 필요한 경우 탭을 닫습니다.
팁
자세한 내용은 이 문서의 뒷부 분에 있는 개발자를 위한 키보드 지침을 참조하세요.
탭 보기 만들기
- 중요 API: TabView 클래스, TabViewItem 클래스
WinUI 3 갤러리 앱에는 대부분의 WinUI 3 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. Microsoft Store에서 앱을 다운로드하거나 GitHub에서 소스 코드를 가져오세요.
이 섹션의 예제에서는 TabView 컨트롤을 구성하는 다양한 방법을 보여 줍니다.
탭 보기 항목
TabView의 각 탭은 탭 스트립에 표시되는 탭과 탭 스트립 아래에 표시된 콘텐츠를 모두 포함하는 TabViewItem 컨트롤로 표시됩니다.
탭 구성
각 TabViewItem에 대해 머리글과 아이콘을 설정하고 사용자가 탭을 닫을 수 있는지 여부를 지정할 수 있습니다.
-
헤더 속성은 일반적으로 탭에 대한 설명 레이블을 제공하는 문자열 값으로 설정됩니다. 그러나 속성은
Header
모든 개체일 수 있습니다. HeaderTemplate 속성을 사용하여 바인딩된 헤더 데이터를 표시하는 방법을 정의하는 DataTemplate을 지정할 수도 있습니다. - 아이콘 원본 속성을 설정하여 탭의 아이콘을 지정합니다.
- 기본적으로 탭에는 닫기 단추 (X)가 표시됩니다.
IsClosable 속성을 설정하여
false
닫기 단추를 숨기고 사용자가 탭을 닫을 수 없도록 할 수 있습니다. (요청된 닫기 이벤트 외부에서 앱 코드의 탭을 닫는 경우 먼저 해당 탭을IsClosable
true
확인해야 합니다.)
TabView의 경우 모든 탭에 적용되는 몇 가지 옵션을 구성할 수 있습니다.
- 기본적으로 닫기 단추는 항상 닫을 수 있는 탭에 대해 표시됩니다.
CloseButtonOverlayMode 속성을 설정하여 이 동작을
OnPointerOver
변경할 수 있습니다. 이 경우 선택된 탭은 닫을 수 있는 경우 항상 닫기 버튼을 표시합니다. 그러나 선택되지 않은 탭은 닫을 수 있고 사용자가 그 위에 포인터를 올려놓은 경우에만 닫기 버튼을 표시합니다. -
TabWidthMode 속성을 설정하여 탭의 크기를 변경할 수 있습니다. (.
Width
에서TabViewItem
속성이 무시됩니다.) TabViewWidthMode 열거형의 옵션은 다음과 같습니다.-
Equal
- 각 탭의 너비는 동일합니다. 기본값입니다. -
SizeToContent
- 각 탭은 탭 내의 콘텐츠에 맞게 너비를 조정합니다. -
Compact
- 선택하지 않은 탭이 축소되어 아이콘만 표시합니다. 선택한 탭이 탭 내의 콘텐츠를 표시하도록 조정됩니다.
-
콘텐츠
선택한 탭에 표시되는 요소는 TabViewItem의 콘텐츠 속성에 추가됩니다. TabViewItem은 ContentControl이므로 모든 형식의 개체를 콘텐츠로 추가할 수 있습니다. ContentTemplate 속성에 DataTemplate을 적용할 수도 있습니다. 자세한 내용은 ContentControl 클래스를 참조하세요.
이 문서의 예제에서는 XAML의 요소에 Content
직접 텍스트를 추가하는 간단한 사례를 보여 줍니다. 그러나 실제 UI는 일반적으로 더 복잡합니다. 탭의 콘텐츠로 복잡한 UI를 추가하는 일반적인 방법은 UserControl 또는 페이지에 캡슐화하고 TabViewItem의 콘텐츠로 추가하는 것입니다. 이 예제에서는 앱에 라는 PictureSettingsControl
XAML UserControl이 있다고 가정합니다.
<TabViewItem>
<TabViewItem.Content>
<local:PictureSettingsControl/>
</TabViewItem.Content>
</TabViewItem>
정적 탭
이 예제에서는 두 개의 정적 탭이 있는 간단한 TabView를 보여 줍니다. 두 탭 항목 모두 XAML에 TabView의 콘텐츠로 추가됩니다.
TabView를 정적으로 만들려면 다음 설정을 사용합니다.
- 탭 추가 단추를 숨기고
false
이벤트가 발생하지 않도록 하려면 IsAddTabButtonVisible 속성을 설정합니다. - 사용자가 탭을 다른 순서로 끌 수 없도록
false
속성을 설정합니다. - 각 TabViewItem에서 IsClosable 속성을 false로 설정하여 탭 닫기 단추를 숨기면 사용자가 TabCloseRequested 이벤트를 발생시키는 것을 방지합니다.
<TabView VerticalAlignment="Stretch"
IsAddTabButtonVisible="False"
CanReorderTabs="False">
<TabViewItem Header="Picture" IsClosable="False">
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Pictures"/>
</TabViewItem.IconSource>
<TabViewItem.Content>
<StackPanel Padding="12">
<TextBlock Text="Picture settings"
Style="{ThemeResource TitleTextBlockStyle}"/>
</StackPanel>
</TabViewItem.Content>
</TabViewItem>
<TabViewItem Header="Sound" IsClosable="False">
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Audio"/>
</TabViewItem.IconSource>
<TabViewItem.Content>
<StackPanel Padding="12">
<TextBlock Text="Sound settings"
Style="{ThemeResource TitleTextBlockStyle}"/>
</StackPanel>
</TabViewItem.Content>
</TabViewItem>
</TabView>
문서 탭
기본적으로 TabView는 문서 탭에 대해 구성됩니다. 사용자는 새 탭을 추가하고, 탭을 다시 정렬하고, 탭을 닫을 수 있습니다. 이 구성에서는 기능을 사용하도록 설정하려면 AddTabButtonClick 및 TabCloseRequested 이벤트를 처리해야 합니다.
탭이 TabView에 추가되면 탭 스트립에 표시할 탭이 너무 많을 수 있습니다. 이 경우 사용자가 탭 스트립을 왼쪽과 오른쪽으로 스크롤하여 숨겨진 탭에 액세스할 수 있는 스크롤 범퍼가 나타납니다.
이 예제에서는 탭 열기 및 닫기를 지원하기 위해 이벤트 처리기와 함께 간단한 TabView를 만듭니다.
TabView_AddTabButtonClick
이벤트 처리기는 코드에 TabViewItem을 추가하는 방법을 보여줍니다.
<TabView VerticalAlignment="Stretch"
AddTabButtonClick="TabView_AddTabButtonClick"
TabCloseRequested="TabView_TabCloseRequested">
<TabViewItem Header="Home" IsClosable="False">
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Home" />
</TabViewItem.IconSource>
<TabViewItem.Content>
<StackPanel Padding="12">
<TextBlock Text="TabView content"
Style="{ThemeResource TitleTextBlockStyle}"/>
</StackPanel>
</TabViewItem.Content>
</TabViewItem>
</TabView>
// Add a new tab to the TabView.
private void TabView_AddTabButtonClick(TabView sender, object args)
{
var newTab = new TabViewItem();
newTab.Header = $"New Document {sender.TabItems.Count}";
newTab.IconSource = new SymbolIconSource() { Symbol = Symbol.Document };
newTab.Content = new TextBlock() { Text = $"Content for new tab {sender.TabItems.Count}.",
Padding = new Thickness(12) };
sender.TabItems.Add(newTab);
sender.SelectedItem = newTab;
}
// Remove the requested tab from the TabView.
private void TabView_TabCloseRequested(TabView sender,
TabViewTabCloseRequestedEventArgs args)
{
sender.TabItems.Remove(args.Tab);
}
마지막 탭이 닫히면 창을 닫습니다.
앱의 모든 탭이 닫을 수 있는 상태며 마지막 탭이 닫힐 때 앱 창도 닫아야 한다면, TabCloseRequested 이벤트 처리기에서도 창을 닫아야 합니다.
먼저 App.xaml.cs
파일에서 public static
인스턴스에 접근할 수 있는 Window
속성을 추가하여 Page
에서 TabView를 호스팅합니다. ( 사용자 인터페이스 마이그레이션 참조)
public partial class App : Application
{
// ... code removed.
// Add this.
public static Window Window { get { return m_window; } }
// Update this to make it static.
private static Window m_window;
}
그런 다음 TabView에서 모든 탭이 제거된 경우 Window.Close를 호출하도록 TabCloseRequested 이벤트 처리기를 수정합니다.
// Remove the requested tab from the TabView.
// If all tabs have been removed, close the Window.
private void TabView_TabCloseRequested(TabView sender,
TabViewTabCloseRequestedEventArgs args)
{
sender.TabItems.Remove(args.Tab);
if (sender.TabItems.Count == 0)
{
App.Window.Close();
}
}
참고 항목
이 예제는 단일 창(MainWindow
)이 있는 앱에서 작동합니다. 앱에 창이 여러 개 있거나 탭 분리를 사용하도록 설정한 경우 창을 추적한 다음 닫을 올바른 창을 찾아야 합니다. 예제는 다음 섹션을 참조하세요.
탭 분리
Tab tear-out 은 사용자가 TabView의 탭 스트립 밖으로 탭을 끌어서 다른 TabView 컨트롤(일반적으로 새 창)으로 이동할 때 발생하는 상황에 대해 설명합니다.
Windows 앱 SDK 1.6부터 TabView에는 탭을 새 창으로 끌어다 놓기 위한 향상된 환경을 제공하도록 설정할 수 있는 CanTearOutTabs 속성이 있습니다. 사용자가 이 옵션을 사용하여 탭 스트립 밖으로 탭을 끌면 끌기 중에 새 창이 즉시 만들어지게 되므로 사용자가 화면 가장자리로 끌어 창을 최대화하거나 한 번의 부드러운 동작으로 스냅 할 수 있습니다. 또한 이 구현은 끌어서 놓기 API를 사용하지 않으므로 해당 API의 제한 사항에 영향을 받지 않습니다.
CanTearOutTabs 속성을 true
설정하면 드래그앤드롭 이벤트 대신 탭 분리 이벤트가 발생합니다. 탭 중단을 구현하려면 다음 이벤트를 처리해야 합니다.
-
이 이벤트는 탭을 탭 스트립 밖으로 처음 끌 때 발생합니다. 이를 처리하여 탭이 이동할 새 창 및 TabView를 만듭니다.
-
이 이벤트는 새 창이 제공된 후에 발생합니다. 원래 TabView에서 새 창의 TabView로 조각난 탭을 이동하도록 처리합니다.
-
이 이벤트는 조각난 탭을 기존 TabView 위로 끌 때 발생합니다. 탭의 수락 여부를 결정하기 위해 분리된 탭을 수신하는 TabView에서 이를 처리합니다.
-
이 이벤트는 조각난 탭이 기존 TabView 위로 드래그되고,
ExternalTornOutTabsDropping
이벤트가 드롭이 허용된 것으로 나타날 때 발생합니다. 제거된 탭을 수신하는 TabView에서 처리하여 원래 TabView에서 탭을 제거하고 지정된 인덱스에서 받는 TabView에 삽입합니다.
TabDragStarting, TabStripDragOver, TabStripDrop, TabDragCompleted, TabDroppedOutside 등 탭 해제를 사용하도록 설정하면 이러한 이벤트가 발생하지 않습니다.
주의
관리자 권한으로 실행되는 프로세스에서 Tab 해제가 지원됩니다.
다음 예제에서는 탭 중단을 지원하도록 이벤트 처리기를 구현하는 방법을 보여 줍니다.
TabView 설정
이 XAML은 CanTearOutTabs 속성을 true
로 설정하고 탭 분리 이벤트 핸들러를 구성합니다.
<TabView x:Name="tabView"
CanTearOutTabs="True"
TabTearOutWindowRequested="TabView_TabTearOutWindowRequested"
TabTearOutRequested="TabView_TabTearOutRequested"
ExternalTornOutTabsDropping="TabView_ExternalTornOutTabsDropping"
ExternalTornOutTabsDropped="TabView_ExternalTornOutTabsDropped">
<!-- TabView content -->
</TabView>
새 창 만들기 및 추적
탭 분리를 사용하려면 앱에서 새 창을 만들고 관리해야 합니다.
팁
WinUI 갤러리 앱에는 앱에서 WindowHelper
창을 더 쉽게 관리할 수 있는 클래스가 포함되어 있습니다. WinUI 갤러리 리포지토리: WindowHelper.cs GitHub에서 복사할 수 있습니다. 탭 분리를 구현하려면 이 도우미 클래스를 사용하는 것이 좋습니다. 사용되는 방법을 보려면 GitHub의 TabViewWindowingSamplePage 를 참조하세요.
이 문서에서는 WindowHelper.cs
에서 도우미 메서드를 복사하고, 가독성을 높이기 위해 수정 후 인라인으로 표시합니다.
여기서는 모든 활성 창을 추적하기 위한 목록이 만들어집니다 App.xaml.cs
.
OnLaunched
메서드가 생성된 후 창을 추적하도록 업데이트됩니다. (클래스를 사용하는 WindowHelper
경우에는 필요하지 않습니다.)
static public List<Window> ActiveWindows = new List<Window>();
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
m_window = new MainWindow();
// Track this window.
ActiveWindows.Add(m_window);
m_window.Activate();
}
탭 분리가 시작되면 새 창이 요청됩니다. 여기서 변수 tabTearOutWindow
는 새 창이 만들어진 후 새 창에 대한 액세스를 제공합니다. 도우미 메서드는 CreateWindow
TrackWindow
새 창을 만들고 활성 창 추적 목록에 추가합니다.
새 창을 만든 후에는 새 페이지를 만들고 창의 콘텐츠로 설정해야 합니다. 새로운 페이지에는 조각난 탭을 TabTearOutRequested
이벤트 처리기에서 이동시킬 TabView 컨트롤이 포함되어야 합니다.
팁
이 예제에서는 빈 TabView만 포함하므로 새 MainPage
클래스를 만듭니다(XAML에서 직접 추가되는 탭 없음). 조각난 창에 표시되지 않아야 하는 다른 UI 요소가 포함된 경우 MainPage
필요한 요소(적어도 TabView 포함)만 포함하는 별도의 페이지를 만들고 해당 페이지의 인스턴스를 만들 수 있습니다.
마지막으로 새 창의 AppWindow.Idargs.
속성에 할당합니다. 이 속성은 TabViewTabTearOutRequestedEventArgs.NewWindowId 속성에 사용되므로 해당 이벤트 처리기에서 창에 액세스할 수 있습니다.
private Window? tabTearOutWindow = null;
private void TabView_TabTearOutWindowRequested(TabView sender, TabViewTabTearOutWindowRequestedEventArgs args)
{
tabTearOutWindow = CreateWindow();
tabTearOutWindow.Content = new MainPage();
// Optional window setup, such as setting the icon or
// extending content into the title bar happens here.
args.NewWindowId = tabTearOutWindow.AppWindow.Id;
}
private Window CreateWindow()
{
Window newWindow = new Window
{
SystemBackdrop = new MicaBackdrop()
};
newWindow.Title = "Torn Out Window";
TrackWindow(newWindow);
return newWindow;
}
private void TrackWindow(Window window)
{
window.Closed += (sender, args) => {
App.ActiveWindows.Remove(window);
};
App.ActiveWindows.Add(window);
}
마지막 탭이 닫히면 창 닫기
앞에서 설명한 것처럼 TabView의 마지막 탭이 닫히면 창을 닫을 수 있습니다. 앱에 여러 창이 있는 경우 추적된 창 목록에서 닫을 올바른 창을 찾아야 합니다. 이 예제에서는 이 작업을 수행하는 방법을 보여줍니다.
// Remove the requested tab from the TabView.
// If all tabs have been removed, close the Window.
private void TabView_TabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs args)
{
sender.TabItems.Remove(args.Tab);
if (sender.TabItems.Count == 0)
{
GetWindowForElement(this)?.Close();
}
}
public Window? GetWindowForElement(UIElement element)
{
if (element.XamlRoot != null)
{
foreach (Window window in App.ActiveWindows)
{
if (element.XamlRoot == window.Content.XamlRoot)
{
return window;
}
}
}
return null;
}
탭을 새 창으로 이동
새 창이 제공되면 TabView에서 제거된 탭을 sender
제거하고 새 창의 TabView에 추가해야 합니다. 이 예에서는 도우미 메서드를 사용하여 원래 페이지 인스턴스에서 새로운 public AddTabToTabs
인스턴스를 통해 TabView에 접근함으로써 조각난 탭을 추가할 수 있습니다.
private void TabView_TabTearOutRequested(TabView sender, TabViewTabTearOutRequestedEventArgs args)
{
if (tabTearOutWindow?.Content is MainPage newPage
&& args.Tabs.FirstOrDefault() is TabViewItem tab)
{
sender.TabItems.Remove(tab);
newPage.AddTabToTabs(tab);
}
}
// This method provides access to the TabView from
// another page instance so you can add the torn-out tab.
public void AddTabToTabs(TabViewItem tab)
{
tabView.TabItems.Add(tab);
}
조각난 탭을 다른 TabView로 끌어다 놓기
이전 단계에 표시된 것처럼 탭이 찢어져 새 창에 배치된 경우 다음 두 가지 중 하나가 발생할 수 있습니다.
- 사용자는 탭을 삭제할 수 있으며 새 창에 남아 있습니다. 해체 프로세스는 여기서 종료되며 더 이상 이벤트가 발생하지 않습니다.
- 사용자는 계속해서 조각난 탭을 기존 TabView 컨트롤로 다시 끌 수 있습니다. 이 경우 프로세스가 계속되고 여러 이벤트가 더 발생하여 원래 TabView에서 탭을 제거하고 외부 탭을 기존 TabView에 삽입할 수 있습니다.
탭을 기존 TabView 위로 끌면 ExternalTornOutTabsDropping 이벤트가 발생합니다. 이벤트 처리기에서 이 TabView에 탭을 삽입할 수 있는지 여부를 확인할 수 있습니다. 대부분의 경우 args.
AllowDrop 속성을 설정하기만 하면 됩니다true
. 그러나 해당 속성을 설정하기 전에 검사를 수행해야 하는 경우 여기에서 수행할 수 있습니다. 만약 AllowDrop
가 false
으로 설정되면, 탭 끌기 작업은 계속되고 ExternalTornOutTabsDropped 이벤트가 발생하지 않습니다.
private void TabView_ExternalTornOutTabsDropping(TabView sender,
TabViewExternalTornOutTabsDroppingEventArgs args)
{
args.AllowDrop = true;
}
AllowDrop
이벤트 처리기에서 true
로 ExternalTornOutTabsDropping
이 설정되면, ExternalTornOutTabsDropped
이벤트가 즉시 발생합니다.
참고 항목
Dropped
이벤트 내 이름은 끌어서 놓기 API의 놓기 동작 개념과 직접 일치하지 않습니다. 여기서 사용자는 삭제 작업을 수행하기 위해 탭을 해제할 필요가 없습니다. 탭이 탭 스트립 위에 있는 동안 이벤트가 발생하고 TabView에 탭을 놓 기 위해 코드가 실행됩니다.
ExternalTornOutTabsDropped 이벤트 처리기는 TabTearOutRequested 이벤트와 동일한 패턴을 따르지만 반전됩니다. 원래 TabView에서 탭을 제거하고 TabView에 sender
삽입해야 합니다.
sender
TabView는 탭이 삽입되는 컨트롤이므로 도우미 메서드를 사용하여 GetParentTabView
원래 탭을 찾습니다. 조각난 TabViewItem으로 시작하고 VisualTreeHelper를 사용하여 시각적 트리를 걸어서 항목이 속한 TabView를 찾습니다. TabView를 찾은 후 TabViewItem은 TabItems 컬렉션에서 제거되고 sender
에서 TabItems
지정한 인덱스의 TabView args.
컬렉션에 삽입됩니다.
private void TabView_ExternalTornOutTabsDropped(TabView sender,
TabViewExternalTornOutTabsDroppedEventArgs args)
{
if (args.Tabs.FirstOrDefault() is TabViewItem tab)
{
GetParentTabView(tab)?.TabItems.Remove(tab);
sender.TabItems.Insert(args.DropIndex, tab);
}
}
// Starting with the TabViewItem, walk up the
// visual tree until you get to the TabView.
private TabView? GetParentTabView(TabViewItem tab)
{
DependencyObject current = tab;
while (current != null)
{
if (current is TabView tabView)
{
return tabView;
}
current = VisualTreeHelper.GetParent(current);
}
return null;
}
팁
Windows 커뮤니티 도구 키트를 사용하는 경우 대신 도구 키트의 FindAscendant
에서 도우미 메서드를 사용할 GetParentTabView
수 있습니다.
창의 제목 표시줄에 TabView 탭 표시
탭이 창의 제목 표시줄 아래에 있는 자신의 행을 차지하는 대신 두 탭을 동일한 영역으로 병합할 수 있습니다. 이렇게 하면 콘텐츠를 위한 세로 공간이 확보되고, 앱에 현대적인 느낌을 줄 수 있습니다.
사용자가 제목 표시줄로 창을 끌어 창 위치를 변경할 수 있으므로 제목 표시줄이 탭으로 완전히 채워지지 않는 것이 중요합니다. 따라서 제목 표시줄에 탭을 표시할 때 제목 표시줄의 일부를 끌기 가능한 영역으로 예약하도록 지정해야 합니다. 끌기 가능 영역을 지정하지 않으면 전체 제목 표시줄을 끌 수 있으므로 탭에서 입력 이벤트를 수신할 수 없습니다. TabView가 창의 제목 표시줄에 표시되는 경우 항상 TabView에 TabStripFooter를 포함하고 끌어서 끌 수 있는 영역으로 표시해야 합니다.
자세한 내용은 제목 표시줄 사용자 지정을 참조하세요.
<TabView VerticalAlignment="Stretch">
<TabViewItem Header="Home" IsClosable="False">
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Home" />
</TabViewItem.IconSource>
</TabViewItem>
<TabView.TabStripFooter>
<Grid x:Name="CustomDragRegion" Background="Transparent" />
</TabView.TabStripFooter>
</TabView>
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
App.Window.ExtendsContentIntoTitleBar = true;
App.Window.SetTitleBar(CustomDragRegion);
CustomDragRegion.MinWidth = 188;
}
참고 항목
창에 대한 참조를 얻는 방법은 앱에서 창을 추적하는 방법에 따라 달라질 수 있습니다. 자세한 내용은 마지막 탭이 닫혀 있을 때 창 닫기 및 이 문서의 새 창 만들기 및 추적을 참조하세요.
개발자를 위한 키보드 지침
팁
기본 제공 키보드 지원에 대한 자세한 내용은 이 문서의 앞부분에서 키보드 탐색 을 참조하세요.
일부 애플리케이션에는 보다 고급 키보드 제어가 필요할 수 있습니다. 앱에 적합한 경우 다음 바로 가기를 구현해 보세요.
경고
기존 앱에 TabView를 추가하는 경우 권장 TabView 바로 가기의 키 조합에 매핑되는 바로 가기 키를 이미 만들었을 수도 있습니다. 이 경우에는 기존 바로 가기를 유지할 것인지 아니면 사용자에게 직관적인 탭 환경을 제공할 것인지 고민해야 합니다.
- Ctrl+T 를 누르면 새 탭이 열립니다. 일반적으로 이 탭은 미리 정의된 문서로 채워지거나 콘텐츠를 선택하는 간단한 방법으로 빈 상태로 만들어집니다. 사용자가 새 탭의 콘텐츠를 선택해야 하는 경우 콘텐츠 선택 컨트롤에 입력 포커스를 제공하는 방법을 고려해 보세요.
- Ctrl+W 를 누르면 선택한 탭이 닫힙니다. TabView는 다음 탭을 자동으로 선택합니다.
- Ctrl + Shift + T 는 최근에 닫힌 탭을 열어야 합니다(또는 더 정확하게 말하면 최근에 닫힌 탭과 동일한 콘텐츠가 있는 새 탭을 엽니다). 가장 최근에 닫은 탭부터 시작하여 이후에 바로 가기가 호출될 때마다 뒤로 이동합니다. 이렇게 하려면 최근에 닫힌 탭 목록을 유지해야 합니다.
- Ctrl+1 은 탭 목록에서 첫 번째 탭을 선택해야 합니다. 마찬가지로 Ctrl + 2 키를 누르면 두 번째 탭이 선택되고, Ctrl + 3 키는 세 번째 탭, 이러한 식으로 Ctrl + 8까지 있습니다.
- Ctrl+9 는 목록에 있는 탭 수에 관계없이 탭 목록의 마지막 탭을 선택해야 합니다.
- 탭에서 닫기 명령 외에도 탭 복제, 탭 고정 등의 기능을 제공하는 경우 상황에 맞는 메뉴를 사용하여 탭에서 수행할 수 있는 모든 작업을 표시합니다.
브라우저 스타일의 키보드 동작 구현
이 예제에서는 위에서 설명한 여러 권장 사항을 TabView에서 구현합니다. 특히 이 예제에서는 Ctrl + T, Ctrl + W, Ctrl + 1-8 및 Ctrl + 9를 구현합니다.
<TabView>
<!-- ... some tabs ... -->
<TabView.KeyboardAccelerators>
<KeyboardAccelerator Key="T" Modifiers="Control"
Invoked="NewTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="W" Modifiers="Control"
Invoked="CloseSelectedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number1" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number2" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number3" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number4" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number5" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number6" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number7" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number8" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
<KeyboardAccelerator Key="Number9" Modifiers="Control"
Invoked="NavigateToNumberedTabKeyboardAccelerator_Invoked" />
</TabView.KeyboardAccelerators>
</TabView>
private void NewTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender,
KeyboardAcceleratorInvokedEventArgs args)
{
// Create new tab.
TabView senderTabView = (TabView)args.Element;
if (senderTabView is not null)
{
// (Click handler defined in previous example.)
TabView_AddTabButtonClick(senderTabView, new EventArgs());
}
args.Handled = true;
}
private void CloseSelectedTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender,
KeyboardAcceleratorInvokedEventArgs args)
{
TabView tabView = (TabView)args.Element;
TabViewItem tab = (TabViewItem)tabView.SelectedItem;
if (tab is not null)
{
CloseSelectedTab(tabView, tab);
}
args.Handled = true;
}
private void TabView_TabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs args)
{
CloseSelectedTab(sender, args.Tab);
}
private void CloseSelectedTab(TabView tabView, TabViewItem tab)
{
// Only remove the selected tab if it can be closed.
if (tab.IsClosable == true)
{
tabView.TabItems.Remove(tab);
}
}
private void NavigateToNumberedTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender,
KeyboardAcceleratorInvokedEventArgs args)
{
TabView tabView = (TabView)args.Element;
int tabToSelect = 0;
switch (sender.Key)
{
case Windows.System.VirtualKey.Number1:
tabToSelect = 0;
break;
case Windows.System.VirtualKey.Number2:
tabToSelect = 1;
break;
case Windows.System.VirtualKey.Number3:
tabToSelect = 2;
break;
case Windows.System.VirtualKey.Number4:
tabToSelect = 3;
break;
case Windows.System.VirtualKey.Number5:
tabToSelect = 4;
break;
case Windows.System.VirtualKey.Number6:
tabToSelect = 5;
break;
case Windows.System.VirtualKey.Number7:
tabToSelect = 6;
break;
case Windows.System.VirtualKey.Number8:
tabToSelect = 7;
break;
case Windows.System.VirtualKey.Number9:
// Select the last tab
tabToSelect = tabView.TabItems.Count - 1;
break;
}
// Only select the tab if it is in the list.
if (tabToSelect < tabView.TabItems.Count)
{
tabView.SelectedIndex = tabToSelect;
}
}
UWP 및 WinUI 2
중요합니다
이 문서의 정보 및 예제는 Windows 앱 SDK 및 WinUI 3를 사용하는 앱에 최적화되어 있지만 WinUI 2를 사용하는 UWP 앱에도 대체로 적용 가능합니다. 플랫폼별 정보 및 예제는 UWP API 참조를 참조하세요.
이 섹션에는 UWP 또는 WinUI 2 앱에서 컨트롤을 사용하는 데 필요한 정보가 포함되어 있습니다.
UWP 앱에 대한 TabView 컨트롤은 WinUI 2의 일부로 포함됩니다. 설치 지침을 비롯한 자세한 내용은 WinUI 2를 참조하세요. 이 컨트롤에 대한 API는 Microsoft.UI.Xaml.Controls 네임스페이스에 있습니다.
Tab Tear-out API는 WinUI 2 버전의 TabView에 포함되지 않습니다.
- WinUI 2 API:TabView 클래스, TabViewItem 클래스
- WinUI 2 갤러리 앱을 열고 TabView의 작동을 확인합니다. WinUI 2 갤러리 앱에는 대부분의 WinUI 2 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. Microsoft Store에서 앱을 다운로드하거나 GitHub에서 소스 코드를 가져오세요.
최신 WinUI 2 를 사용하여 모든 컨트롤에 대한 최신 스타일, 템플릿 및 기능을 가져오는 것이 좋습니다. WinUI 2.2 이상에는 둥근 모서리를 사용하는 이 컨트롤에 대한 새 템플릿이 포함되어 있습니다. 자세한 내용은 모서리 반경을 참조하세요.
이 문서의 코드를 WinUI 2와 함께 사용하려면 XAML의 별칭(여기서는 muxc
를 사용)을 사용하여 프로젝트에 포함된 Windows UI 라이브러리 API를 표현합니다. 자세한 내용은 WinUI 2 시작을 참조하세요.
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
<muxc:TabView />
관련된 문서
Windows developer