From 6ab7271423abbf575f80adaf519bd80ec77ee961 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Sun, 29 Mar 2020 12:49:26 +0200 Subject: [PATCH 1/2] Update way handling of datatemplateselector with null values --- dev/Repeater/APITests/ItemTemplateTests.cs | 95 +++++++++++++++++++++- dev/Repeater/ItemTemplateWrapper.cpp | 24 ++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/dev/Repeater/APITests/ItemTemplateTests.cs b/dev/Repeater/APITests/ItemTemplateTests.cs index 09257edf91..748895ce64 100644 --- a/dev/Repeater/APITests/ItemTemplateTests.cs +++ b/dev/Repeater/APITests/ItemTemplateTests.cs @@ -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; @@ -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; @@ -597,6 +598,85 @@ public void ValidateNullItemTemplateAndContainerInItems() }); } + [TestMethod] + public void VerifySelectTemplateLayoutFallback() + { + RunOnUIThread.Execute(() => + { + var dataTemplateOdd = (DataTemplate)XamlReader.Load( + @" + + "); + var dataTemplateEven = (DataTemplate)XamlReader.Load( + @" + + "); + 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, @@ -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; + } + + } } diff --git a/dev/Repeater/ItemTemplateWrapper.cpp b/dev/Repeater/ItemTemplateWrapper.cpp index f2112a71e5..76f686c1a7 100644 --- a/dev/Repeater/ItemTemplateWrapper.cpp +++ b/dev/Repeater/ItemTemplateWrapper.cpp @@ -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; From 63caa97effb868732c47073732cd6c5b628dcc87 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Sun, 29 Mar 2020 18:24:11 +0200 Subject: [PATCH 2/2] Fix typos Co-Authored-By: ItzLevvie --- dev/Repeater/APITests/ItemTemplateTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/Repeater/APITests/ItemTemplateTests.cs b/dev/Repeater/APITests/ItemTemplateTests.cs index 748895ce64..5213355c54 100644 --- a/dev/Repeater/APITests/ItemTemplateTests.cs +++ b/dev/Repeater/APITests/ItemTemplateTests.cs @@ -634,13 +634,13 @@ public void VerifySelectTemplateLayoutFallback() var element = (TextBlock)repeater.TryGetElement(i); if (i % 2 == 0) { - // Text is bound to the data for even indicies + // Text is bound to the data for even indices Verify.AreEqual(i.ToString(), element.Text); Verify.AreEqual(i, element.DataContext); } else { - // Text explicitly set on the element only for odd indicies + // Text explicitly set on the element only for odd indices Verify.AreEqual("static", element.Text); } }