NavigationView

NavigationView 컨트롤은 앱에 대한 최상위 탐색을 제공합니다. 이 컨트롤은 다양한 화면 크기에 맞게 조정되고 ‘위쪽’ 및 ‘왼쪽’ 탐색 스타일을 둘 다 지원합니다.

top navigationleft navigation
NavigationView는 위쪽 및 왼쪽 탐색 창 또는 메뉴를 모두 지원

올바른 컨트롤인가요?

NavigationView는 다음 작업에 적합한 적응형 탐색 컨트롤입니다.

  • 앱 전체에 일관성 있는 탐색 환경 제공
  • 더 작은 창에서 화면 공간 유지
  • 여러 탐색 범주에 대한 액세스 구성

다른 탐색 패턴의 경우 탐색 디자인 기본 사항을 참조하세요.

UWP 및 WinUI 2

Important

이 문서의 정보 및 예제는 Windows 앱 SDKWinUI 3를 사용하는 앱에 최적화되어 있지만 일반적으로 WinUI 2를 사용하는 UWP 앱에 적용할 수 있습니다. 플랫폼별 정보 및 예제는 UWP API 참조를 확인하세요.

이 섹션에는 UWP 또는 WinUI 2 앱에서 컨트롤을 사용하는 데 필요한 정보가 있습니다.

UWP 앱용 NavigationView 컨트롤은 Windows UI 라이브러리 2의 일부로 포함됩니다. 설치 지침을 비롯한 자세한 내용은 Windows UI 라이브러리를 참조하세요. 이 컨트롤용 API는 Windows.UI.Xaml.ControlsMicrosoft.UI.Xaml.Controls 네임스페이스에 모두 존재합니다.

최신 WinUI 2를 사용하여 모든 컨트롤에 대한 최신 스타일, 템플릿 및 기능을 가져오는 것이 좋습니다. 위쪽계층적 탐색과 같은 NavigationView의 몇 가지 기능을 사용하려면 Windows 10 버전 1809(SDK 17763) 이상이나 Windows UI 라이브러리가 필요합니다.

이 문서의 코드를 WinUI 2와 함께 사용하려면 XAML의 별칭(여기서는 muxc 사용)을 사용하여 프로젝트에 포함된 Windows UI 라이브러리 API를 표현합니다. 자세한 내용은 WinUI 2 시작을 참조하세요.

xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

<muxc:NavigationView />

탐색 보기 만들기

WinUI 3 갤러리 앱에는 대부분의 WinUI 3 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. Microsoft Store에서 앱을 다운로드하거나 GitHub에서 소스 코드를 가져오세요.

이 예제에서는 XAML에서 간단한 탐색 보기를 만드는 방법을 보여줍니다.

<NavigationView>
    <NavigationView.MenuItems>
        <NavigationViewItem Content="Nav Item A"/>
        <NavigationViewItem Content="Nav Item B"/>
        <NavigationViewItem Content="Nav Item C"/>
    </NavigationView.MenuItems>

    <Frame x:Name="ContentFrame"/>
</NavigationView>

디스플레이 모드

PaneDisplayMode 속성을 사용하여 NavigationView에 대한 다른 탐색 스타일 또는 디스플레이 모드를 구성할 수 있습니다.

창이 콘텐츠 위에 배치됩니다.
PaneDisplayMode="Top"

Example of top navigation

다음과 같은 경우 ‘위쪽’ 탐색을 권장합니다.

  • 중요성이 같은 최상위 탐색 범주가 5개 이하이며 드롭다운 오버플로 메뉴에서 끝나는 추가적인 최상위 탐색 범주가 덜 중요한 것으로 간주되는 경우
  • 화면에 모든 탐색 옵션을 표시해야 하는 경우
  • 앱 콘텐츠에 더 많은 공간이 필요한 경우
  • 아이콘으로 앱의 탐색 범주를 명확하게 설명할 수 없는 경우

Left

창이 확장되고 콘텐츠 왼쪽에 배치됩니다.
PaneDisplayMode="Left"

Example of expanded left navigation pane

다음과 같은 경우 ‘왼쪽’ 탐색을 권장합니다.

  • 중요성이 같은 최상의 탐색 범주가 5-10개인 경우
  • 다른 앱 콘텐츠의 공간을 줄여 탐색 범주를 눈에 띄게 하려는 경우

LeftCompact

창은 열릴 때까지 아이콘만 표시하며 콘텐츠의 왼쪽에 배치됩니다. 열린 창은 콘텐츠를 오버레이합니다.
PaneDisplayMode="LeftCompact"

Example of compact left navigation pane

LeftMinimal

창이 열릴 때까지 메뉴 단추만 표시됩니다. 열린 창은 콘텐츠의 왼쪽을 오버레이합니다.
PaneDisplayMode="LeftMinimal"

Example of minimal left navigation pane

자동

기본적으로 PaneDisplayMode는 Auto로 설정되어 있습니다. Auto 모드에서 NavigationView는 창이 좁을 때 LeftMinimal 사이를 LeftCompact로 조정한 다음 창이 넓어지면 Left로 조정합니다. 자세한 내용은 적응형 동작 섹션을 참조하세요.

Left navigation default adaptive behavior
NavigationView 기본 적응형 동작

구조

이 이미지에서는 ‘위쪽’ 또는 ‘왼쪽’ 탐색용으로 구성되는 경우 컨트롤의 창, 헤더 및 콘텐츠 영역에 대한 레이아웃을 보여 줍니다.

Top NavigationView layout
위쪽 탐색 레이아웃

Left NavigationView layout
왼쪽 탐색 레이아웃

PaneDisplayMode 속성을 사용하여 창을 콘텐츠 위에 배치하거나 콘텐츠 왼쪽에 배치할 수 있습니다.

NavigationView 창은 다음을 포함할 수 있습니다.

왼쪽 창에는 다음도 포함됩니다.

  • 열린 창과 닫힌 창을 전환하는 메뉴 단추. 더 큰 앱 창에서 창이 열리면 IsPaneToggleButtonVisible 속성을 사용하여 이 단추를 숨기도록 선택할 수 있습니다.

NavigationView의 뒤로 단추는 창의 왼쪽 위 모서리에 배치됩니다. 그러나 탐색 보기는 뒤로 탐색을 자동으로 처리하지 않으며 뒤로 스택에 콘텐츠를 추가합니다. 뒤로 탐색을 사용하려면 뒤로 탐색 섹션을 참조하세요.

다음은 위쪽 및 왼쪽 창 위치에 대한 자세한 창 구조입니다.

위쪽 탐색 창

NavigationView top pane anatomy

  1. 헤더
  2. 탐색 항목
  3. 구분 기호
  4. AutoSuggestBox(선택 사항)
  5. 설정 단추(선택 사항)

왼쪽 탐색 창

NavigationView left pane anatomy

  1. 메뉴 단추
  2. 탐색 항목
  3. 구분 기호
  4. 헤더
  5. AutoSuggestBox(선택 사항)
  6. 설정 단추(선택 사항)

FooterMenuItems를 사용하면 창 시작 부분에 항목을 배치하는 MenuItems 속성과 달리 탐색 창 끝에 탐색 항목을 배치할 수 있습니다.

FooterMenuItems는 기본적으로 Settings 설정 항목 앞에 표시됩니다. IsSettingsVisible 속성을 사용하면 Settings 항목을 계속 설정/해제할 수 있습니다.

탐색 항목만 FooterMenuItems에 배치되어야 합니다. 창의 바닥글에 맞춰야 하는 다른 모든 콘텐츠는 PaneFooter에 배치해야 합니다.

