TabView

TabView コントロールは、一連のタブとそれぞれの内容を表示するための手段です。 TabView は、ユーザーに新しいタブを再配置したり、開いたり、閉じたりする機能を提供しながら、コンテンツの複数のページ (またはドキュメント) を表示する場合に便利です。

TabView の例

Windows UI ライブラリを入手する

WinUI ロゴ

TabView コントロールでは、Windows アプリのための新しいコントロールと UI 機能を含む NuGet パッケージである Windows UI ライブラリが必要になります。 インストール手順などについて詳しくは、「Windows UI Library (Windows UI ライブラリ)」をご覧ください。

Windows UI ライブラリ API: TabView クラスTabViewItem クラス

ヒント

このドキュメントでは、XAML で muxc エイリアスを使って、プロジェクトに含めた Windows UI Library API を表します。 Page 要素にこれを追加しました。xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

コードビハインドでは、C# でも muxc エイリアスを使って、プロジェクトに含めた Windows UI Library API を表します。 この using ステートメントは、ファイルの先頭に追加されています。using muxc = Microsoft.UI.Xaml.Controls;

これは適切なコントロールですか?

一般に、タブ付き UI は、機能と外観が異なる 2 種類のスタイルのいずれかで提供されます。静的タブは、設定ウィンドウでよく見られるタブの種類です。 通常、内容があらかじめ定義されている固定順序の複数のページが含まれます。 ドキュメント タブは、Microsoft Edge のようなブラウザーで見られるタブの種類です。 ユーザーは、タブの作成、削除、再配置、ウィンドウ間でのタブの移動、タブの内容の変更を行うことができます。

TabView では、UWP アプリ用のドキュメント タブが提供されています。 次のような場合は TabView を使用します。

  • ユーザーが、タブを動的に開いたり、閉じたり、再配置したりできる。
  • ユーザーが、ドキュメントや Web ページを直接タブで開くことができる。
  • ユーザーが、ウィンドウ間でタブをドラッグ アンド ドロップできる。

TabView がアプリに適していない場合は、NavigationView コントロールの使用を検討してください。

構造

下の図では、TabView コントロールの各部分を示します。 TabStrip にはヘッダーとフッターがありますが、ドキュメントとは異なり、TabStrip のヘッダーとフッターはそれぞれストリップの左端と右端にあります。

TabView コントロールの構造

次の図では、TabViewItem コントロールの各部分を示します。 コンテンツは TabView コントロールの内部に表示されますが、コンテンツは実際には TabViewItem の一部であることに注意してください。

TabViewItem コントロールの構造

タブ ビューを作成する

この例では、簡単な TabView と、タブの開閉をサポートするためのイベント ハンドラーを作成します。

 <muxc:TabView AddTabButtonClick="TabView_AddTabButtonClick"
               TabCloseRequested="TabView_TabCloseRequested"/>
// Add a new Tab to the TabView
private void TabView_AddTabButtonClick(muxc.TabView sender, object args)
{
    var newTab = new muxc.TabViewItem();
    newTab.IconSource = new muxc.SymbolIconSource() { Symbol = Symbol.Document };
    newTab.Header = "New Document";

    // The Content of a TabViewItem is often a frame which hosts a page.
    Frame frame = new Frame();
    newTab.Content = frame;
    frame.Navigate(typeof(Page1));

    sender.TabItems.Add(newTab);
}

// Remove the requested tab from the TabView
private void TabView_TabCloseRequested(muxc.TabView sender, muxc.TabViewTabCloseRequestedEventArgs args)
{
    sender.TabItems.Remove(args.Tab);
}

動作

TabView の機能を利用または拡張する方法は多数あります。

TabItemsSource を TabViewItemCollection にバインドする

<muxc:TabView TabItemsSource="{x:Bind TabViewItemCollection}" />

ウィンドウのタイトル バーに TabView のタブを表示する

ウィンドウのタイトル バーの下にタブ専用の行を挿入するのではなく、2 つを同じ領域に統合できます。 これにより、コンテンツの縦方向の領域が節約され、最新のアプリの感じになります。

ユーザーはウィンドウをタイトル バーでドラッグしてウィンドウの位置を変更できるため、タイトル バーをタブで完全に埋めないことが重要です。 そのため、タイトル バーにタブを表示する場合は、ドラッグ可能な領域として確保するタイトル バーの部分を指定する必要があります。 ドラッグ可能な領域を指定しないと、タイトル バー全体がドラッグ可能になり、タブが入力イベントを受信できなくなります。 TabView をウィンドウのタイトルバーに表示する場合は、常に TabStripFooterTabView に含めて、それをドラッグ可能な領域としてマークする必要があります。

