Skip to content

Commit

Permalink
Fix issue with TabPaneFooter Width/MinWidth not being respected (#3531)
Browse files Browse the repository at this point in the history
* Fix bug where app in minimal mode would show pane on NavigationView

* Add test

* Fix issue with sizing of TabPaneFooter not being respected when changed after layout

* Rename function

* Use helper for registering callback

* update nits

* fix bad merge

---------

Co-authored-by: Karen Lai <biilai@microsoft.com>
  • Loading branch information
marcelwgn and karkarl authored Aug 23, 2023
1 parent 4b77c24 commit 73990b0
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 8 deletions.
10 changes: 9 additions & 1 deletion dev/Generated/TabView.properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ void TabViewProperties::EnsureProperties()
winrt::name_of<winrt::TabView>(),
false /* isAttached */,
ValueHelper<winrt::IInspectable>::BoxedDefaultValue(),
nullptr);
winrt::PropertyChangedCallback(&OnTabStripFooterPropertyChanged));
}
if (!s_TabStripFooterTemplateProperty)
{
Expand Down Expand Up @@ -302,6 +302,14 @@ void TabViewProperties::OnTabItemsSourcePropertyChanged(
winrt::get_self<TabView>(owner)->OnTabItemsSourcePropertyChanged(args);
}

void TabViewProperties::OnTabStripFooterPropertyChanged(
winrt::DependencyObject const& sender,
winrt::DependencyPropertyChangedEventArgs const& args)
{
auto owner = sender.as<winrt::TabView>();
winrt::get_self<TabView>(owner)->OnTabStripFooterPropertyChanged(args);
}

void TabViewProperties::OnTabWidthModePropertyChanged(
winrt::DependencyObject const& sender,
winrt::DependencyPropertyChangedEventArgs const& args)
Expand Down
4 changes: 4 additions & 0 deletions dev/Generated/TabView.properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ class TabViewProperties
winrt::DependencyObject const& sender,
winrt::DependencyPropertyChangedEventArgs const& args);

static void OnTabStripFooterPropertyChanged(
winrt::DependencyObject const& sender,
winrt::DependencyPropertyChangedEventArgs const& args);

static void OnTabWidthModePropertyChanged(
winrt::DependencyObject const& sender,
winrt::DependencyPropertyChangedEventArgs const& args);
Expand Down
2 changes: 1 addition & 1 deletion dev/NavigationView/NavigationView.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "pch.h"
Expand Down
52 changes: 51 additions & 1 deletion dev/TabView/APITests/TabViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,57 @@ static ISelectionItemProvider GetProviderFromTVI(TabViewItem item)
}

[TestMethod]
public void VerifyTabViewWithoutTabsDoesNotCrash()
public void VerifyPaneFooterSizingRespected()
{
foreach (var property in new DependencyProperty[] { FrameworkElement.MinWidthProperty, FrameworkElement.WidthProperty })
{
TabView tabView = null;
Grid paneFooter = null;
RunOnUIThread.Execute(() =>
{
paneFooter = new Grid();
tabView = new TabView();
var container = new Grid() {
MaxWidth = 600
};
container.Children.Add(tabView);
tabView.TabItems.Add(new TabViewItem());
tabView.TabItems.Add(new TabViewItem());
tabView.TabItems.Add(new TabViewItem());
tabView.TabItems.Add(new TabViewItem());
tabView.TabStripFooter = paneFooter;
tabView.UpdateLayout();
tabView.HorizontalAlignment = HorizontalAlignment.Stretch;
tabView.Width = 600;
container.UpdateLayout();
Content = container;
paneFooter.UpdateLayout();
});

IdleSynchronizer.Wait();

RunOnUIThread.Execute(() =>
{
Verify.IsTrue(Math.Abs(600 - tabView.ActualWidth) < 1);
paneFooter.SetValue(property, 300);
});
IdleSynchronizer.Wait();

RunOnUIThread.Execute(() =>
{
var footerPresenter = VisualTreeHelper.GetParent(paneFooter) as FrameworkElement;
Verify.IsTrue(Math.Abs(300 - footerPresenter.ActualWidth) < 1);
});
}
}



private void VerifyTabViewWithoutTabsDoesNotCrash()
{
RunOnUIThread.Execute(() =>
{
Expand Down
55 changes: 53 additions & 2 deletions dev/TabView/TabView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ TabView::TabView()
}
}

TabView::~TabView()
{
UnhookEventsAndClearFields();
if (const auto footer = TabStripFooter().try_as<winrt::FrameworkElement>())
{
m_footerMinWidthProperyChangedRevoker.revoke();
m_footerWidthProperyChangedRevoker.revoke();
}
}

void TabView::OnApplyTemplate()
{
UnhookEventsAndClearFields();
Expand Down Expand Up @@ -340,6 +350,20 @@ void TabView::OnSelectedItemPropertyChanged(const winrt::DependencyPropertyChang
UpdateSelectedItem();
}

void TabView::OnTabStripFooterPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args)
{
if (const auto oldFooter = args.OldValue().try_as<winrt::FrameworkElement>())
{
m_footerMinWidthProperyChangedRevoker.revoke();
m_footerWidthProperyChangedRevoker.revoke();
}
if (const auto newFooter = args.NewValue().try_as<winrt::FrameworkElement>())
{
m_footerMinWidthProperyChangedRevoker = RegisterPropertyChanged(newFooter,winrt::FrameworkElement::MinWidthProperty(), { this,&TabView::OnFooterSizeChanged });
m_footerWidthProperyChangedRevoker = RegisterPropertyChanged(newFooter,winrt::FrameworkElement::WidthProperty(), { this,&TabView::OnFooterSizeChanged });
}
}

void TabView::OnTabItemsSourcePropertyChanged(const winrt::DependencyPropertyChangedEventArgs&)
{
UpdateListViewItemContainerTransitions();
Expand Down Expand Up @@ -718,6 +742,11 @@ void TabView::BringSelectedTabIntoView()
}
}