NavigationView에 FooterMenuItems를 추가하는 방법에 대한 예는 FooterMenuItems 클래스를 참조하세요.

아래 이미지는 바닥글 메뉴에 계정, 카트도움말 탐색 항목이 있는 NavigationView를 보여줍니다.

A NavigationView with FooterMenuItems

자유 형식 콘텐츠를 PaneFooter 속성에 추가하여 창의 바닥글에 배치할 수 있습니다.

Pane footer top nav
위쪽 창 바닥글

Pane footer left nav
왼쪽 창 바닥글

창 제목 및 헤더

PaneTitle 속성을 설정하여 창 헤더 영역에 텍스트 콘텐츠를 배치할 수 있습니다. 문자열을 사용하고 메뉴 단추 옆에 있는 텍스트를 표시합니다.

이미지나 로고와 같은 텍스트가 아닌 콘텐츠를 추가하려면 해당 요소를 PaneHeader 속성에 추가하여 창 헤더에 요소를 배치할 수 있습니다.

PaneTitle 및 PaneHeader를 둘 다 설정하면 콘텐츠는 메뉴 단추 옆에 가로로 누적되며 PaneTitle이 메뉴 단추에 가장 가깝습니다.

Pane header top nav
위쪽 창 헤더

Pane header left nav
왼쪽 창 헤더

창 콘텐츠

자유 형식 콘텐츠를 PaneCustomContent 속성에 추가하여 창에 배치할 수 있습니다.

Pane custom content top nav
위쪽 창 사용자 지정 콘텐츠

Pane custom content left nav
왼쪽 창 사용자 지정 콘텐츠

Header 속성을 설정하여 페이지 제목을 추가할 수 있습니다.

Example of NavigationView header area
NavigationView 헤더

헤더 영역은 왼쪽 창 위치에서는 탐색 단추에 세로로 맞춰지며 위쪽 창에서는 창 아래에 배치됩니다. 헤더 영역에는 고정 높이 52px이 사용됩니다. 선택한 탐색 범주의 페이지 제목을 유지하는 것이 목적입니다. 헤더가 페이지 위쪽에 고정되고 콘텐츠 영역에 대한 스크롤 자르기 지점 역할을 합니다.

NavigationView가 Minimal 디스플레이 모드이면 항상 헤더가 표시됩니다. 더 큰 창 너비에 사용되는 다른 모드에서는 헤더를 숨기도록 선택할 수 있습니다. 헤더를 숨기려면 AlwaysShowHeader 속성을 false로 설정합니다.

콘텐츠

Example of NavigationView content area
NavigationView 콘텐츠

콘텐츠 영역은 선택한 탐색 범주에 대한 대부분의 정보가 표시되는 곳입니다.

NavigationView가 Minimal 모드이고 여백이 24px일 경우 콘텐츠 영역에 대해 12px 여백이 적당합니다.

적응형 동작

기본적으로 NavigationView는 사용 가능한 화면 공간에 따라 자동으로 디스플레이 모드를 변경합니다. CompactModeThresholdWidthExpandedModeThresholdWidth 속성은 디스플레이 모드가 변경되는 중단점을 지정합니다. 이 값을 수정하여 적응형 디스플레이 모드 동작을 사용자 지정할 수 있습니다.

기본값

