The Tab control is a way to display a set of tabs and their respective content. Tab controls are useful for displaying several pages (or documents) of content while giving a user the capability to rearrange, open, or close new tabs.
As a UI paradigm, Tabs has been around for a long time, and can be found in everything from dialogs to browsers. The focus for this proposal is "document-style" tabs, which enables a user to rearrange, open, and close new tabs.
_Tabs in the Edge browser_
_Tabs in Visual Studio_
Note that "static-style" tabs also exist as a paradigm. "Static-style" tabs are tabs that do not support user interaction beyond switching tabs. UWP already has a solution for static-style tabs in the form of Pivot and NavigationView.
_Static-style tabs in WPF_
A lack of a proper Tab control has been a continual painpoint in UWP, particularly for developers attempting to migrate from WPF.
The XAML platform provides a number of ways to achieve "static-style" tabs in Pivot and top NavigationView. However, these solutions do not well support "document-style" tabs which support user reorder, opening, and closing tabs. The platform does demonstrate how to create "document-style" tabs in the Van Arsdel sample using top NavigationView:
Although these workarounds have unblocked apps trying to create "document-style" tabs, they have a number of limitations, including:
Fortunately, the Windows Community Toolkit has created a TabView control that helps address some of the aforementioned pain points.
However, the Windows Community Toolkit is a managed/C# project, meaning that customers that don't want to load the CLR or are particularly performance-focused cannot leverage the Windows Community Toolkit implementation.
The goal for this feature proposal is not to "reinvent the wheel" - instead, we hope to take the learnings from the WCT implementation and discussion and build a native control directly into WinUI.
| # | Feature | Priority |
|:--:|:--------|:--------:|
| 1. | User can switch tabs using common inputs (like ctrl+tab) | Must |
| 2. | Control has a mode to close tabs | Must |
| 3. | User can open new tabs | Must |
| 4. | Control supports tab "tear off" | Must |
| 5. | Control supports tab "recombination" (both into the same Tab group or between Tab groups) | Must |
| 6. | Control supports tab reorder | Must |
| 7. | Control supports data binding to a collection of tabs | Must |
| 8. | Control supports a custom header/footer | Must |
| 9. | When not all tabs fit, control provides affordance to access all tabs | Must |
| 10. | Tab items can have a label and icon | Should |
| 11. | Tab height, width, and template can be customized | Should |
| 12. | The app may decide how size the tabs relative to each other (ie. equally sized, sized to content, etc.) | Should |
| 13. | The control supports Tab Placement with tabs on the Left, Right, or Bottom. | Could |
| 14. | App can specify specific "unclosable" tabs | Could |
To replicate the behavior of Microsoft Edge:
<TabControl TabWidthBehavior="Equal"
CanCloseTabs="True"
CloseButtonOverlay="OnHover"
CanDragItems="True"
CanReorderItems="True"
TabDraggedOutside="OpenTabInNewWindow">
<TabControl.TabFooter>
<Button Content="+" Click="NewTab_Click" />
</TabControl.TabFooter>
...
</TabControl>
The TabControl also supports databinding:
<TabControl ItemsSource="{x:Bind TabItemCollection}" />
| Property | Type | Description |
|:-------- |:---- |:----------- |
| CanCloseTabs | bool | Default value for the item if it doesn't specify a IsClosable value. |
| CloseButtonOverlay | enum | Describes the behavior of the close button. Values are {Auto, OnHover, Persistent} |
| ItemHeaderTemplate | DataTemplate | Default template for the item if no template specified. |
| SelectedTabWidth | double | The size of the selected tab header. |
| TabHeader | object | Content to the left of the tab strip. |
| TabHeaderTemplate | DataTemplate | Template for the Header. |
| TabFooter | object | Content to the far right of the tab strip. |
| TabFooterTemplate | DataTemplate | Template for the Footer. |
| TabActionContent | object | Content immediately to the right of the tabs |
| TabActionContentTemplate | DataTemplate | Template for the ActionContent. |
| TabWidthBehavior | enum | Specifies how the tabs should be sized. Values are {Actual, Equal, Compact} |
| Event | Description |
|---|---|
| TabClosing | Fires when a tab is about to be closed. Can be cancelled to prevent closure. |
| TabDraggedOutside | Fires when a Tab is dragged outside of the Tab bar. |
| Property | Type | Description |
|:-------- |:---- |:----------- |
| Content | object | The main content that appears in the tab. |
| Header | object | The content that appears inside the tab itself. |
| HeaderTemplate | DataTemplate | Template for the header object. |
| Icon | IconElement | Icon for the tab. |
| IsClosable | bool | Determines if the tab shows a close button. |
| Event | Description |
|---|---|
| TabClosing | Fires when a tab's close button is clicked. |
1. Should Tab Tearoff be something that the control handles or that the app handles?
The app will handle it. We will have good samples showing how.
2. Should there be a built-in "Add new tab" button? (I suspect probably not, because the control doesn't own the collection of Tabs.)
The app will own the "Add tab" button. In the case of Terminal, for example, they will add a button with a dropdown. Adding an "Add" button doesn't add much value.
3. Should TabItem.Icon be IconElement or IconElementSource?
IconElement. IconElementSource is useful when the same icon could appear in multiple places in the tree, which isn't the case here.
How can an app customize that the Selected tab looks like (ie. in Edge)?
The API currently takes a lot of inspiration from the Toolkit. Are there any chances to iterate/improve?
[x] PM: docs.microsoft.com updates ready
[x] Dev: feature previously shipped in a prerelease NuGet package
Should add a link to the Van Arsdel sample
I think the more generic controls from the Toolkit should always find their way ported to C++ and available in the Windows UI Library. Tabs control when it is mature and stable enough should make its way too.
It happened with the Main Menu control :)
Link to Tab Tear-Off sample called out in the proposal as well.
Link to Tab Tear-Off sample called out in the proposal as well.
Does tearing off a tab spawn a new AppWindow, new CoreWindow, and how do you handle fallback where the new window APIs are not present?
@mdtauk the sample uses the older multi-windows ApplicationView API currently, the window gets created in a default position instead of where dragged. I've been testing with AppWindow and hope to update the sample for the new SDK.
"Although these workarounds have helped bridge the platform gap from WPF, they have a number of limitations, including:"
You should list keyboarding here as well. This is not just tab key or shortcut keys. Arrowing was something we struggled with when we didn't have tab control and we had to make everyone be consistent in the lack of it.
Comments on open questions from what we've heard in the toolkit:
- Should Tab Tearoff be something that the control handles or that the app handles?
I think it's important for the Tab Control to handle the dragging and recombining between TabViews (hosting the same data type) and exposing an drag-out event, with the ability to inspect/intercept.
But I don't know if it's important for the TabView to also directly manage the window lifecycle as that gets more tricky. A developer may also want to customize what happens when dragging a tab out, maybe it just removes it, or creates a special window for just that content vs. a new 'main' window with tabs? Or they may not want to handle having to code for multiple-windows.
It may just be easier to expose the event and have helpers (or leverage existing ones like the ones in Windows Template Studio) to aid in the creation/management of Windows. The trickiest part of this scenario is "data transfer" between threads, which at least the new windowing API should help alleviate.
- Should there be a built-in "Add new tab" button? (I suspect probably not, because the control doesn't own the collection of Tabs.)
I think as long as there are adequate header templates and examples, having an implementor add an 'add' button is trivial, so I don't think it needs to be built-in, as then the paradigm would need another event. This is something we had originally thought about too in the toolkit. :)
- Should TabItem.Icon be IconElement or IconElementSource?
We used IconElement
to mimic the NavigationViewItem's property. Also, IconElementSource
is an IconElement
so isn't IconElement
better as it's broader? It'd be nice if there were an IconElement
subclass in WinUI that could just host a straight-up Image
though (similar to BitmapIcon
but without the forced masking). That way the Icon property could host anything be it a nice Vector Symbol/Font Icon or an ico/png file (think browser type scenario favicon).
- How can an app customize that the Selected tab looks like (ie. in Edge)?
Are you talking about exposed style properties which can be modified or like a completely different template for the selected item?
- The API currently takes a lot of inspiration from the Toolkit. Are there any chances to iterate/improve?
One feedback item we got was about customizing the space at the end of the tabs. E.g. Should it just be a single template area that can then use left/right horizontal alignment to stick things to the edges vs. two separate areas?
Other questions:
Also forgot to call out for Requirement 9, we had a discussion originally on whether or not to use the Edge model (like the toolkit control currently behaves) or the NavigationView model where it creates a drop-down chevron. We thought maybe in the future we'd support both.
We ended up going with the Edge model as it was simpler to implement, as then we didn't need a SplitObservableCollection
type object. I had started working on one as a test, but I wonder if it'd be a useful construct to expose?
@stmoy did you close on the open issues/feedback from above that we had from the toolkit designs?
How does the news of Windows Sets abandoning the Edge Tab model, affect how this control may be developed?
With Edge and it's UI pretty much abandoned now in favour of ChromiumEdge (Edgium) do we need/want to rethink the appearance or behaviour of this control to be more suitable to the types of apps that may use it?
Not to mention similar controls like Pivot, and the UWP Ribbon in development. Which is recommended for which use case, and why would someone choose one over the other? Guidance and examples should be clear to answer these questions to avoid confusion or a heavier control being used instead of a performant and lighter one.
First Party controls should use the right control for the right purpose, to try to lead the way in consistent use.
On #629 @mdtauk had a feature suggestion:
Maybe an idea for the ToDo list. Tab Placement? Bottom, Left, Right.
Yeah, Tab Placement should be up there in the priority list at some-point to match WPF parity for migration scenarios. It's something we didn't have time to add in the initial Toolkit version either though.
When we scoped this feature, it became a non-goal to support the "property sheet tabs" scenario and to focus on the browser-tabs scenario. We do see some overlap with property sheets, but the property sheet metaphor isn't part of fluent design, we have NavigationView (which has Left/Top modes) for that scenario right now.
Navigation View is a heavy control, and wants to take up the whole screen. Also Sets is gone from the roadmap, as is the XAML version of Edge.
So there may be scope to re-think the TabView/Control to make it more flexible and useful for the future.
These scenarios could be possible in UWP XAML apps, if the TabView control were to have the ability to hide the add and close buttons.
But there is also Pivot controls, however it only supports pivot headers on top, and Tabs could go on the top, bottom, left or right...
In the toolkit we supported not having the close button, the add button was also something the implementer had to add themselves. This is why we had the TabWidthBehavior
property to help setup these different modes between documents and classic tabs. One of the main things we didn't support yet was the tab positioning.
Thanks for all of the great conversation all! Circling back around...
It's interesting with drag about how to handle dragging into the content area vs. the header as well are both targets? What if the app wants to allow dropping something else in the tab area (like a file from explorer)?
Interesting question. Naively, I'd think that they'd both be the same (large) target as the TabControl hosts both the Tab strip area and the content area. However, this model falls apart when considering apps like Edge (as an example). If Edge was maximized and the user drags a tab out of the tab strip but still over the content because it's max size, the user expects the Tab to create a new window.
Therefore I think we should only have the header area/tab strip be the drag/drop target.
Is "The app may decide how position/size the tabs" talking about the TabStripPlacement property?
This was meant to describe the "TabWidthBehavior" property and enum and not TabStripPlacement. I updated the requirement and added a Tab Placement requirement as well (discussed a bit more below).
Sets is gone from the roadmap, as is the XAML version of Edge.
The primary motivator for this work right now is actually the new Windows Terminal app. We will continue to expand/explore other apps to use the Tab control, but our primary focus is on enabling their scenarios for the moment.
Tab Placement should be up there in the priority list at some-point to match WPF parity for migration scenarios. It's something we didn't have time to add in the initial Toolkit version either though.
Tab Placement is an awesome idea, particularly as it relates to WPF parity. Per the above, however, since we're largely scoping based on what Terminal needs, this ends up being more of a "could" than a "need". That said, I added it to the requirements table above.
These scenarios could be possible in UWP XAML apps, if the TabView control were to have the ability to hide the add and close buttons.
The TabControl supports hiding the close button on the tabs themselves. Additionally, the "Add" button will be provided by the app itself (and not the control), so we expect to be able to make property-style tab scenarios using the control as spec'd above.
If the Add tab button is not part of the control, might I suggest a button style and XAML Command be included with the control, to make it easier for people to implement a button consistently wherever the control is used.
@stmoy for the drag scenario, I think there'll be cases where dragging on to the header and the area can be different or the same. It'd be good if a sample could show the dragging for the tear-away, but also for instance dropping a file in the main content area and how to catch that (that may be out of the purview of the control itself, but useful to it's usage).
button style and XAML Command [for adding a new Tab]
@mdtauk - great idea. I'll add it to the spec.
It'd be good if a sample could show the dragging for the tear-away, but also for instance dropping a file in the main content area and how to catch that
@michael-hawker - Agreed, a sample would be worthwhile. I don't know if it needs to be Tab-specific though, since dragging a file in would be handled by the content area.
Fingers crossed that they feel more like Chrome's tabs than Edge's.
Fingers crossed that they feel more like Chrome's tabs than Edge's.
We want the interaction design to "feel right", so definitely file issues with specific feedback against the preview version so we can get it right before we ship!
Not sure if someone like @chigy has already made out a design comp for the TabView Control, but I had a quick go, also demonstrating the Side and Bottom placement options that could come in the future.
I don't like that the close-button hover/pressed states change the background of the button and not its foreground. Changing the background adds too much emphasis to the button (and away from the tab title) and the latter would also be much more elegant.
For examples, see the close-button in Edge UWP (foreground changes only) or the tab-audio mute button in Edge (Chromium).
I did consider sticking with the foreground change, but felt the background change would be inline with the other controls and titlebar behaviour - but as a default it could just be the foreground.
@mdtauk - you rock. Thank you for creating these design comps. Although we're not going to do side placement for the first drop of the control, it's great to see what it could look like.
@chigy and I are working together to build out some design comps that line up with the Windows principles (incl. things like rounded corners, standard shadow, etc.) but the design comps you provided will help inspire us.
I don't like that the close-button hover/pressed states change the background of the button and not its foreground. Changing the background adds too much emphasis to the button (and away from the tab title) and the latter would also be much more elegant.
Our current thinking is just to change the foreground color and not the background, as is being suggested here. This has the added benefit of fitting a bit better with the rounded corners that we are considering.
The Edge tabs use a background behind the close glyph, but it is smaller than the one I proposed - but the Edgium tabs are not yet supporting any Touch modes.
When we were thinking of the side tabs for the design for the toolkit, we were torn between doing them vertically with rotated text as @mdtauk showed or still having them horizontal and taking up a larger margin to the left/right side like OneNote used to before their latest redesign and akin to WPF (which we would thought would be important for compatibility).
So, I'd suggest to keep it as WPF did with the wide-margin and horizontal, but also just make it super simple to support both as shown here, which is another reason to support #546.
@michael-hawker I endorse having the vertical list of tabs to the right with the wide margin, and making it easy to rotate them as in Visual Studio.
It will affect the before and after tab spots, but I will look into doing a design mock up when I return home on Monday
It will affect the before and after tab spots, but I will look into doing a design mock up when I return home on Monday
I am home, and here is that design:
I have refined a design mockup for the control.
_added Tab Overflow and Tab Sizing_
Like that your tab concept looks much more similar to Edge UWP tabs than Edge Chromium tabs. IMO the look of the tab control should be as close as possible to the tab control in Edge UWP. Acrylic as a tab control background like in Edge UWP also would be quite nice.
I still hope the tab-close button can be made like in Edge UWP, where the button is only displayed for the currently selected tab or on tab mouse hover.
For the WinUI team: @stmoy Apparently the Windows Terminal team can't use acrylic as a background for the tab control (see https://github.com/microsoft/terminal/issues/1375). The terminal team claims that is due to architectural limitations. Is that something the WinUI team could help with? There seem to be quite a few fans of the acrylic background for the tab control as in Edge UWP.
@Felix-Dev I know Edgeium is the new design to move towards, but I think the UWP Edge design is a bit nicer - so I wanted to try to bridge that gap
I still hope the tab-close button can be made like in Edge UWP, where the button is only displayed for the currently selected tab or on tab mouse hover.
CloseButtonOverlay Is the property that would do that I think
@mdtauk
To clarify, I'd like to see CloseButtonOverlay = OnHover to become the default of the Tab Control, instead of the close button being constantly shown on all tabs (unnecessarily takes away space from the tab title imo).
Always visible is best for touch users, so I think the default should be as inclusive as possible
Perhaps overlay mode fo the close button could be made dependent on the capabilities of the user's display then?
Not sure if that could somehow be an issue as then there would be a slight visual difference in the Tab Control depending on the display type.
Perhaps overlay mode fo the close button could be made dependent on the capabilities of the user's display then?
Not sure if that could somehow be an issue as then there would be a _slight_ visual difference in the Tab Control depending on the display type.
That works for things like flyouts because it can detect the type of input that triggered it, providing larger touch targets if summoned by a touch input.
This doesn't work for controls always visible, as some touch devices will also have a mouse attached.
Developers will be able to change the default option of course, and if an app gets lots of feedback asking for the close button to only show on hover - then they can choose to change it.
I have added an idea for Tab overflow
Love this.
Just one small thing. When there’s shadow and rounded corners, we might want a little gap in between the selected tab item and the edge of the background.
Love this.
Just one small thing. When there’s shadow and rounded corners, we might want a little gap in between the selected tab item and the edge of the background.
Curious why that would be needed... Doesn't the shadow get cropped by the Window frame? Is it just an aesthetic choice so the shadow is visible above?
Tab Sizing
@mdtauk : I love seeing these designs! I have a few probing questions:
I'll use these designs to talk with our friends on the Terminal and see what they think. These designs include a few deltas from our current direction, but we will use these to help foster the conversation.
@Felix-Dev : Thank you for the feedback; addressing some of your points below.
Like that your tab concept looks much more similar to Edge UWP tabs than Edge Chromium tabs. IMO the look of the tab control should be as close as possible to the tab control in Edge UWP. Acrylic as a tab control background like in Edge UWP also would be quite nice.
We're actively iterating with various teams (including Terminal and Edge teams) to try to align the Tabs across these app experiences. As we continue to iterate (and implement items like #524), the Tab control design will look more consistent with the updated controls.
I still hope the tab-close button can be made like in Edge UWP, where the button is only displayed for the currently selected tab or on tab mouse hover.
For the short term, we're focusing on implementing the scenarios that Terminal needs and trimming the API surface where appropriate. For v1, we will only support Persistent. That said, I originally spec'd the hover behavior as well because I think it's a great idea. I'm keeping track of these sorts of asks (like x-on-hover, tab placement, etc.) so that we can reassess in the next rev of the control.
For the WinUI team: @stmoy Apparently the Windows Terminal team can't use acrylic as a background for the tab control (see microsoft/terminal#1375). The terminal team claims that is due to architectural limitations. Is that something the WinUI team could help with? There seem to be quite a few fans of the acrylic background for the tab control as in Edge UWP.
We're actively working with the Terminal team. Unfortunately this scenario is a result of a known limitation with Xaml Islands and not with Tabs. We expect that non-Terminal apps should be able to use acrylic background as an "opt-in" feature if the app sets the TabControl.Background to an acrylic brush.
Is purple the accent color?
Yes - I didn't want to confuse my design with an official design comp
Is the purpose of the overflow screenshot to show the bumpers? I just want to ensure I'm reading the picture correctly.
Yes, is showing how the left and right buttons would look, and how the tabs would be clipped.
What is the corner radius of the tabs?
4 epx - I know the guidance is 2epx, but the 4epx height of the selected indicator looked better with 4
How tall are the tabs?
40 epx
What font size was used?
14 for the tab title text, 16 for the MDL2 icons
In Edge Canary there is a flag called New tab-loading animation - edge://flags#new-tab-loading-animation
Part of the spec should include theme and/or implicit animations such as:
@stmoy
We're actively working with the Terminal team. Unfortunately this scenario is a result of a known limitation with Xaml Islands and not with Tabs.
Is it possible to address this limitation in future versions of Xaml Island? There seems to be a strong desire for background acrylic in the Windows Terminal app, for example. See https://github.com/microsoft/terminal/issues/1375#issuecomment-504625390 and especially the last image in that post (with background acrylic in the titlebar). Also note the many up-votes that post got 😉. Another post with many upvotes here: https://github.com/microsoft/terminal/issues/1375#issuecomment-504644293
Speaking more broadly about the Tabs UI design, also note the supportive reactions to user comments preferring the Edge UWP tabs look over Edge Chromium tab look: https://github.com/microsoft/terminal/issues/1375#issuecomment-504622788 and the post directly below it.
If Edge Chromium tabs are meant to represent the future look of (modern) Windows Tabs those overwhelming user reactions might be a reason to re-think the look of tabs both used in Edge Chromium and in WinUI. (Also adding @chigy here so she can notice those tab UI reactions in the Windows Terminal repo.)
Edit: Also calling the attention of @marb2000 to this topic as it the Windows Terminal team just reaffirmed that they "won't be able to fix" this issue. How is the WinUI team seeing chances for acrylic titlebar (highly requested!) happening for the Windows Terminal? @stmoy
@marb2000 @stmoy Please comment on the below question:
Also calling the attention of @marb2000 to this topic as the Windows Terminal team just reaffirmed that they "won't be able to fix" this issue [(acrylic titlebar in terminal)]. How is the WinUI team seeing chances for acrylic titlebar (highly requested!) happening for the Windows Terminal?
@marb2000 @SavoySchuler @jesbis @jevansaks
Pinged everyone one of you guys from the recent community call 😁
About technical acrylic titlebar support in Xaml Islands (Windows Terminal), see my posts above and which were already partly answered by @stmoy
Here are three of the main points taken from my posts and the replies above:
For the WinUI team: Apparently the Windows Terminal team can't use acrylic as a background for the tab control (see microsoft/terminal#1375). The terminal team claims that is due to architectural limitations. Is that something the WinUI team could help with? There seem to be quite a few fans of the acrylic background for the tab control as in Edge UWP.
Reply by @stmoy:
We're actively working with the Terminal team. Unfortunately this scenario is a result of a known limitation with Xaml Islands and not with Tabs. We expect that non-Terminal apps should be able to use acrylic background as an "opt-in" feature if the app sets the TabControl.Background to an acrylic brush.
My question:
Also calling the attention of @marb2000 to this topic as the Windows Terminal team just reaffirmed that they "won't be able to fix" this issue [(acrylic titlebar in terminal)]. How is the WinUI team seeing chances for acrylic titlebar (highly requested!) happening for the Windows Terminal?
Thanks for answering 😁
Most helpful comment
I have refined a design mockup for the control.
_added Tab Overflow and Tab Sizing_