Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update way handling of DataTemplateSelector with null values #2185

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion dev/Repeater/APITests/ItemTemplateTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Common;
Expand All @@ -13,6 +13,7 @@
using Windows.UI.Xaml.Tests.MUXControls.ApiTests.RepeaterTests.Common;
using Windows.UI.Xaml.Tests.MUXControls.ApiTests.RepeaterTests.Common.Mocks;
using Microsoft.UI.Xaml.Controls;
using System;

#if USING_TAEF
using WEX.TestExecution;
Expand Down Expand Up @@ -597,6 +598,85 @@ public void ValidateNullItemTemplateAndContainerInItems()
});
}

[TestMethod]
public void VerifySelectTemplateLayoutFallback()
{
RunOnUIThread.Execute(() =>
{
var dataTemplateOdd = (DataTemplate)XamlReader.Load(
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<TextBlock Text='static' Height='30' />
</DataTemplate>");
var dataTemplateEven = (DataTemplate)XamlReader.Load(
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<TextBlock Text='{Binding}' Height='30' />
</DataTemplate>");
ItemsRepeater repeater = null;
const int numItems = 10;
var selector = new MyContainerSelector() {
TemplateOdd = dataTemplateOdd,
TemplateEven = dataTemplateEven
};

Content = CreateAndInitializeRepeater
(
itemsSource: Enumerable.Range(0, numItems),
elementFactory: selector,
layout: new StackLayout(),
repeater: ref repeater
);

Content.UpdateLayout();

Verify.AreEqual(numItems, VisualTreeHelper.GetChildrenCount(repeater));
for (int i = 0; i < numItems; i++)
{
var element = (TextBlock)repeater.TryGetElement(i);
if (i % 2 == 0)
{
// Text is bound to the data for even indicies
Verify.AreEqual(i.ToString(), element.Text);
Verify.AreEqual(i, element.DataContext);
}
else
{
// Text explicitly set on the element only for odd indicies
Verify.AreEqual("static", element.Text);
}
}
});
}

[TestMethod]
public void VerifyNullTemplateGivesMeaningfullError()
{
RunOnUIThread.Execute(() =>
{
ItemsRepeater repeater = null;
const int numItems = 10;

Content = CreateAndInitializeRepeater
(
itemsSource: Enumerable.Range(0, numItems),
// DataTemplateSelector always returns null, but throws hresult_invalid_argument when container is null
elementFactory: new DataTemplateSelector(),
layout: new StackLayout(),
repeater: ref repeater
);
bool threwException = false;
try
{
Content.UpdateLayout();
} catch(Exception e)
{
threwException = true;
Verify.IsTrue(e.Message.Contains("Null encountered as data template. That is not a valid value for a data template, and can not be used."));
}
Verify.IsTrue(threwException);
// Set content to null so testapp does not try to update layout again
Content = null;
});
}
private ItemsRepeaterScrollHost CreateAndInitializeRepeater(
object itemsSource,
Layout layout,
Expand Down Expand Up @@ -651,4 +731,17 @@ protected override DataTemplate SelectTemplateCore(object item)
return (((int)item) % 2 == 0) ? TemplateEven : TemplateOdd;
}
}

public class MyContainerSelector : DataTemplateSelector
{
public DataTemplate TemplateOdd { get; set; }

public DataTemplate TemplateEven { get; set; }

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return (((int)item) % 2 == 0) ? TemplateEven : TemplateOdd;
}

}
}
24 changes: 24 additions & 0 deletions dev/Repeater/ItemTemplateWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ void ItemTemplateWrapper::TemplateSelector(winrt::DataTemplateSelector const& va
winrt::UIElement ItemTemplateWrapper::GetElement(winrt::ElementFactoryGetArgs const& args)
{
auto selectedTemplate = m_dataTemplate ? m_dataTemplate : m_dataTemplateSelector.SelectTemplate(args.Data());
// Check if selected template we got is valid
if (selectedTemplate == nullptr)
{
// Null template, use other SelectTemplate method
try
{
selectedTemplate = m_dataTemplateSelector.SelectTemplate(args.Data(), nullptr);
}
catch (winrt::hresult_error e)
{
// The default implementation of SelectTemplate(IInspectable item, ILayout container) throws invalid arg for null container
// To not force everbody to provide an implementation of that, catch that here
if (e.code().value != E_INVALIDARG)
{
throw e;
}
}

if (selectedTemplate == nullptr)
{
// Still nullptr, fail with a reasonable message now.
throw winrt::hresult_invalid_argument(L"Null encountered as data template. That is not a valid value for a data template, and can not be used.");
}
}
auto recyclePool = RecyclePool::GetPoolInstance(selectedTemplate);
winrt::UIElement element = nullptr;

Expand Down