void TabView::OnFooterSizeChanged(const winrt::DependencyObject& /*sender*/, const winrt::DependencyProperty& args)
{
UpdateTabWidths();
}

void TabView::OnItemsChanged(winrt::IInspectable const& item)
{
if (auto args = item.as<winrt::IVectorChangedEventArgs>())
Expand Down Expand Up @@ -1067,8 +1096,30 @@ void TabView::UpdateTabWidths(bool shouldUpdateWidths, bool fillAllAvailableSpac
if (auto&& rightContentPresenter = m_rightContentPresenter.get())
{
const winrt::Size rightContentSize = rightContentPresenter.DesiredSize();
rightContentColumn.MinWidth(rightContentSize.Width);
widthTaken += rightContentSize.Width;

if (const auto tabStripFooter = TabStripFooter().try_as<winrt::FrameworkElement>())
{
const auto minWidth = tabStripFooter.MinWidth();
const auto width = tabStripFooter.Width();
const auto footerWidth = [this, width, minWidth, rightContentSize]() {
if (minWidth < width)
{
return rightContentSize.Width < width ? width : rightContentSize.Width;
}
else
{
return rightContentSize.Width < minWidth ? minWidth : rightContentSize.Width;
}
}();

rightContentColumn.MinWidth(footerWidth);
widthTaken += footerWidth;
}
else
{
rightContentColumn.MinWidth(rightContentSize.Width);
widthTaken += rightContentSize.Width;
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions dev/TabView/TabView.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class TabView :

public:
TabView();
~TabView();

// IFrameworkElement
void OnApplyTemplate();
Expand All @@ -112,6 +113,7 @@ class TabView :
void OnTabWidthModePropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);
void OnSelectedIndexPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);
void OnSelectedItemPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);
void OnTabStripFooterPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);

void OnItemsChanged(winrt::IInspectable const& item);
void UpdateTabContent();
Expand All @@ -135,6 +137,8 @@ class TabView :
void OnScrollViewerViewChanged(winrt::IInspectable const& sender, winrt::ScrollViewerViewChangedEventArgs const& args);
void OnItemsPresenterSizeChanged(const winrt::IInspectable& sender, const winrt::SizeChangedEventArgs& args);

void OnFooterSizeChanged(const winrt::DependencyObject& /*sender*/, const winrt::DependencyProperty& args);

void OnListViewLoaded(const winrt::IInspectable& sender, const winrt::RoutedEventArgs& args);
void OnTabStripPointerExited(const winrt::IInspectable& sender, const winrt::PointerRoutedEventArgs& args);
void OnTabStripPointerEntered(const winrt::IInspectable& sender, const winrt::PointerRoutedEventArgs& args);
Expand Down Expand Up @@ -197,6 +201,9 @@ class TabView :

tracker_ref<winrt::Grid> m_shadowReceiver{ this };

PropertyChanged_revoker m_footerMinWidthProperyChangedRevoker{};
PropertyChanged_revoker m_footerWidthProperyChangedRevoker{};

winrt::ListView::Loaded_revoker m_listViewLoadedRevoker{};
winrt::ListView::PointerExited_revoker m_tabStripPointerExitedRevoker{};
winrt::ListView::PointerEntered_revoker m_tabStripPointerEnteredRevoker{};
Expand Down
3 changes: 2 additions & 1 deletion dev/TabView/TabView.idl
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ unsealed runtimeclass TabView : Windows.UI.Xaml.Controls.Control
[MUX_DEFAULT_VALUE("winrt::TabViewCloseButtonOverlayMode::Auto")]
[MUX_PROPERTY_CHANGED_CALLBACK(TRUE)]
TabViewCloseButtonOverlayMode CloseButtonOverlayMode{ get; set; };

Object TabStripHeader{ get; set; };
Windows.UI.Xaml.DataTemplate TabStripHeaderTemplate{ get; set; };

[MUX_PROPERTY_CHANGED_CALLBACK(TRUE)]
Object TabStripFooter{ get; set; };
Windows.UI.Xaml.DataTemplate TabStripFooterTemplate{ get; set; };

Expand Down
31 changes: 31 additions & 0 deletions dev/TabView/TestUI/TabViewAppwindowPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Page
x:Class="MUXControlsTestApp.TabViewAppwindowPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<muxc:TabView x:Name="Tabs"
VerticalAlignment="Stretch"
AddTabButtonClick="Tabs_AddTabButtonClick"
TabCloseRequested="Tabs_TabCloseRequested"
AllowDropTabs="True"
CanDragTabs="True"
CanReorderTabs="True"
TabDroppedOutside="Tabs_TabDroppedOutside"
TabStripDragOver="Tabs_TabStripDragOver"
TabStripDrop="Tabs_TabStripDrop"
TabDragStarting="Tabs_TabDragStarting" >

<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>
</Grid>
</Page>
Loading

0 comments on commit 73990b0

Please sign in to comment.