PaneDisplayMode가 그 기본값 Auto로 설정되면 적응형 동작은 다음을 표시합니다.

  • 큰 창 너비(1008px 이상)의 확장된 왼쪽 창.
  • 중간 창 너비(641px-1007px)의 왼쪽, 아이콘 전용, 탐색 창(LeftCompact).
  • 작은 창 너비(640px 이하)의 메뉴 단추만(LeftMinimal.

적응형 동작 창 크기에 대한 자세한 내용은 화면 크기 및 중단점을 참조하세요.

Left navigation default adaptive behavior
NavigationView 기본 적응형 동작

최소

두 번째 일반적인 적응형 패턴은 큰 창 너비에서는 확장된 왼쪽 창을 사용하고 중간 및 작은 창 너비에서는 메뉴 단추만 사용하는 것입니다.

다음과 같은 경우에 권장됩니다.

  • 더 작은 창 너비에서 앱 콘텐츠에 더 많은 공간을 원하는 경우
  • 아이콘으로 탐색 범주를 명확하게 표시할 수 없는 경우

Left navigation minimal adaptive behavior
NavigationView "최소" 적응형 동작

이 동작을 구성하려면 CompactModeThresholdWidth를 창을 축소할 너비로 설정합니다. 여기서는 기본값 640부터 1007까지 변경됩니다. 또한 값이 충돌하지 않도록 ExpandedModeThresholdWidth를 설정해야 합니다.

<NavigationView CompactModeThresholdWidth="1007" ExpandedModeThresholdWidth="1007"/>

Compact

세 번째 일반적인 적응형 패턴은 큰 창 너비에서는 확장된 왼쪽 창을 사용하고 중간 및 작은 창 너비에서는 LeftCompact, 아이콘 전용, 탐색 창을 사용하는 것입니다.

다음과 같은 경우에 권장됩니다.

  • 항상 화면에 모든 탐색 옵션을 표시하는 것이 중요합니다.
  • 아이콘으로 탐색 범주를 명확하게 표시할 수 있습니다.

Left navigation compact adaptive behavior
NavigationView "컴팩트" 적응형 동작

이 동작을 구성하려면 CompactModeThresholdWidth를 0으로 설정합니다.

<NavigationView CompactModeThresholdWidth="0"/>

적응형 동작 없음

자동 적응형 동작을 사용하지 않으려면 PaneDisplayMode를 Auto 이외의 값으로 설정합니다. 여기서는 LeftMinimal로 설정되므로 창 너비에 관계없이 메뉴 단추만 표시됩니다.

Left navigation no adaptive behavior
PaneDisplayMode가 LeftMinimal로 설정된 NavigationView

<NavigationView PaneDisplayMode="LeftMinimal" />

앞서 ‘디스플레이 모드’ 섹션에서 설명한 대로 창이 항상 위쪽에 있거나, 항상 확장되거나, 항상 컴팩트하거나, 항상 최소화되도록 설정할 수 있습니다. 앱 코드에서 직접 디스플레이 모드를 관리할 수도 있습니다. 이 예제는 다음 섹션에 나와 있습니다.

위쪽에서 왼쪽으로 탐색

앱에서 위쪽 탐색을 사용하면 창 너비가 감소하면서 탐색 항목이 오버플로 메뉴로 축소됩니다. 앱 창이 좁은 경우에는 모든 항목이 오버플로 메뉴로 축소되게 하지 않고 Top에서 LeftMinimal 탐색으로 PaneDisplayMode를 전환하도록 향상된 사용자 환경을 제공할 수 있습니다.

다음과 같은 경우에는 큰 창 크기에서 위쪽 탐색을 사용하고 작은 창 크기에서 왼쪽 탐색을 사용하는 것이 좋습니다.

  • 중요성이 같은 최상위 탐색 범주 세트를 함께 표시하려는 경우, 이 세트의 한 범주가 화면에 맞춰지지 않으면 왼쪽 탐색으로 축소하여 같은 중요성을 부여합니다.
  • 작은 창 크기에서 가능한 한 많은 콘텐츠 공간을 유지하려고 합니다.

이 예제에서는 VisualStateManagerAdaptiveTrigger.MinWindowWidth 속성을 사용하여 TopLeftMinimal 탐색 사이에서 전환하는 방법을 보여 줍니다.

Example of top or left adaptive behavior 1

<Grid>
    <NavigationView x:Name="NavigationViewControl" >
        <NavigationView.MenuItems>
            <NavigationViewItem Content="A" x:Name="A" />
            <NavigationViewItem Content="B" x:Name="B" />
            <NavigationViewItem Content="C" x:Name="C" />
        </NavigationView.MenuItems>
    </NavigationView>

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState>
                <VisualState.StateTriggers>
                    <AdaptiveTrigger
                        MinWindowWidth="{x:Bind NavigationViewControl.CompactModeThresholdWidth}" />
                </VisualState.StateTriggers>

                <VisualState.Setters>
                    <Setter Target="NavigationViewControl.PaneDisplayMode" Value="Top"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

AdaptiveTrigger.MinWindowWidth를 사용하는 경우 창이 지정된 최소 너비보다 넓으면 시각적 상태가 트리거됩니다. 이는 기본 XAML은 좁은 창을 정의하며 VisualState는 창이 넓어질 때 적용되는 수정 사항을 정의함을 의미합니다. NavigationView의 기본 PaneDisplayMode는 [자동]이므로, 창 너비가 CompactModeThresholdWidth 이하일 경우 LeftMinimal 탐색이 사용됩니다. 창이 넓어지면 VisualState는 기본값을 재정의하며 Top 탐색이 사용됩니다.

NavigationView는 탐색 작업을 자동으로 수행하지 않습니다. 사용자가 탐색 항목을 탭하면 NavigationView는 해당 항목을 선택된 상태로 표시하고 ItemInvoked 이벤트를 발생시킵니다. 탭하면 새 항목이 선택되는 경우 SelectionChanged 이벤트도 발생합니다.

요청된 탐색과 관련된 작업을 수행하도록 한쪽 이벤트를 처리할 수 있습니다. 처리해야 하는 이벤트는 앱에서 필요한 동작에 따라 달라집니다. 일반적으로 요청된 페이지로 이동하여 해당 이벤트에 대한 응답으로 NavigationView 헤더를 업데이트합니다.

  • ItemInvoked는 이미 선택된 경우에도 사용자가 탐색 항목을 탭하면 항상 발생합니다. (마우스, 키보드 또는 기타 입력을 사용하여 동일한 동작으로 항목을 호출할 수도 있습니다. 자세한 내용은 입력 및 상호 작용을 참조하세요.) ItemInvoked 처리기에서 탐색하는 경우 기본적으로 페이지가 다시 로드되고 중복 항목이 탐색 스택에 추가됩니다. 항목이 호출될 때 탐색하는 경우 페이지 다시 로드를 허용하지 않거나, 페이지가 다시 로드될 때 탐색 백스택에서 중복 입력이 생성되지 않는지 확인해야 합니다. 코드 예제를 참조하세요.
  • SelectionChanged는 사용자가 현재 선택되지 않은 항목을 호출하거나 선택된 항목을 프로그래밍 방식으로 변경하여 발생할 수 있습니다. 사용자가 항목을 호출하여 선택이 변경되는 경우에는 ItemInvoked 이벤트가 먼저 발생합니다. 선택이 프로그래밍 방식으로 변경되면 ItemInvoked가 발생하지 않습니다.

모든 탐색 항목은 MenuItems의 일부이든 FooterMenuItems의 일부이든 동일한 선택 모델의 일부입니다. 한 번에 하나의 탐색 항목만 선택할 수 있습니다.

뒤로 탐색

NavigationView에는 기본 제공 뒤로 단추가 있지만, 앞으로 탐색처럼 뒤로 탐색이 자동으로 수행되는지 않습니다. 사용자가 뒤로 단추를 탭하면 BackRequested 이벤트가 발생합니다. 뒤로 탐색을 수행하도록 이 이벤트를 처리합니다. 자세한 내용 및 코드 예제는 탐색 기록 뒤로 탐색을 참조하세요.

Minimal 또는 Compact 모드에서는 NavigationView Pane이 플라이아웃으로 열립니다. 이 경우 뒤로 단추를 클릭하면 Pane이 닫히고 대신 PaneClosing 이벤트가 발생합니다.

다음 속성을 설정하여 뒤로 단추를 숨기거나 사용하지 않을 수 있습니다.

  • IsBackButtonVisible: 뒤로 단추를 표시하고 숨기는 데 사용합니다. 이 속성은 NavigationViewBackButtonVisible 열거형 값을 사용하며 기본적으로 Auto으로 설정됩니다. 단추가 축소되면 레이아웃에서 단추 공간이 예약되지 않습니다.
  • IsBackEnabled: 뒤로 단추를 사용하거나 사용하지 않도록 설정하는 데 사용합니다. 이 속성을 탐색 프레임의 CanGoBack 속성에 데이터 바인딩할 수 있습니다. IsBackEnabledfalse이면 BackRequested가 발생하지 않습니다.

NavigationView back button in the left navigation pane
왼쪽 탐색 창의 뒤로 단추

NavigationView back button in the top navigation pane
위쪽 탐색 창의 뒤로 단추

코드 예

이 예제는 NavigationView를 큰 창 크기의 위쪽 탐색 창과 작은 창 크기의 왼쪽 탐색 창과 함께 사용하는 방법을 보여줍니다. VisualStateManager에서 ‘위쪽’ 탐색 설정을 제거하여 왼쪽 전용 탐색에 맞게 조정할 수 있습니다.

이 예제는 다양한 시나리오에 적합한 탐색 데이터를 설정하는 일반적인 방법을 보여줍니다. 이 예제에서는 먼저 탐색하려는 페이지의 전체 형식 이름을 NavigationViewItem 태그에 저장합니다. 이벤트 처리기에서 해당 값을 unboxing하고 형식(C#) 또는 Windows::UI::Xaml::Interop::TypeName(C++/WinRT) 개체로 전환한 다음, 이 개체를 사용하여 대상 페이지로 이동합니다. 이렇게 단위 테스트를 만들어 태그 내의 값이 유효한 형식인지 확인할 수 있습니다. (C++/WinRT를 사용해 값을 IInspectable로 boxing 및 unboxing도 참조하세요.) 또한 NavigationView의 뒤로 단추를 사용하여 뒤로 탐색을 구현하는 방법도 보여줍니다.

이 코드는 HomePage, AppsPage, GamesPage, MusicPage, MyContentPageSettingsPage로 이동할 다음 이름을 가진 페이지가 앱에 포함되어 있다고 가정합니다. 이와 같은 페이지에 대한 코드는 표시되지 않습니다.

<Page ... >
 <Grid>
     <NavigationView x:Name="NavView"
                     Loaded="NavView_Loaded"
                     ItemInvoked="NavView_ItemInvoked"
                     BackRequested="NavView_BackRequested">
         <NavigationView.MenuItems>
             <NavigationViewItem Tag="NavigationViewDemo.HomePage" Icon="Home" Content="Home"/>
             <NavigationViewItemSeparator/>
             <NavigationViewItemHeader x:Name="MainPagesHeader"
                                       Content="Main pages"/>
             <NavigationViewItem Tag="NavigationViewDemo.AppsPage" Content="Apps">
                 <NavigationViewItem.Icon>
                     <FontIcon Glyph="&#xEB3C;"/>
                 </NavigationViewItem.Icon>
             </NavigationViewItem>
             <NavigationViewItem Tag="NavigationViewDemo.GamesPage" Content="Games">
                 <NavigationViewItem.Icon>
                     <FontIcon Glyph="&#xE7FC;"/>
                 </NavigationViewItem.Icon>
             </NavigationViewItem>
             <NavigationViewItem Tag="NavigationViewDemo.MusicPage" Icon="Audio" Content="Music"/>
         </NavigationView.MenuItems>

         <NavigationView.AutoSuggestBox>
             <!-- See AutoSuggestBox documentation for
              more info about how to implement search. -->
             <AutoSuggestBox x:Name="NavViewSearchBox" QueryIcon="Find"/>
         </NavigationView.AutoSuggestBox>

         <ScrollViewer>
             <Frame x:Name="ContentFrame" IsTabStop="True"
                NavigationFailed="ContentFrame_NavigationFailed"/>
         </ScrollViewer>
     </NavigationView>

     <VisualStateManager.VisualStateGroups>
         <VisualStateGroup>
             <VisualState>
                 <VisualState.StateTriggers>
                     <AdaptiveTrigger
                     MinWindowWidth="{x:Bind NavViewCompactModeThresholdWidth}"/>
                 </VisualState.StateTriggers>
                 <VisualState.Setters>
                     <!-- Remove the next 3 lines for left-only navigation. -->
                     <Setter Target="NavView.PaneDisplayMode" Value="Top"/>
                     <Setter Target="NavViewSearchBox.Width" Value="200"/>
                     <Setter Target="MainPagesHeader.Visibility" Value="Collapsed"/>
                     <!-- Leave the next line for left-only navigation. -->
                     <Setter Target="ContentFrame.Padding" Value="24,0,24,24"/>
                 </VisualState.Setters>
             </VisualState>
         </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
 </Grid>
</Page>
private double NavViewCompactModeThresholdWidth { get { return NavView.CompactModeThresholdWidth; } }

private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
    throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}

private void NavView_Loaded(object sender, RoutedEventArgs e)
{
    // You can also add items in code.
    NavView.MenuItems.Add(new NavigationViewItemSeparator());
    NavView.MenuItems.Add(new NavigationViewItem
    {
        Content = "My content",
        Icon = new SymbolIcon((Symbol)0xF1AD),
        Tag = "NavigationViewDemo.MyContentPage"
    });

    // Add handler for ContentFrame navigation.
    ContentFrame.Navigated += On_Navigated;

    // NavView doesn't load any page by default, so load home page.
    NavView.SelectedItem = NavView.MenuItems[0];
    // If navigation occurs on SelectionChanged, this isn't needed.
    // Because we use ItemInvoked to navigate, we need to call Navigate
    // here to load the home page.
    NavView_Navigate(typeof(HomePage), new EntranceNavigationTransitionInfo());
}

private void NavView_ItemInvoked(NavigationView sender,
                                 NavigationViewItemInvokedEventArgs args)
{
    if (args.IsSettingsInvoked == true)
    {
        NavView_Navigate(typeof(SettingsPage), args.RecommendedNavigationTransitionInfo);
    }
    else if (args.InvokedItemContainer != null)
    {
        Type navPageType = Type.GetType(args.InvokedItemContainer.Tag.ToString());
        NavView_Navigate(navPageType, args.RecommendedNavigationTransitionInfo);
    }
}

// NavView_SelectionChanged is not used in this example, but is shown for completeness.
// You will typically handle either ItemInvoked or SelectionChanged to perform navigation,
// but not both.
private void NavView_SelectionChanged(NavigationView sender,
                                      NavigationViewSelectionChangedEventArgs args)
{
    if (args.IsSettingsSelected == true)
    {
        NavView_Navigate(typeof(SettingsPage), args.RecommendedNavigationTransitionInfo);
    }
    else if (args.SelectedItemContainer != null)
    {
        Type navPageType = Type.GetType(args.SelectedItemContainer.Tag.ToString());
        NavView_Navigate(navPageType, args.RecommendedNavigationTransitionInfo);
    }
}

private void NavView_Navigate(
    Type navPageType,
    NavigationTransitionInfo transitionInfo)
{
    // Get the page type before navigation so you can prevent duplicate
    // entries in the backstack.
    Type preNavPageType = ContentFrame.CurrentSourcePageType;

    // Only navigate if the selected page isn't currently loaded.
    if (navPageType is not null && !Type.Equals(preNavPageType, navPageType))
    {
        ContentFrame.Navigate(navPageType, null, transitionInfo);
    }
}

private void NavView_BackRequested(NavigationView sender,
                                   NavigationViewBackRequestedEventArgs args)
{
    TryGoBack();
}

private bool TryGoBack()
{
    if (!ContentFrame.CanGoBack)
        return false;

    // Don't go back if the nav pane is overlayed.
    if (NavView.IsPaneOpen &&
        (NavView.DisplayMode == NavigationViewDisplayMode.Compact ||
         NavView.DisplayMode == NavigationViewDisplayMode.Minimal))
        return false;

    ContentFrame.GoBack();
    return true;
}

private void On_Navigated(object sender, NavigationEventArgs e)
{
    NavView.IsBackEnabled = ContentFrame.CanGoBack;

    if (ContentFrame.SourcePageType == typeof(SettingsPage))
    {
        // SettingsItem is not part of NavView.MenuItems, and doesn't have a Tag.
        NavView.SelectedItem = (NavigationViewItem)NavView.SettingsItem;
        NavView.Header = "Settings";
    }
    else if (ContentFrame.SourcePageType != null)
    {
        // Select the nav view item that corresponds to the page being navigated to.
        NavView.SelectedItem = NavView.MenuItems
                    .OfType<NavigationViewItem>()
                    .First(i => i.Tag.Equals(ContentFrame.SourcePageType.FullName.ToString()));

        NavView.Header =
            ((NavigationViewItem)NavView.SelectedItem)?.Content?.ToString();

    }
}
// MainPage.idl
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
    ...
    Double NavViewCompactModeThresholdWidth{ get; };
}

