Skip to content

Commit

Permalink
ItemsRepeater: Fix bug where inserting at front would result in unren…
Browse files Browse the repository at this point in the history
…dered items (#3699)

* Add failing test

* Fix bug

* Update test
  • Loading branch information
marcelwgn authored Dec 14, 2020
1 parent 50f29e7 commit 864c068
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 4 deletions.
2 changes: 1 addition & 1 deletion dev/Repeater/APITests/FlowLayoutCollectionChangeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public void CanRemoveItemsStartingBeforeRealizedRange()
repeater.UpdateLayout();

realized = VerifyRealizedRange(repeater, dataSource);
Verify.IsLessThanOrEqual(3, realized);
Verify.IsLessThanOrEqual(2, realized);
});
}

Expand Down
2 changes: 1 addition & 1 deletion dev/Repeater/ElementManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ void ElementManager::OnItemsAdded(int index, int count)
// to insert items.
const int lastRealizedDataIndex = m_firstRealizedDataIndex + GetRealizedElementCount() - 1;
const int newStartingIndex = index;
if (newStartingIndex > m_firstRealizedDataIndex &&
if (newStartingIndex >= m_firstRealizedDataIndex &&
newStartingIndex <= lastRealizedDataIndex)
{
// Inserted within the realized range
Expand Down
21 changes: 21 additions & 0 deletions dev/Repeater/InteractionTests/RepeaterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,26 @@ public void TestCleanup()
{
TestCleanupHelper.Cleanup();
}

[TestMethod]
public void InsertAtStartBehavior()
{
using (var setup = new TestSetupHelper(new[] { "ItemsRepeater Tests" }))
{
FindElement.ByName("Basic Demo").Click();
var addItemButton = FindElement.ByName("InsertAtStartButton");
var itemCountLabel = FindElement.ByName("InsertAtStartChildCountLabel");

for (int i = 0; i < 10; i++)
{
// For performance reasons, invoking the button also reevaluates the children count.
// Technically there are i+1 children, but the button is one item behind.
// Since i starts at 0, everything lines up correctly in here and we don't have to invoke two buttons.
addItemButton.Click();
Wait.ForIdle();
Verify.AreEqual(i.ToString(), itemCountLabel.GetText());
}
}
}
}
}
23 changes: 22 additions & 1 deletion dev/Repeater/TestUI/Samples/BasicDemo.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<StackPanel>
<Button x:Name="goBackButton">Back</Button>
<Button Click="OnAddRecipeButton_Click" AutomationProperties.Name="InsertAtStartButton">Insert item at start</Button>
<TextBlock x:Name="InsertAtStartChildCountLabel" AutomationProperties.Name="InsertAtStartChildCountLabel" Text="0"/>
</StackPanel>

<controls:ItemsRepeaterScrollHost x:Name="tracker" Grid.Row="1">
Expand All @@ -42,7 +45,25 @@
</controls:ItemsRepeater>
</ScrollViewer>
</controls:ItemsRepeaterScrollHost>


<controls:ItemsRepeaterScrollHost Grid.Row="2">
<ScrollViewer>
<controls:ItemsRepeater Grid.Row="2" MinHeight="100"
x:Name="insertStartTestRepeater"
ItemsSource="{x:Bind simpleStringsList}">
<controls:ItemsRepeater.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</controls:ItemsRepeater.ItemTemplate>
</controls:ItemsRepeater>
</ScrollViewer>
</controls:ItemsRepeaterScrollHost>
<!-- No ItemsRepeaterScrollHost for RS5+. ScrollViewer can do the anchoring itself-->
<!--controls:ScrollViewer x:Name="scrollViewer" Height="500">
<controls:ItemsRepeater x:Name="repeater" VerticalCacheLength="0" />
Expand Down
13 changes: 12 additions & 1 deletion dev/Repeater/TestUI/Samples/BasicDemo.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,36 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using RecyclingElementFactory = Microsoft.UI.Xaml.Controls.RecyclingElementFactory;
using SelectTemplateEventArgs = Microsoft.UI.Xaml.Controls.SelectTemplateEventArgs;

namespace MUXControlsTestApp.Samples
{
public sealed partial class BasicDemo : Page
{
public ObservableCollection<string> simpleStringsList = new ObservableCollection<string>();

public BasicDemo()
{
this.InitializeComponent();
goBackButton.Click += delegate { Frame.GoBack(); };
repeater.ItemTemplate = elementFactory;
repeater.ItemTemplate = elementFactory;
var stack = repeater.Layout as StackLayout;
int numItems = (stack != null && stack.DisableVirtualization) ? 10 : 10000;
repeater.ItemsSource = Enumerable.Range(0, numItems).Select(x => x.ToString());
}

private void OnAddRecipeButton_Click(object sender, RoutedEventArgs e)
{
InsertAtStartChildCountLabel.Text = VisualTreeHelper.GetChildrenCount(insertStartTestRepeater).ToString();
simpleStringsList.Insert(0,"Item" + simpleStringsList.Count );
}

private void OnSelectTemplateKey(RecyclingElementFactory sender, SelectTemplateEventArgs args)
{
args.TemplateKey = (int.Parse(args.DataContext.ToString()) % 2 == 0) ? "even" : "odd";
Expand Down

0 comments on commit 864c068

Please sign in to comment.