詳しくは、「タイトル バーのカスタマイズ」をご覧ください

タイトル バーのタブ

<muxc:TabView HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <muxc:TabViewItem Header="Home" IsClosable="False">
        <muxc:TabViewItem.IconSource>
            <muxc:SymbolIconSource Symbol="Home" />
        </muxc:TabViewItem.IconSource>
    </muxc:TabViewItem>
    <muxc:TabViewItem Header="Document 0">
        <muxc:TabViewItem.IconSource>
            <muxc:SymbolIconSource Symbol="Document" />
        </muxc:TabViewItem.IconSource>
    </muxc:TabViewItem>
    <muxc:TabViewItem Header="Document 1">
        <muxc:TabViewItem.IconSource>
            <muxc:SymbolIconSource Symbol="Document" />
        </muxc:TabViewItem.IconSource>
    </muxc:TabViewItem>
    <muxc:TabViewItem Header="Document 2">
        <muxc:TabViewItem.IconSource>
            <muxc:SymbolIconSource Symbol="Document" />
        </muxc:TabViewItem.IconSource>
    </muxc:TabViewItem>

    <muxc:TabView.TabStripHeader>
        <Grid x:Name="ShellTitlebarInset" Background="Transparent" />
    </muxc:TabView.TabStripHeader>
    <muxc:TabView.TabStripFooter>
        <Grid x:Name="CustomDragRegion" Background="Transparent" />
    </muxc:TabView.TabStripFooter>
</muxc:TabView>
public MainPage()
{
    this.InitializeComponent();

    var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
    coreTitleBar.ExtendViewIntoTitleBar = true;
    coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged;

    Window.Current.SetTitleBar(CustomDragRegion);
}

private void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
    if (FlowDirection == FlowDirection.LeftToRight)
    {
        CustomDragRegion.MinWidth = sender.SystemOverlayRightInset;
        ShellTitlebarInset.MinWidth = sender.SystemOverlayLeftInset;
    }
    else
    {
        CustomDragRegion.MinWidth = sender.SystemOverlayLeftInset;
        ShellTitlebarInset.MinWidth = sender.SystemOverlayRightInset;
    }

    CustomDragRegion.Height = ShellTitlebarInset.Height = sender.Height;
}

注意

タイトル バーのタブがシェル コンテンツによって詰まってしまわないようにするには、左と右のオーバーレイを考慮する必要があります。 LTR レイアウトでは、右側の挿入部にキャプション ボタンとドラッグ領域が含まれます。 RTL では逆になります。 SystemOverlayLeftInset と SystemOverlayRightInset の値は物理的な左と右を意味するので、RTL ではこれらも逆にします。

コントロールのオーバーフロー動作

タブ バーがタブでいっぱいになったら、TabView.TabWidthMode を設定して、タブの表示方法を制御できます。

TabWidthMode の値 動作
Equal 新しいタブが追加されたら、非常に小さい最小幅になるまで、すべてのタブを横方向に縮小します。
SizeToContent タブは、常に、アイコンとヘッダーを表示するために必要な最小サイズである "自然サイズ" になります。 タブが追加されたり閉じられたりしても、拡張または縮小されません。

どちらの値を選択した場合でも、最終的にタブの数が多すぎてタブ ストリップに表示できなくなる可能性があります。 この場合は、スクロール バンパーが表示され、ユーザーは TabStrip を左右にスクロールできます。

タブの選択に関するガイダンス

ほとんどのユーザーは、Web ブラウザーを使用して簡単にドキュメント タブを使用した経験があります。 アプリでドキュメント タブを使用するとき、ユーザーは経験からタブがどのように動作するかを予期します。

ユーザーがドキュメント タブのセットを操作する方法に関係なく、常にアクティブなタブが必要になります。ユーザーが選択したタブを閉じたり、選択したタブを別のウィンドウに分割したりした場合は、別のタブがアクティブになる必要があります。TabView では、次のタブを自動的に選択することで、これが試みられます。タブが選択されていない TabView をアプリで許可する十分な理由がある場合、TabView のコンテンツ領域は単に空白になります。

キーボード ナビゲーション

TabView では、既定で多くの一般的なキーボード ナビゲーション シナリオがサポートされています。 このセクションでは、組み込みの機能について説明し、一部のアプリで役立つ追加機能についての推奨事項を示します。

タブとカーソル キーの動作

フォーカスが TabStrip 領域に移動すると、選択された TabViewItem がフォーカスを得ます。 ユーザーは左右の方向キーを使用して、TabStrip 内の他のタブにフォーカスを移動できます (選択はされません)。 矢印フォーカスはタブ ストリップ内でトラップされ、ある場合はタブ追加 [+] ボタンが表示されます。 Tab キーを押すとフォーカスを TabStrip 領域の外に移動することができ、フォーカスは次のフォーカス可能な要素に移動します。