// pch.h
...
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Media.Animation.h>


// MainPage.h
#pragma once

#include "MainPage.g.h"

namespace muxc
{
    using namespace winrt::Microsoft::UI::Xaml::Controls;
};

namespace winrt::NavigationViewDemo::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        double NavViewCompactModeThresholdWidth();
        void ContentFrame_NavigationFailed(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::Navigation::NavigationFailedEventArgs const& args);
        void NavView_Loaded(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::RoutedEventArgs const& /* args */);
        void NavView_ItemInvoked(
            Windows::Foundation::IInspectable const& /* sender */,
            muxc::NavigationViewItemInvokedEventArgs const& args);

        // NavView_SelectionChanged is not used in this example, but is shown for completeness.
        // You'll typically handle either ItemInvoked or SelectionChanged to perform navigation,
        // but not both.
        void NavView_SelectionChanged(
            muxc::NavigationView const& /* sender */,
            muxc::NavigationViewSelectionChangedEventArgs const& args);
        void NavView_Navigate(
            Windows::UI::Xaml::Interop::TypeName navPageType,
            Microsoft::UI::Xaml::Media::Animation::NavigationTransitionInfo const& transitionInfo);
        void NavView_BackRequested(
            muxc::NavigationView const& /* sender */,
            muxc::NavigationViewBackRequestedEventArgs const& /* args */);
        void On_Navigated(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& args);
        bool TryGoBack();

    private:

    };
}

namespace winrt::NavigationViewDemo::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

// MainPage.cpp
#include "pch.h"
#include "MainPage.xaml.h"
#if __has_include("MainPage.g.cpp")
#include "MainPage.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;

