From 1d04de0cca28c0e6df9b2ada725af55f68003430 Mon Sep 17 00:00:00 2001 From: Yimeng Wu Date: Mon, 31 Aug 2020 00:40:09 +0800 Subject: [PATCH] Better workaround for ItemsRepeater --- .../NavigationView/NavigationView.cs | 3 - .../NavigationView/NavigationViewItem.cs | 1 - .../Repeater/ItemsRepeater/ItemsRepeater.cs | 4 +- .../ItemsRepeater/ItemsRepeater.wpf.cs | 75 +++---------------- .../Repeater/Layouts/VirtualizationInfo.cs | 2 + .../ControlPages/ItemsRepeaterPage.xaml | 6 +- .../ControlPages/ItemsRepeaterPage.xaml.cs | 14 ++++ .../ControlPages/ListViewPage.xaml.cs | 11 ++- 8 files changed, 44 insertions(+), 72 deletions(-) diff --git a/ModernWpf.Controls/NavigationView/NavigationView.cs b/ModernWpf.Controls/NavigationView/NavigationView.cs index 5ba5a0b7..02212006 100644 --- a/ModernWpf.Controls/NavigationView/NavigationView.cs +++ b/ModernWpf.Controls/NavigationView/NavigationView.cs @@ -472,7 +472,6 @@ public override void OnApplyTemplate() m_leftNavRepeaterGettingFocusHelper.GettingFocus += OnRepeaterGettingFocus; leftNavRepeater.ItemTemplate = m_navigationViewItemsFactory; - leftNavRepeater.AlwaysInvalidateMeasureOnChildDesiredSizeChanged = true; } // Change code to NOT do this if we're in left nav mode, to prevent it from being realized: @@ -496,7 +495,6 @@ public override void OnApplyTemplate() m_topNavRepeaterGettingFocusHelper.GettingFocus += OnRepeaterGettingFocus; topNavRepeater.ItemTemplate = m_navigationViewItemsFactory; - topNavRepeater.AlwaysInvalidateMeasureOnChildDesiredSizeChanged = true; } // Change code to NOT do this if we're in left nav mode, to prevent it from being realized: @@ -516,7 +514,6 @@ public override void OnApplyTemplate() topNavListOverflowRepeater.ElementClearing += OnRepeaterElementClearing; topNavListOverflowRepeater.ItemTemplate = m_navigationViewItemsFactory; - topNavListOverflowRepeater.AlwaysInvalidateMeasureOnChildDesiredSizeChanged = true; } if (GetTemplateChild(c_topNavOverflowButton) is Button topNavOverflowButton) diff --git a/ModernWpf.Controls/NavigationView/NavigationViewItem.cs b/ModernWpf.Controls/NavigationView/NavigationViewItem.cs index aad8033a..8f4b628f 100644 --- a/ModernWpf.Controls/NavigationView/NavigationViewItem.cs +++ b/ModernWpf.Controls/NavigationView/NavigationViewItem.cs @@ -125,7 +125,6 @@ public override void OnApplyTemplate() m_repeaterElementClearingRevoker = new ItemsRepeaterElementClearingRevoker(repeater, nvImpl.OnRepeaterElementClearing); repeater.ItemTemplate = nvImpl.GetNavigationViewItemsFactory(); - repeater.AlwaysInvalidateMeasureOnChildDesiredSizeChanged = true; } UpdateRepeaterItemsSource(); diff --git a/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.cs b/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.cs index 44262168..ce0f3ce3 100644 --- a/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.cs +++ b/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.cs @@ -111,7 +111,6 @@ protected override Size MeasureOverride(Size availableSize) m_viewportManager.SetLayoutExtent(extent); m_lastAvailableSize = availableSize; - OnLayoutEvent(LayoutEvent.Measure); return desiredSize; } finally @@ -175,14 +174,13 @@ protected override Size ArrangeOverride(Size finalSize) } virtInfo.ArrangeBounds = newBounds; + virtInfo.DesiredSize = element.DesiredSize; } } m_viewportManager.OnOwnerArranged(); AnimationManager.OnOwnerArranged(); - OnLayoutEvent(LayoutEvent.Arrange); - return arrangeSize; } finally diff --git a/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.wpf.cs b/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.wpf.cs index 53990314..c600fba4 100644 --- a/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.wpf.cs +++ b/ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.wpf.cs @@ -1,85 +1,34 @@ -using System.Collections.Generic; -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace ModernWpf.Controls { partial class ItemsRepeater { - internal bool AlwaysInvalidateMeasureOnChildDesiredSizeChanged { get; set; } - + // WPF-specific workaround to avoid freezing and improve performance protected override void OnChildDesiredSizeChanged(UIElement child) { - if (AlwaysInvalidateMeasureOnChildDesiredSizeChanged) + var virtInfo = TryGetVirtualizationInfo(child); + if (virtInfo != null && virtInfo.IsRealized) { - base.OnChildDesiredSizeChanged(child); - return; - } - - bool ignore = true; - - var last = m_layoutEvents.Last; - if (last != null) - { - if (last.Value == LayoutEvent.Measure) - { - if (last.Previous?.Value != LayoutEvent.Measure) - { - ignore = false; - } - } - else if (last.Value == LayoutEvent.Arrange) + var oldDesiredSize = virtInfo.DesiredSize; + if (!oldDesiredSize.IsEmpty) { - var previous = last.Previous; - if (previous == null || previous.Value == LayoutEvent.Measure) + var newDesiredSize = child.DesiredSize; + var renderSize = child.RenderSize; + + if (newDesiredSize.Height != oldDesiredSize.Height && renderSize.Height == oldDesiredSize.Height || + newDesiredSize.Width != oldDesiredSize.Width && renderSize.Width == oldDesiredSize.Width) { - var virtInfo = GetVirtualizationInfo(child); - if (virtInfo.IsPinned) - { - ignore = false; - } + base.OnChildDesiredSizeChanged(child); } } } - else - { - ignore = false; - } - - if (!ignore) - { - OnLayoutEvent(LayoutEvent.ChildDesiredSizeChanged); - base.OnChildDesiredSizeChanged(child); - } } protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent) { return new RepeaterUIElementCollection(this, logicalParent); } - - private void OnLayoutEvent(LayoutEvent e) - { - if (AlwaysInvalidateMeasureOnChildDesiredSizeChanged) - { - return; - } - - if (m_layoutEvents.Count >= 2) - { - m_layoutEvents.RemoveFirst(); - } - - m_layoutEvents.AddLast(e); - } - - private enum LayoutEvent - { - Measure, - Arrange, - ChildDesiredSizeChanged - } - - private readonly LinkedList m_layoutEvents = new LinkedList(); } } diff --git a/ModernWpf.Controls/Repeater/Layouts/VirtualizationInfo.cs b/ModernWpf.Controls/Repeater/Layouts/VirtualizationInfo.cs index 084b1788..03fbdeb5 100644 --- a/ModernWpf.Controls/Repeater/Layouts/VirtualizationInfo.cs +++ b/ModernWpf.Controls/Repeater/Layouts/VirtualizationInfo.cs @@ -147,6 +147,8 @@ public void UpdateIndex(int newIndex) public bool AutoRecycleCandidate { get; set; } = false; + public Size DesiredSize { get; set; } = Size.Empty; + private uint m_pinCounter = 0u; } } \ No newline at end of file diff --git a/ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml b/ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml index dfbeb1bd..cc5201d8 100644 --- a/ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml +++ b/ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml @@ -191,7 +191,7 @@ @@ -361,6 +361,10 @@ x:Name="changeFirstItemButton" Content="Change first item" Click="ChangeFirstItemButton_Click" /> +