Skip to content

Commit

Permalink
Better workaround for ItemsRepeater
Browse files Browse the repository at this point in the history
  • Loading branch information
Kinnara committed Aug 30, 2020
1 parent 467e8de commit 1d04de0
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 72 deletions.
3 changes: 0 additions & 3 deletions ModernWpf.Controls/NavigationView/NavigationView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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)
Expand Down
1 change: 0 additions & 1 deletion ModernWpf.Controls/NavigationView/NavigationViewItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ public override void OnApplyTemplate()
m_repeaterElementClearingRevoker = new ItemsRepeaterElementClearingRevoker(repeater, nvImpl.OnRepeaterElementClearing);

repeater.ItemTemplate = nvImpl.GetNavigationViewItemsFactory();
repeater.AlwaysInvalidateMeasureOnChildDesiredSizeChanged = true;
}

UpdateRepeaterItemsSource();
Expand Down
4 changes: 1 addition & 3 deletions ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ protected override Size MeasureOverride(Size availableSize)

m_viewportManager.SetLayoutExtent(extent);
m_lastAvailableSize = availableSize;
OnLayoutEvent(LayoutEvent.Measure);
return desiredSize;
}
finally
Expand Down Expand Up @@ -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
Expand Down
75 changes: 12 additions & 63 deletions ModernWpf.Controls/Repeater/ItemsRepeater/ItemsRepeater.wpf.cs
Original file line number Diff line number Diff line change
@@ -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<LayoutEvent> m_layoutEvents = new LinkedList<LayoutEvent>();
}
}
2 changes: 2 additions & 0 deletions ModernWpf.Controls/Repeater/Layouts/VirtualizationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
6 changes: 5 additions & 1 deletion ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
<TextBlock
Grid.Column="1"
Grid.Row="1"
Text="{Binding Company, Mode=OneTime}"
Text="{Binding Company}"
Style="{StaticResource BodyTextBlockStyle}"
Margin="12,0,0,6" />
</Grid>
Expand Down Expand Up @@ -361,6 +361,10 @@
x:Name="changeFirstItemButton"
Content="Change first item"
Click="ChangeFirstItemButton_Click" />
<Button
x:Name="modifyFirstItemButton"
Content="Modify first item"
Click="ModifyFirstItemButton_Click" />
</ui:SimpleStackPanel>
</sc:ControlExample.Options>
</sc:ControlExample>
Expand Down
14 changes: 14 additions & 0 deletions ModernWpf.SampleApp/ControlPages/ItemsRepeaterPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,20 @@ private void ChangeFirstItemButton_Click(object sender, RoutedEventArgs e)
var contacts = (ObservableCollection<Contact>)DataContext;
contacts[0] = new Contact("First", "Last", "Line 1\nLine 2");
}

private void ModifyFirstItemButton_Click(object sender, RoutedEventArgs e)
{
var contacts = (ObservableCollection<Contact>)DataContext;
var firstContact = contacts[0];
if (firstContact.Company.Contains("\n"))
{
firstContact.ChangeCompany("Line 1");
}
else
{
firstContact.ChangeCompany("Line 1\nLine 2");
}
}
}

public class NestedCategory
Expand Down
11 changes: 10 additions & 1 deletion ModernWpf.SampleApp/ControlPages/ListViewPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
Expand All @@ -24,7 +25,7 @@ private async void OnLoaded(object sender, RoutedEventArgs e)
}
}

public class Contact
public class Contact : INotifyPropertyChanged
{
#region Properties
public string FirstName { get; private set; }
Expand All @@ -40,6 +41,8 @@ public Contact(string firstName, string lastName, string company)
Company = company;
}

public event PropertyChangedEventHandler PropertyChanged;

#region Public Methods
public static Task<ObservableCollection<Contact>> GetContactsAsync()
{
Expand Down Expand Up @@ -77,6 +80,12 @@ public override string ToString()
{
return Name;
}

public void ChangeCompany(string company)
{
Company = company;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Company)));
}
#endregion
}

Expand Down

0 comments on commit 1d04de0

Please sign in to comment.