namespace winrt::NavigationViewDemo::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }

    double MainPage::NavViewCompactModeThresholdWidth()
    {
        return NavView().CompactModeThresholdWidth();
    }

    void MainPage::ContentFrame_NavigationFailed(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::Navigation::NavigationFailedEventArgs const& args)
    {
        throw winrt::hresult_error(
            E_FAIL, winrt::hstring(L"Failed to load Page ") + args.SourcePageType().Name);
    }

    void MainPage::NavView_Loaded(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::RoutedEventArgs const& /* args */)
    {
        // You can also add items in code.
        NavView().MenuItems().Append(muxc::NavigationViewItemSeparator());
        muxc::NavigationViewItem navigationViewItem;
        navigationViewItem.Content(winrt::box_value(L"My content"));
        navigationViewItem.Icon(muxc::SymbolIcon(static_cast<muxc::Symbol>(0xF1AD)));
        navigationViewItem.Tag(winrt::box_value(L"NavigationViewDemo.MyContentPage"));
        NavView().MenuItems().Append(navigationViewItem);

        // Add handler for ContentFrame navigation.
        ContentFrame().Navigated({ this, &MainPage::On_Navigated });

        // NavView doesn't load any page by default, so load home page.
        NavView().SelectedItem(NavView().MenuItems().GetAt(0));
        // If navigation occurs on SelectionChanged, then this isn't needed.
        // Because we use ItemInvoked to navigate, we need to call Navigate
        // here to load the home page.
        NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::HomePage>(),
            Microsoft::UI::Xaml::Media::Animation::EntranceNavigationTransitionInfo());
    }

    void MainPage::NavView_ItemInvoked(
        Windows::Foundation::IInspectable const& /* sender */,
        muxc::NavigationViewItemInvokedEventArgs const& args)
    {
        if (args.IsSettingsInvoked())
        {
            NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::SettingsPage>(),
                args.RecommendedNavigationTransitionInfo());
        }
        else if (args.InvokedItemContainer())
        {
            Windows::UI::Xaml::Interop::TypeName pageTypeName;
            pageTypeName.Name = unbox_value<hstring>(args.InvokedItemContainer().Tag());
            pageTypeName.Kind = Windows::UI::Xaml::Interop::TypeKind::Primitive;
            NavView_Navigate(pageTypeName, args.RecommendedNavigationTransitionInfo());
        }
    }

    // NavView_SelectionChanged is not used in this example, but is shown for completeness.
    // You will typically handle either ItemInvoked or SelectionChanged to perform navigation,
    // but not both.
    void MainPage::NavView_SelectionChanged(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewSelectionChangedEventArgs const& args)
    {
        if (args.IsSettingsSelected())
        {
            NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::SettingsPage>(),
                args.RecommendedNavigationTransitionInfo());
        }
        else if (args.SelectedItemContainer())
        {
            Windows::UI::Xaml::Interop::TypeName pageTypeName;
            pageTypeName.Name = unbox_value<hstring>(args.SelectedItemContainer().Tag());
            pageTypeName.Kind = Windows::UI::Xaml::Interop::TypeKind::Primitive;
            NavView_Navigate(pageTypeName, args.RecommendedNavigationTransitionInfo());
        }
    }

    void MainPage::NavView_Navigate(
        Windows::UI::Xaml::Interop::TypeName navPageType,
        Microsoft::UI::Xaml::Media::Animation::NavigationTransitionInfo const& transitionInfo)
    {
        // Get the page type before navigation so you can prevent duplicate
        // entries in the backstack.
        Windows::UI::Xaml::Interop::TypeName preNavPageType =
            ContentFrame().CurrentSourcePageType();

        // Navigate only if the selected page isn't currently loaded.
        if (navPageType.Name != L"" && preNavPageType.Name != navPageType.Name)
        {
            ContentFrame().Navigate(navPageType, nullptr, transitionInfo);
        }
    }

    void MainPage::NavView_BackRequested(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewBackRequestedEventArgs const& /* args */)
    {
        TryGoBack();
    }

    bool MainPage::TryGoBack()
    {
        if (!ContentFrame().CanGoBack())
            return false;
        // Don't go back if the nav pane is overlayed.
        if (NavView().IsPaneOpen() &&
            (NavView().DisplayMode() == muxc::NavigationViewDisplayMode::Compact ||
                NavView().DisplayMode() == muxc::NavigationViewDisplayMode::Minimal))
            return false;
        ContentFrame().GoBack();
        return true;
    }

    void MainPage::On_Navigated(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& args)
    {
        NavView().IsBackEnabled(ContentFrame().CanGoBack());

        if (ContentFrame().SourcePageType().Name ==
            winrt::xaml_typename<NavigationViewDemo::SettingsPage>().Name)
        {
            // SettingsItem is not part of NavView.MenuItems, and doesn't have a Tag.
            NavView().SelectedItem(NavView().SettingsItem().as<muxc::NavigationViewItem>());
            NavView().Header(winrt::box_value(L"Settings"));
        }
        else if (ContentFrame().SourcePageType().Name != L"")
        {
            for (auto&& eachMenuItem : NavView().MenuItems())
            {
                auto navigationViewItem =
                    eachMenuItem.try_as<muxc::NavigationViewItem>();
                {
                    if (navigationViewItem)
                    {
                        winrt::hstring hstringValue =
                            winrt::unbox_value_or<winrt::hstring>(
                                navigationViewItem.Tag(), L"");
                        if (hstringValue == ContentFrame().SourcePageType().Name)
                        {
                            NavView().SelectedItem(navigationViewItem);
                            NavView().Header(navigationViewItem.Content());
                        }
                    }
                }
            }
        }
    }
}

계층적 탐색

일부 앱은 단순한 탐색 항목 목록 이상을 필요로 하는 더 복잡한 계층 구조를 포함할 수 있습니다. 최상위 탐색 항목을 사용하여 특정 페이지를 표시하는 자식 항목을 포함하는 페이지 범주를 표시할 수도 있습니다. 다른 페이지로만 연결되는 허브 스타일 페이지가 있는 경우에도 유용합니다. 이러한 경우 계층적 NavigationView를 만들어야 합니다.

창에서 중첩된 탐색 항목의 계층적 목록을 표시하려면 NavigationViewItemMenuItems 속성 또는 MenuItemsSource 속성을 사용합니다. 각 NavigationViewItem은 다른 NavigationViewItems를 포함하고 항목 헤더 및 구분 기호와 같은 요소를 구성할 수 있습니다. MenuItemsSource를 사용할 때 계층적 목록을 표시하려면 ItemTemplate을 NavigationViewItem으로 설정하고 해당 MenuItemsSource 속성을 계층의 다음 수준에 바인딩합니다.

NavigationViewItem은 중첩된 수준을 원하는 만큼 포함할 수 있지만, 앱의 탐색 계층 구조를 단순하게 유지하는 것이 좋습니다. 두 수준이 유용성과 이해력에 이상적입니다.

NavigationView는 Top, LeftLeftCompact 창 표시 모드로 계층 구조를 표시합니다. 다음은 각 창 표시 모드에서 확장된 하위 트리가 표시되는 모양입니다.

NavigationView with Hierarchy

태그에 항목의 계층 구조 추가

이 예제는 XAML 태그에서 계층적 앱 탐색을 선언하는 방법을 보여줍니다.

