Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Kinnara authored Dec 8, 2023
1 parent e599c14 commit 191fcee
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 44 deletions.
89 changes: 57 additions & 32 deletions ModernWpf.Controls/NavigationView/NavigationView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ public partial class NavigationView : ContentControl, IControlProtected

const string c_itemsContainer = "ItemsContainerGrid";
const string c_itemsContainerRow = "ItemsContainerRow";
const string c_visualItemsSeparator = "VisualItemsSeparator";
const string c_menuItemsScrollViewer = "MenuItemsScrollViewer";
const string c_footerItemsScrollViewer = "FooterItemsScrollViewer";

Expand All @@ -93,13 +92,17 @@ public partial class NavigationView : ContentControl, IControlProtected
const string c_paneHeaderToggleButtonColumn = "PaneHeaderToggleButtonColumn";
const string c_paneHeaderContentBorderRow = "PaneHeaderContentBorderRow";

const string c_separatorVisibleStateName = "SeparatorVisible";
const string c_separatorCollapsedStateName = "SeparatorCollapsed";

const int c_backButtonHeight = 40;
const int c_backButtonWidth = 40;
const int c_paneToggleButtonHeight = 40;
const int c_paneToggleButtonWidth = 40;
const int c_toggleButtonHeightWhenShouldPreserveNavigationViewRS3Behavior = 56;
const int c_backButtonRowDefinition = 1;
const float c_paneElevationTranslationZ = 32;
const int c_paneItemsSeparatorHeight = 21;

const int c_mainMenuBlockIndex = 0;
const int c_footerMenuBlockIndex = 1;
Expand Down Expand Up @@ -711,12 +714,11 @@ public override void OnApplyTemplate()
m_itemsContainerRow = GetTemplateChildT<RowDefinition>(c_itemsContainerRow, controlProtected);
m_menuItemsScrollViewer = GetTemplateChildT<FrameworkElement>(c_menuItemsScrollViewer, controlProtected);
m_footerItemsScrollViewer = GetTemplateChildT<FrameworkElement>(c_footerItemsScrollViewer, controlProtected);
m_visualItemsSeparator = GetTemplateChildT<FrameworkElement>(c_visualItemsSeparator, controlProtected);

m_itemsContainerSizeChangedRevoker?.Revoke();
if (GetTemplateChildT<FrameworkElement>(c_itemsContainer, controlProtected) is { } itemsContainerRow)
if (GetTemplateChildT<FrameworkElement>(c_itemsContainer, controlProtected) is { } itemsContainer)
{
m_itemsContainerSizeChangedRevoker = new FrameworkElementSizeChangedRevoker(itemsContainerRow, OnItemsContainerSizeChanged);
m_itemsContainerSizeChangedRevoker = new FrameworkElementSizeChangedRevoker(itemsContainer, OnItemsContainerSizeChanged);
}

