Skip to content

Commit

Permalink
fix(ListView): unnecessary SelectTemplate call on collection reset
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed May 12, 2023
1 parent 65f2ccb commit fab5c1c
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public partial class Given_ListViewBase // resources
{
private ResourceDictionary _testsResources;

[TestInitialize]
public void Init()
{
_testsResources = new TestsResources();
}

private Style BasicContainerStyle => _testsResources["BasicListViewContainerStyle"] as Style;

private Style ContainerMarginStyle => _testsResources["ListViewContainerMarginStyle"] as Style;
Expand Down Expand Up @@ -88,12 +94,6 @@ public partial class Given_ListViewBase // resources
#endif
public partial class Given_ListViewBase // test cases
{
[TestInitialize]
public void Init()
{
_testsResources = new TestsResources();
}

[TestMethod]
[RunsOnUIThread]
public void ValidSelectionChange()
Expand Down Expand Up @@ -2803,6 +2803,41 @@ bool IsVisible(object x) => x is UIElement uie
: false;
#endif
}

[TestMethod]
public async Task When_ItemsSource_INCC_Reset()
{
// note: In order to repro #12059 (extra SelectTemplate call) & SelectTemplateCore called with incorrect item (the `source` is passed as item),
// we need a binding that inherits source from DataContext, and not: new Binding { Source = source }, or direct assignment.
Action<object> onSelectTemplateHook = null;

var source = new ObservableCollection<object>(new[] { new object() });
var sut = new ListView
{
ItemTemplateSelector = new LambdaDataTemplateSelector(x =>
{
onSelectTemplateHook?.Invoke(x);
return TextBlockItemTemplate;
}),
};
sut.DataContext = source;
sut.SetBinding(ItemsControl.ItemsSourceProperty, new Binding());

WindowHelper.WindowContent = sut;
await WindowHelper.WaitForLoaded(sut);
await WindowHelper.WaitForIdle();

// Clearing the source SHOULD NOT cause the ItemTemplateSelector to be re-evaluated,
// especially so when we are leaving the source empty.
var wasSelectTemplateCalled = false;
onSelectTemplateHook = x => wasSelectTemplateCalled = true;
source.Clear();

if (wasSelectTemplateCalled)
{
Assert.Fail("DataTemplateSelector.SelectTemplateCore is invoked during an INCC reset.");
}
}
}

public partial class Given_ListViewBase // data class, data-context, view-model, template-selector
Expand Down Expand Up @@ -3035,6 +3070,19 @@ public string Display
set => SetAndRaiseIfChanged(ref _display, value);
}
}

public class LambdaDataTemplateSelector : DataTemplateSelector
{
private readonly Func<object, DataTemplate> _impl;

public LambdaDataTemplateSelector(Func<object, DataTemplate> impl)
{
this._impl = impl;
}

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) => SelectTemplateCore(item);
protected override DataTemplate SelectTemplateCore(object item) => _impl(item);
}
}

public partial class Given_ListViewBase // helpers
Expand Down
6 changes: 4 additions & 2 deletions src/Uno.UI/UI/Xaml/Controls/ItemsControl/ItemsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1171,8 +1171,6 @@ internal void CleanUpContainer(global::Windows.UI.Xaml.DependencyObject element)
}
else if (element is ContentControl contentControl)
{
contentControl.ClearValue(DataContextProperty);

if (!isOwnContainer)
{
static void ClearPropertyWhenNoExpression(ContentControl target, DependencyProperty property)
Expand Down Expand Up @@ -1201,6 +1199,10 @@ static void ClearPropertyWhenNoExpression(ContentControl target, DependencyPrope
ClearPropertyWhenNoExpression(contentControl, ContentControl.ContentTemplateSelectorProperty);
}
}

// We are clearing the DataContext last. Because if there is a binding set on any of the above properties, Content(Template(Selector)?)?,
// clearing the DC can cause the data-bound property to be unnecessarily re-evaluated with an inherited DC from the visual parent.
contentControl.ClearValue(DataContextProperty);
}
}

Expand Down

0 comments on commit fab5c1c

Please sign in to comment.