<NavigationView>
    <NavigationView.MenuItems>
        <NavigationViewItem Content="Home" Icon="Home" ToolTipService.ToolTip="Home"/>
        <NavigationViewItem Content="Collections" Icon="Keyboard" ToolTipService.ToolTip="Collections">
            <NavigationViewItem.MenuItems>
                <NavigationViewItem Content="Notes" Icon="Page" ToolTipService.ToolTip="Notes"/>
                <NavigationViewItem Content="Mail" Icon="Mail" ToolTipService.ToolTip="Mail"/>
            </NavigationViewItem.MenuItems>
        </NavigationViewItem>
    </NavigationView.MenuItems>
</NavigationView>

데이터 바인딩을 사용하여 항목의 계층 구조 추가

다음을 통해 메뉴 항목의 계층 구조를 NavigationView에 추가

  • MenuItemsSource 속성을 계층적 데이터에 바인딩
  • 항목 템플릿을 NavigationViewMenuItem으로 정의하고, 해당 콘텐츠는 메뉴 항목의 레이블로 설정 및 MenuItemsSource 속성은 계층 구조의 다음 수준에 바인딩됨

또한 이 예에서는 ExpandingCollapsed 이벤트를 설명합니다. 이러한 이벤트는 자식 항목을 포함하는 메뉴 항목에 대해 발생합니다.

<Page ... >
    <Page.Resources>
        <DataTemplate x:Key="NavigationViewMenuItem" x:DataType="local:Category">
            <NavigationViewItem Content="{x:Bind Name}" MenuItemsSource="{x:Bind Children}"/>
        </DataTemplate>
    </Page.Resources>

    <Grid>
        <NavigationView x:Name="navview"
    MenuItemsSource="{x:Bind Categories, Mode=OneWay}"
    MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
    ItemInvoked="{x:Bind OnItemInvoked}"
    Expanding="OnItemExpanding"
    Collapsed="OnItemCollapsed"
    PaneDisplayMode="Left">
            <StackPanel Margin="10,10,0,0">
                <TextBlock Margin="0,10,0,0" x:Name="ExpandingItemLabel" Text="Last Expanding: N/A"/>
                <TextBlock x:Name="CollapsedItemLabel" Text="Last Collapsed: N/A"/>
            </StackPanel>
        </NavigationView>
    </Grid>
</Page>
public class Category
{
    public String Name { get; set; }
    public String CategoryIcon { get; set; }
    public ObservableCollection<Category> Children { get; set; }
}

public sealed partial class HierarchicalNavigationViewDataBinding : Page
{
    public HierarchicalNavigationViewDataBinding()
    {
        this.InitializeComponent();
    }

    public ObservableCollection<Category> Categories = new ObservableCollection<Category>()
    {
        new Category(){
            Name = "Menu item 1",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 2",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() {
                            Name  = "Menu item 3",
                            CategoryIcon = "Icon",
                            Children = new ObservableCollection<Category>() {
                                new Category() { Name  = "Menu item 4", CategoryIcon = "Icon" },
                                new Category() { Name  = "Menu item 5", CategoryIcon = "Icon" }
                            }
                        }
                    }
                }
            }
        },
        new Category(){
            Name = "Menu item 6",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 7",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() { Name  = "Menu item 8", CategoryIcon = "Icon" },
                        new Category() { Name  = "Menu item 9", CategoryIcon = "Icon" }
                    }
                }
            }
        },
        new Category(){ Name = "Menu item 10", CategoryIcon = "Icon" }
    };

    private void OnItemInvoked(object sender, NavigationViewItemInvokedEventArgs e)
    {
        var clickedItem = e.InvokedItem;
        var clickedItemContainer = e.InvokedItemContainer;
    }
    private void OnItemExpanding(object sender, NavigationViewItemExpandingEventArgs e)
    {
        var nvib = e.ExpandingItemContainer;
        var name = "Last expanding: " + nvib.Content.ToString();
        ExpandingItemLabel.Text = name;
    }
    private void OnItemCollapsed(object sender, NavigationViewItemCollapsedEventArgs e)
    {
        var nvib = e.CollapsedItemContainer;
        var name = "Last collapsed: " + nvib.Content;
        CollapsedItemLabel.Text = name;
    }
}
// Category.idl
namespace HierarchicalNavigationViewDataBinding
{
    runtimeclass Category
    {
        String Name;
        String CategoryIcon;
        Windows.Foundation.Collections.IObservableVector<Category> Children;
    }
}

// Category.h
#pragma once
#include "Category.g.h"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    struct Category : CategoryT<Category>
    {
        Category();
        Category(winrt::hstring name,
            winrt::hstring categoryIcon,
            Windows::Foundation::Collections::
                IObservableVector<HierarchicalNavigationViewDataBinding::Category> children);

        winrt::hstring Name();
        void Name(winrt::hstring const& value);
        winrt::hstring CategoryIcon();
        void CategoryIcon(winrt::hstring const& value);
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> Children();
        void Children(Windows::Foundation::Collections:
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> const& value);

    private:
        winrt::hstring m_name;
        winrt::hstring m_categoryIcon;
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> m_children;
    };
}

// Category.cpp
#include "pch.h"
#include "Category.h"
#include "Category.g.cpp"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    Category::Category()
    {
        m_children = winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    }

    Category::Category(
        winrt::hstring name,
        winrt::hstring categoryIcon,
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> children)
    {
        m_name = name;
        m_categoryIcon = categoryIcon;
        m_children = children;
    }

    hstring Category::Name()
    {
        return m_name;
    }

    void Category::Name(hstring const& value)
    {
        m_name = value;
    }

    hstring Category::CategoryIcon()
    {
        return m_categoryIcon;
    }

    void Category::CategoryIcon(hstring const& value)
    {
        m_categoryIcon = value;
    }

    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
        Category::Children()
    {
        return m_children;
    }

    void Category::Children(
        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
            const& value)
    {
        m_children = value;
    }
}

// MainPage.idl
import "Category.idl";

namespace HierarchicalNavigationViewDataBinding
{
    [default_interface]
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        Windows.Foundation.Collections.IObservableVector<Category> Categories{ get; };
    }
}

// MainPage.h
#pragma once

#include "MainPage.g.h"

namespace muxc
{
    using namespace winrt::Microsoft::UI::Xaml::Controls;
};

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
            Categories();

        void OnItemInvoked(muxc::NavigationView const& sender, muxc::NavigationViewItemInvokedEventArgs const& args);
        void OnItemExpanding(
            muxc::NavigationView const& sender,
            muxc::NavigationViewItemExpandingEventArgs const& args);
        void OnItemCollapsed(
            muxc::NavigationView const& sender,
            muxc::NavigationViewItemCollapsedEventArgs const& args);

    private:
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> m_categories;
    };
}

namespace winrt::HierarchicalNavigationViewDataBinding::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