if (SharedHelpers.IsRS2OrHigher())
Expand Down Expand Up @@ -1553,25 +1555,44 @@ void UpdatePaneLayout()
{
if (!IsTopNavigationView())
{
double totalAvailableHeight;
var totalAvailableHeight = TotalAvailableHeight();
double TotalAvailableHeight()
{
totalAvailableHeight = init();
double init()
if (m_itemsContainerRow is { } paneContentRow)
{
if (m_itemsContainerRow is { } paneContentRow)
var itemsContainerMargin = ItemsContainerMargin();
double ItemsContainerMargin()
{
// 20px is the padding between the two item lists
if (m_leftNavFooterContentBorder is { } paneFooter)
if (m_itemsContainer is { } itemsContainer)
{
return paneContentRow.ActualHeight - 29 - paneFooter.ActualHeight;
var margin = itemsContainer.Margin;
return margin.Top + margin.Bottom;
}
else
return 0.0;
}
var availableHeight = paneContentRow.ActualHeight - itemsContainerMargin;

// The -21 below is to account for the separator height that we need to subtract.
if (PaneFooter is { })
{
availableHeight -= c_paneItemsSeparatorHeight;
if (m_leftNavFooterContentBorder is { } paneFooter)
{
return paneContentRow.ActualHeight - 29;
availableHeight -= paneFooter.ActualHeight;
}
}
return 0.0;
else if (IsSettingsVisible)
{
availableHeight -= c_paneItemsSeparatorHeight;
}
else if (m_footerItemsSource is { } && m_menuItemsSource is { } && m_footerItemsSource.Count * m_menuItemsSource.Count > 0)
{
availableHeight -= c_paneItemsSeparatorHeight;
}

return availableHeight;
}
return 0.0;
}

// Only continue if we have a positive amount of space to manage.
Expand All @@ -1594,44 +1615,44 @@ double init()
{
var footersActualHeight = footerItemsRepeater.ActualHeight;
var menuItemsActualHeight = menuItems.ActualHeight;
if (totalAvailableHeight > menuItemsActualHeight + footersActualHeight)

if (m_footerItemsSource.Count == 0 && !IsSettingsVisible)
{
VisualStateManager.GoToState(this, c_separatorCollapsedStateName, false);
return totalAvailableHeight;
}
else if (m_menuItemsSource.Count == 0)
{
footerItemsScrollViewer.MaxHeight = totalAvailableHeight;
VisualStateManager.GoToState(this, c_separatorCollapsedStateName, false);
return 0.0;
}
else if (totalAvailableHeight > menuItemsActualHeight + footersActualHeight)
{
// We have enough space for two so let everyone get as much as they need.
footerItemsScrollViewer.MaxHeight = footersActualHeight;
if (m_visualItemsSeparator is { } separator)
{
separator.Visibility = Visibility.Collapsed;
}
VisualStateManager.GoToState(this, c_separatorCollapsedStateName, false);
return totalAvailableHeight - footersActualHeight;
}
else if (menuItemsActualHeight <= totalAvailableHeightHalf)
{
// Footer items exceed over the half, so let's limit them.
footerItemsScrollViewer.MaxHeight = totalAvailableHeight - menuItemsActualHeight;
if (m_visualItemsSeparator is { } separator)
{
separator.Visibility = Visibility.Visible;
}
VisualStateManager.GoToState(this, c_separatorVisibleStateName, false);
return menuItemsActualHeight;
}
else if (footersActualHeight <= totalAvailableHeightHalf)
{
// Menu items exceed over the half, so let's limit them.
footerItemsScrollViewer.MaxHeight = footersActualHeight;
if (m_visualItemsSeparator is { } separator)
{
separator.Visibility = Visibility.Visible;
}
VisualStateManager.GoToState(this, c_separatorVisibleStateName, false);
return totalAvailableHeight - footersActualHeight;
}
else
{
// Both are more than half the height, so split evenly.
footerItemsScrollViewer.MaxHeight = totalAvailableHeightHalf;
if (m_visualItemsSeparator is { } separator)
{
separator.Visibility = Visibility.Visible;
}
VisualStateManager.GoToState(this, c_separatorVisibleStateName, false);
return totalAvailableHeightHalf;
}
}
Expand Down Expand Up @@ -4172,6 +4193,10 @@ void PropertyChanged(DependencyPropertyChangedEventArgs args)
{
SyncItemTemplates();
}
else if (property == PaneFooterProperty)
{
UpdatePaneLayout();
}
}

void UpdateNavigationViewItemsFactory()
Expand Down Expand Up @@ -5746,7 +5771,6 @@ protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
ColumnDefinition m_paneToggleButtonIconGridColumn;
FrameworkElement m_paneTitleHolderFrameworkElement;
FrameworkElement m_paneTitleFrameworkElement;
FrameworkElement m_visualItemsSeparator;
Button m_paneSearchButton;
Button m_backButton;
Button m_closeButton;
Expand Down Expand Up @@ -5787,6 +5811,7 @@ protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
ColumnDefinition m_paneHeaderCloseButtonColumn;
ColumnDefinition m_paneHeaderToggleButtonColumn;
RowDefinition m_paneHeaderContentBorderRow;
FrameworkElement m_itemsContainer;

NavigationViewItem m_lastItemExpandedIntoFlyout;

Expand Down
34 changes: 27 additions & 7 deletions ModernWpf.Controls/NavigationView/NavigationView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -869,8 +869,16 @@
</VisualStateGroup>

<VisualStateGroup x:Name="TogglePaneGroup">
<VisualState x:Name="TogglePaneButtonVisible" />
<VisualState x:Name="TogglePaneButtonCollapsed" />
<VisualState x:Name="TogglePaneButtonVisible">
<Storyboard>
<!-- Might not need this setter when all the margins/paddings will be adequately set. -->
<!-- This MinHeight setter is only here to ensure that AutoSuggestBox doesn't draw over ToggelButton. -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="PaneContentGridToggleButtonRow" Storyboard.TargetProperty="MinHeight">
<DiscreteDoubleKeyFrame KeyTime="0" Value="{DynamicResource NavigationViewPaneHeaderRowMinHeight}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>

<VisualStateGroup x:Name="HeaderGroup">
Expand Down Expand Up @@ -978,6 +986,17 @@
</Storyboard>
</VisualState>
</VisualStateGroup>

<VisualStateGroup x:Name="PaneSeparatorStates">
<VisualState x:Name="SeparatorCollapsed"/>
<VisualState x:Name="SeparatorVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VisualItemsSeparator" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<!-- Button grid -->
Expand Down Expand Up @@ -1220,7 +1239,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="0"/>
<!-- above button margin + back button space -->
<RowDefinition x:Name="PaneContentGridToggleButtonRow" Height="Auto" MinHeight="{DynamicResource NavigationViewPaneHeaderRowMinHeight}"/>
<RowDefinition x:Name="PaneContentGridToggleButtonRow" Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="8"/>
Expand Down Expand Up @@ -1295,10 +1314,10 @@
<!-- "Non header" content -->
<Grid x:Name="ItemsContainerGrid" Grid.Row="6" Margin="0,0,0,8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="{DynamicResource NavigationViewItemOnLeftMinHeight}"/> <!-- MenuItems -->
<RowDefinition Height="*" MinHeight="21"/> <!-- Fill spacing -->
<RowDefinition Height="Auto" /> <!-- PaneFooter -->
<RowDefinition Height="Auto" MinHeight="{DynamicResource NavigationViewItemOnLeftMinHeight}"/> <!-- FooterItems -->
<RowDefinition Height="Auto"/> <!-- MenuItems -->
<RowDefinition Height="*"/> <!-- Fill spacing -->
<RowDefinition Height="Auto" /> <!-- PaneFooter -->
<RowDefinition Height="Auto"/> <!-- FooterItems -->
</Grid.RowDefinitions>

<!-- MenuItems -->
Expand Down Expand Up @@ -1329,7 +1348,8 @@

<!-- FooterItems -->
<local:ItemsRepeaterScrollHost Grid.Row="3">
<ScrollViewer x:Name="FooterItemsScrollViewer">
<ScrollViewer x:Name="FooterItemsScrollViewer"
VerticalScrollBarVisibility="Auto">
<!-- contract7Present:VerticalAnchorRatio="1" -->
<local:ItemsRepeater
x:Name="FooterMenuItemsHost" VerticalAlignment="Bottom"/>
Expand Down
7 changes: 6 additions & 1 deletion test/NavigationView_TestUI/Footer/PaneLayoutTestPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<muxc:NavigationView x:Name="RootNavigationView">
<!-- Set max-height to speed up "filling" of menu/footer items to test behavior -->
<muxc:NavigationView x:Name="RootNavigationView" MaxHeight="500" IsPaneOpen="True" PaneDisplayMode="Left">

<StackPanel>
<muxc:RadioButtons SelectionChanged="TestCaseSelectionChanged" SelectedIndex="2">
Expand All @@ -22,6 +23,10 @@
<Button Click="AddMenuItemButton_Click" AutomationProperties.Name="AddMenuItemButton" Margin="0,4,0,0">Add menu item</Button>
<Button Click="AddFooterItemButton_Click" AutomationProperties.Name="AddFooterItemButton">Add footer item</Button>
<Button Click="ResetCollectionsButton_Click" AutomationProperties.Name="ResetCollectionsButton">Reset collections</Button>
<Button Click="ClearCollectionsButton_Click" AutomationProperties.Name="ClearCollectionsButton">Clear collections</Button>
<CheckBox x:Name="IsSettingsEnabledCheckbox" AutomationProperties.Name="IsSettingsEnabledCheckbox"
IsChecked="True" Content="Settings visible"
Checked="IsSettingsEnabledCheckbox_Checked" Unchecked="IsSettingsEnabledCheckbox_UnChecked"/>

<Button Click="GetLayoutHeightsButton_Click" Margin="0,4,0,0" AutomationProperties.Name="GetLayoutHeightsButton">Get layout heights</Button>
<TextBlock x:Name="LayoutHeightsReport" AutomationProperties.Name="LayoutHeightsReport"/>
Expand Down
23 changes: 19 additions & 4 deletions test/NavigationView_TestUI/Footer/PaneLayoutTestPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public PaneLayoutTestPage()
this.InitializeComponent();


for (int i = 0; i < 4; i++)
for (int i = 0; i < 3; i++)
{
menuItems.Add(
new NavigationViewItem() {
Expand All @@ -31,7 +31,7 @@ public PaneLayoutTestPage()
}


for (int i = 0; i < 4; i++)
for (int i = 0; i < 3; i++)
{
footerItems.Add(
new NavigationViewItem() {
Expand Down Expand Up @@ -77,23 +77,38 @@ private void AddFooterItemButton_Click(object sender, RoutedEventArgs e)

private void ResetCollectionsButton_Click(object sender, RoutedEventArgs e)
{
for (int i = menuItems.Count - 1; i > 3; i--)
for (int i = menuItems.Count - 1; i > 2; i--)
{
menuItems.RemoveAt(i);
}

for (int i = footerItems.Count - 1; i > 3; i--)
for (int i = footerItems.Count - 1; i > 2; i--)
{
footerItems.RemoveAt(i);
}
}

private void ClearCollectionsButton_Click(object sender, RoutedEventArgs e)
{
menuItems.Clear();
footerItems.Clear();
}

private void GetLayoutHeightsButton_Click(object sender, RoutedEventArgs e)
{
var itemsScroll = VisualTreeUtils.FindVisualChildByName(RootNavigationView, "MenuItemsScrollViewer");
var footerScroll = VisualTreeUtils.FindVisualChildByName(RootNavigationView, "FooterItemsScrollViewer");
LayoutHeightsReport.Text = itemsScroll.ActualHeight + ";" + footerScroll.ActualHeight;
}

private void IsSettingsEnabledCheckbox_Checked(object sender, RoutedEventArgs e)
{
RootNavigationView.IsSettingsVisible = true;
}

private void IsSettingsEnabledCheckbox_UnChecked(object sender, RoutedEventArgs e)
{
RootNavigationView.IsSettingsVisible = false;
}
}
}

0 comments on commit 191fcee

Please sign in to comment.