Tab キーを使用してフォーカスを移動する

Tab キーを使用してフォーカスを移動する

方向キーではフォーカスは循環しない

方向キーではフォーカスは循環しない

タブの選択

TabViewItem にフォーカスがあるときに、Space キーまたは Enter キーを押すと、その TabViewItem が選択されます。

方向キーを使用してフォーカスを移動し、Space キーを押してタブを選択します。

Space キーでタブを選択する

隣接するタブを選択するためのショートカット

Ctrl + Tab キーを押すと、次の TabViewItem が選択されます。 Ctrl + Shift + Tab キーを押すと、前の TabViewItem が選択されます。 これらの目的のため、タブ リストは "ループ" になっているので、最後のタブが選択されているときに次のタブを選択すると、最初のタブが選択されます。

タブを閉じる

Ctrl + F4 キーを押すと、TabCloseRequested イベントが発生します。 イベントを処理し、必要に応じてタブを閉じます。

アプリ開発者向けのキー ボードガイダンス

アプリケーションによっては、より高度なキーボード制御が必要になる場合があります。 アプリに適している場合は、次のショートカットを実装することを検討してください。

警告

既存のアプリに TabView を追加する場合は、推奨される TabView キーボード ショートカットのキーの組み合わせに対応するキーボード ショートカットが、既に作成されている可能性があります。 この場合は、既存のショートカットを保持するか、ユーザーに直感的なタブ エクスペリエンスを提供するかを、検討する必要があります。

  • Ctrl + T キーが押されたら、新しいタブを開く必要があります。通常、このタブは、定義済みのドキュメントを設定されるか、またはコンテンツを選択する簡単な方法を備えて空で作成されます。 ユーザーが新しいタブのコンテンツを選択する必要がある場合は、コンテンツ選択コントロールに入力フォーカスを設定することを検討します。
  • Ctrl + W キーが押されたら、選択されているタブを閉じる必要があります。TabView では次のタブが自動的に選択されることを思い出してください。
  • Ctrl + Shift + T キーが押されたら、最近閉じられたタブを開く必要があります (より正確には、最近閉じられタブと同じコンテンツで新しいタブを開きます)。 最後に閉じられたタブから始めて、ショートカットが呼び出されるたびに、時間を遡って移動します。 これには、最近閉じられたタブのリストを保持する必要があることに注意してください。
  • Ctrl + 1 キーが押されたら、タブ リストの最初のタブを選択します。 同様に、Ctrl + 2 キーが押されたら 2 番目のタブを選択し、Ctrl + 3 キーが押されたら 3 番目のタブを選択します。Ctrl + 8 キーマで同様です。
  • Ctrl + 9 キーが押されたら、リスト内のタブの数に関係なく、タブ リストの最後のタブを選択します。
  • タブで "閉じる" 以外のコマンド (タブの複製やピン留めなど) も提供する場合は、コンテキスト メニューを使用して、タブで実行できるすべての操作を表示します。

ブラウザー スタイルのキーボード動作を実装する

この例では、TabView に関する上記の推奨事項がいくつか実装されています。 具体的には、この例では Ctrl + T、Ctrl + W、Ctrl + 1-8、Ctrl + 9 が実装されています。

<muxc:TabView x:Name="TabRoot">
    <muxc: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" />
    </muxc:TabView.KeyboardAccelerators>
    <!-- ... some tabs ... -->
</muxc:TabView>
private void NewTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
    // Create new tab.
    var newTab = new muxc.TabViewItem();
    newTab.IconSource = new muxc.SymbolIconSource() { Symbol = Symbol.Document };
    newTab.Header = "New Document";

    // The Content of a TabViewItem is often a frame which hosts a page.
    Frame frame = new Frame();
    newTab.Content = frame;
    frame.Navigate(typeof(Page1));

    TabRoot.TabItems.Add(newTab);
}

private void CloseSelectedTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
    // Only remove the selected tab if it can be closed.
    if (((muxc.TabViewItem)TabRoot.SelectedItem).IsClosable)
    {
        TabRoot.TabItems.Remove(TabRoot.SelectedItem);
    }
}

private void NavigateToNumberedTabKeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
    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 = TabRoot.TabItems.Count - 1;
            break;
    }

    // Only select the tab if it is in the list
    if (tabToSelect < TabRoot.TabItems.Count)
    {
        TabRoot.SelectedIndex = tabToSelect;
    }
}