#include "Category.h"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();

        m_categories =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();

        auto menuItem10 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 10", L"Icon", nullptr);

        auto menuItem9 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 9", L"Icon", nullptr);
        auto menuItem8 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 8", L"Icon", nullptr);
        auto menuItem7Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem7Children.Append(*menuItem9);
        menuItem7Children.Append(*menuItem8);

        auto menuItem7 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 7", L"Icon", menuItem7Children);
        auto menuItem6Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem6Children.Append(*menuItem7);

        auto menuItem6 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 6", L"Icon", menuItem6Children);

        auto menuItem5 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 5", L"Icon", nullptr);
        auto menuItem4 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 4", L"Icon", nullptr);
        auto menuItem3Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem3Children.Append(*menuItem5);
        menuItem3Children.Append(*menuItem4);

        auto menuItem3 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 3", L"Icon", menuItem3Children);
        auto menuItem2Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem2Children.Append(*menuItem3);

        auto menuItem2 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 2", L"Icon", menuItem2Children);
        auto menuItem1Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem1Children.Append(*menuItem2);

        auto menuItem1 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 1", L"Icon", menuItem1Children);

        m_categories.Append(*menuItem1);
        m_categories.Append(*menuItem6);
        m_categories.Append(*menuItem10);
    }

    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
        MainPage::Categories()
    {
        return m_categories;
    }

    void MainPage::OnItemInvoked(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemInvokedEventArgs const& args)
    {
        auto clickedItem = args.InvokedItem();
        auto clickedItemContainer = args.InvokedItemContainer();
    }

    void MainPage::OnItemExpanding(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemExpandingEventArgs const& args)
    {
        auto nvib = args.ExpandingItemContainer();
        auto name = L"Last expanding: " + winrt::unbox_value<winrt::hstring>(nvib.Content());
        ExpandingItemLabel().Text(name);
    }

    void MainPage::OnItemCollapsed(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemCollapsedEventArgs const& args)
    {
        auto nvib = args.CollapsedItemContainer();
        auto name = L"Last collapsed: " + winrt::unbox_value<winrt::hstring>(nvib.Content());
        CollapsedItemLabel().Text(name);
    }
}

선택 사항

기본적으로 모든 항목은 자식을 포함하거나 호출 및 선택할 수 있습니다.

사용자에게 탐색 옵션의 계층 트리를 제공할 때 앱에 부모 항목에 연결된 대상 페이지가 없는 경우와 같이 부모 항목을 선택할 수 없도록 설정할 수도 있습니다. 부모 항목을 선택할 수 있는 경우 왼쪽 확장 또는 위쪽 창 표시 모드를 사용하는 것이 좋습니다. LeftCompact 모드를 사용하면 사용자가 부모 항목을 탐색하여 호출될 때마다 자식 하위 트리를 열 수 있습니다.

선택 항목을 선택하면 왼쪽 모드의 경우 왼쪽 가장자리를 따라, 위쪽 모드에서는 아래쪽 가장자리를 따라 선택 표시기가 표시됩니다. 아래에는 부모 항목이 선택된 왼쪽 및 위쪽 모드의 NavigationViews가 나와 있습니다.

NavigationView in left-mode with parent selected

NavigationView in top-mode with parent selected

선택한 항목이 항상 표시되는 것은 아닙니다. 축소되거나 확장되지 않은 하위 트리의 자식을 선택하면 첫 번째로 표시되는 상위 항목이 선택된 상태로 표시됩니다. 하위 트리가 확장되면 선택 표시기가 선택된 항목으로 다시 이동합니다.

예를 들어 위의 이미지에서 사용자가 일정 항목을 선택한 다음, 사용자가 해당 하위 트리를 축소할 수 있습니다. 이 경우 계정이 일정의 첫 번째 상위 항목이기 때문에 계정 항목 아래에 선택 표시기가 표시됩니다. 사용자가 하위 트리를 다시 확장하면 선택 표시기가 일정 항목으로 다시 이동합니다.

전체 NavigationView에는 둘 이상의 선택 표시기가 표시되지 않습니다.

위쪽과 왼쪽 모드 모두에서 NavigationViewItems의 화살표를 클릭하면 하위 트리가 확장되거나 축소됩니다. NavigationViewItem에서 아무데나 클릭하거나 탭하면 ItemInvoked 이벤트가 트리거되고 하위 트리도 축소 또는 확장됩니다.

항목이 호출될 때 선택 표시기를 표시하지 않도록 하려면 아래와 같이 SelectsOnInvoked 속성을 False로 설정합니다.

<Page ...>
    <Page.Resources>
        <DataTemplate x:Key="NavigationViewMenuItem" x:DataType="local:Category">
            <NavigationViewItem Content="{x:Bind Name}"
            MenuItemsSource="{x:Bind Children}"
            SelectsOnInvoked="{x:Bind IsLeaf}"/>
        </DataTemplate>
    </Page.Resources>

    <Grid>
        <NavigationView x:Name="navview"
    MenuItemsSource="{x:Bind Categories, Mode=OneWay}"
    MenuItemTemplate="{StaticResource NavigationViewMenuItem}">
        </NavigationView>
    </Grid>
</Page>
public class Category
{
    public String Name { get; set; }
    public String CategoryIcon { get; set; }
    public ObservableCollection<Category> Children { get; set; }
    public bool IsLeaf { get; set; }
}

public sealed partial class HierarchicalNavigationViewDataBinding : Page
{
    public HierarchicalNavigationViewDataBinding()
    {
        this.InitializeComponent();
    }

    public ObservableCollection<Category> Categories = new ObservableCollection<Category>()
    {
        new Category(){
            Name = "Menu item 1",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 2",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() {
                            Name  = "Menu item 3",
                            CategoryIcon = "Icon",
                            Children = new ObservableCollection<Category>() {
                                new Category() { Name  = "Menu item 4", CategoryIcon = "Icon", IsLeaf = true },
                                new Category() { Name  = "Menu item 5", CategoryIcon = "Icon", IsLeaf = true }
                            }
                        }
                    }
                }
            }
        },
        new Category(){
            Name = "Menu item 6",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 7",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() { Name  = "Menu item 8", CategoryIcon = "Icon", IsLeaf = true },
                        new Category() { Name  = "Menu item 9", CategoryIcon = "Icon", IsLeaf = true }
                    }
                }
            }
        },
        new Category(){ Name = "Menu item 10", CategoryIcon = "Icon", IsLeaf = true }
    };
}
// Category.idl
namespace HierarchicalNavigationViewDataBinding
{
    runtimeclass Category
    {
        ...
        Boolean IsLeaf;
    }
}

// Category.h
...
struct Category : CategoryT<Category>
{
    ...
    Category(winrt::hstring name,
        winrt::hstring categoryIcon,
        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category> children,
        bool isleaf = false);
    ...
    bool IsLeaf();
    void IsLeaf(bool value);

private:
    ...
    bool m_isleaf;
};

// Category.cpp
...
Category::Category(winrt::hstring name,
    winrt::hstring categoryIcon,
    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category> children,
    bool isleaf) : m_name(name), m_categoryIcon(categoryIcon), m_children(children), m_isleaf(isleaf) {}
...
bool Category::IsLeaf()
{
    return m_isleaf;
}

void Category::IsLeaf(bool value)
{
    m_isleaf = value;
}

// MainPage.h and MainPage.cpp
// Delete OnItemInvoked, OnItemExpanding, and OnItemCollapsed.

// MainPage.cpp
...
MainPage::MainPage()
{
    InitializeComponent();

    m_categories = winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();

    auto menuItem10 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 10", L"Icon", nullptr, true);

    auto menuItem9 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 9", L"Icon", nullptr, true);
    auto menuItem8 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 8", L"Icon", nullptr, true);
    auto menuItem7Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem7Children.Append(*menuItem9);
    menuItem7Children.Append(*menuItem8);

    auto menuItem7 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 7", L"Icon", menuItem7Children);
    auto menuItem6Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem6Children.Append(*menuItem7);

    auto menuItem6 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 6", L"Icon", menuItem6Children);

    auto menuItem5 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 5", L"Icon", nullptr, true);
    auto menuItem4 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 4", L"Icon", nullptr, true);
    auto menuItem3Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem3Children.Append(*menuItem5);
    menuItem3Children.Append(*menuItem4);

    auto menuItem3 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 3", L"Icon", menuItem3Children);
    auto menuItem2Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem2Children.Append(*menuItem3);

    auto menuItem2 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 2", L"Icon", menuItem2Children);
    auto menuItem1Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem1Children.Append(*menuItem2);

    auto menuItem1 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 1", L"Icon", menuItem1Children);

    m_categories.Append(*menuItem1);
    m_categories.Append(*menuItem6);
    m_categories.Append(*menuItem10);
}
...

계층적 NavigationView 내의 키보드 사용

사용자는 키보드를 사용하여 NavigationView 주위에서 포커스를 이동할 수 있습니다. 화살표 키는 창 내에서 “내부 탐색”을 노출하고 트리 보기에서 제공하는 상호 작용을 따릅니다. 키 동작은 HierarchicalNavigationView의 위쪽 및 왼쪽 컴팩트 모드에서 표시되는 NavigationView 또는 플라이아웃 메뉴를 통해 탐색할 때 변경됩니다. 다음은 각 키가 계층적 NavigationView에서 수행할 수 있는 특정 작업입니다.

왼쪽 모드에서 위쪽 모드에서 플라이아웃에서
위로 현재 포커스를 둔 항목 바로 위에 있는 항목으로 포커스를 이동합니다. 아무 작업도 하지 않습니다. 현재 포커스를 둔 항목 바로 위에 있는 항목으로 포커스를 이동합니다.
아래로 현재 포커스를 둔 항목 바로 아래에 포커스를 이동합니다.* 아무 작업도 하지 않습니다. 현재 포커스를 둔 항목 바로 아래에 포커스를 이동합니다.*
Right 아무 작업도 하지 않습니다. 현재 포커스를 둔 항목 바로 오른쪽에 있는 항목으로 포커스를 이동합니다. 아무 작업도 하지 않습니다.
Left 아무 작업도 하지 않습니다. 현재 포커스를 둔 항목 바로 왼쪽에 있는 항목으로 포커스를 이동합니다. 아무 작업도 하지 않습니다.
스페이스 바/Enter 키 항목에 자식 항목이 있으면 항목을 확장/축소하고 포커스를 변경하지 않습니다. 항목에 자식 항목이 있으면 자식 항목을 플라이아웃으로 확장하고 플라이아웃의 첫 번째 항목에 포커스를 둡니다. 항목을 호출하거나 선택하고 플라이아웃을 닫습니다.
Esc 아무 작업도 하지 않습니다. 아무 작업도 하지 않습니다. 플라이아웃을 닫습니다.

스페이스바 또는 Enter 키는 항상 항목을 호출하거나 선택합니다.

*항목이 시각적으로 인접하지 않아도 되는 경우 창의 목록에 있는 마지막 항목에서 설정 항목으로 포커스가 이동합니다.

창 배경

기본적으로 NavigationView 창은 디스플레이 모드에 따라 다른 배경을 사용합니다.

  • 창이 콘텐츠와 나란히 왼쪽에서 확장되는 경우(Left 모드) 회색으로 표시됩니다.
  • 창이 콘텐츠 위쪽에 오버레이로 열리는 경우(Top, Minimal 또는 Compact 모드) 앱 내 아크릴을 사용합니다.

창 배경을 수정하기 위해 각 모드에서 배경을 렌더링하는 데 사용되는 XAML 테마 리소스를 재정의할 수 있습니다. 이 기술은 디스플레이 모드에 따라 다른 배경을 지원하기 위해 단일 PaneBackground 속성 대신 사용됩니다.

이 표에서는 각 디스플레이 모드에서 사용되는 테마 리소스를 보여 줍니다.

표시 모드 테마 리소스
Left NavigationViewExpandedPaneBackground
LeftCompact
LeftMinimal
NavigationViewDefaultPaneBackground
NavigationViewTopPaneBackground

이 예제는 App.xaml에서 테마 리소스를 재정의하는 방법을 보여 줍니다. 테마 리소스를 재정의하는 경우에는 항상 최소한 “Default” 및 “HighContrast” 리소스 사전을 제공하며 필요에 따라 “Light” 또는 “Dark” 리소스 사전을 제공해야 합니다. 자세한 내용은 ResourceDictionary.ThemeDictionaries를 참조하세요.

Important

이 코드는 AcrylicBrush의 Windows UI 라이브러리 버전을 사용하는 방법을 보여 줍니다. AcrylicBrush 플랫폼 버전을 사용하는 경우에는 앱 프로젝트의 최소 버전이 SDK 16299 이상이어야 합니다. 플랫폼 버전을 사용하려면 muxm:에 대한 모든 참조를 제거합니다.

<Application ... xmlns:muxm="using:Microsoft.UI.Xaml.Media" ...>
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/>
                <ResourceDictionary>
                    <ResourceDictionary.ThemeDictionaries>
                        <ResourceDictionary x:Key="Default">
                            <!-- The "Default" theme dictionary is used unless a specific
                                 light, dark, or high contrast dictionary is provided. These
                                 resources should be tested with both the light and dark themes,
                                 and specific light or dark resources provided as needed. -->
                            <muxm:AcrylicBrush x:Key="NavigationViewDefaultPaneBackground"
                                   BackgroundSource="Backdrop"
                                   TintColor="LightSlateGray"
                                   TintOpacity=".6"/>
                            <muxm:AcrylicBrush x:Key="NavigationViewTopPaneBackground"
                                   BackgroundSource="Backdrop"
                                   TintColor="{ThemeResource SystemAccentColor}"
                                   TintOpacity=".6"/>
                            <LinearGradientBrush x:Key="NavigationViewExpandedPaneBackground"
                                     StartPoint="0.5,0" EndPoint="0.5,1">
                                <GradientStop Color="LightSlateGray" Offset="0.0" />
                                <GradientStop Color="White" Offset="1.0" />
                            </LinearGradientBrush>
                        </ResourceDictionary>
                        <ResourceDictionary x:Key="HighContrast">
                            <!-- Always include a "HighContrast" dictionary when you override
                                 theme resources. This empty dictionary ensures that the
                                 default high contrast resources are used when the user
                                 turns on high contrast mode. -->
                        </ResourceDictionary>
                    </ResourceDictionary.ThemeDictionaries>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

상단 공백

IsTitleBarAutoPaddingEnabled 속성을 사용하려면 Windows UI 라이브러리 2.2 이상이 필요합니다.

일부 앱은 해당 창의 제목 표시줄을 사용자 지정하여 해당 앱 콘텐츠를 제목 표시줄 영역으로 확장하도록 선택할 수 있습니다. NavigationView가 ExtendViewIntoTitleBar API를 사용하여 제목 표시줄 로 확장되는 앱의 루트 요소인 경우 컨트롤이 그 대화형 요소의 위치를 자동으로 조정하여 끌기 가능 영역과 중첩되는 것을 방지합니다.

An app extending into the title bar

앱이 Window.SetTitleBar 메서드를 호출하여 끌기 가능 영역을 지정할 때 [뒤로] 및 [메뉴] 단추를 앱 창의 상단에 더 가깝게 이동하려면 IsTitleBarAutoPaddingEnabledfalse로 설정합니다.

App extending into the title bar without extra padding

<muxc:NavigationView x:Name="NavView" IsTitleBarAutoPaddingEnabled="False">

설명

NavigationView의 헤더 영역 위치를 추가로 조정하려면 NavigationViewHeaderMargin XAML 테마 리소스를 재정의합니다(예: 페이지 리소스에서).

<Page.Resources>
    <Thickness x:Key="NavigationViewHeaderMargin">12,0</Thickness>
</Page.Resources>

이 테마 리소스가 NavigationView.Header 주위의 여백을 수정합니다.