From db7b371a8155a81800f4432d6052ef54cb20642a Mon Sep 17 00:00:00 2001 From: Ranjesh <28935693+ranjeshj@users.noreply.github.com> Date: Fri, 25 Jan 2019 12:06:26 -0800 Subject: [PATCH] Allow ItemsRepeater's layout to take non virtualizing layouts (#222) * fix debug_test configuration * fix back version * updates. still need to add the adapter. * add the adapter to go from virtualizing context to non-virtualizing context. * adding a test * cr fixes * undo changes to solution file --- dev/Generated/ItemsRepeater.properties.cpp | 12 +- dev/Generated/ItemsRepeater.properties.h | 4 +- .../APITests/Common/NonVirtualStackLayout.cs | 44 ++++++ dev/Repeater/APITests/LayoutTests.cs | 33 ++++- .../APITests/Repeater_APITests.projitems | 1 + dev/Repeater/ItemsRepeater.cpp | 18 ++- dev/Repeater/ItemsRepeater.h | 8 +- dev/Repeater/ItemsRepeater.idl | 2 +- dev/Repeater/Layout.cpp | 27 +++- dev/Repeater/Repeater.vcxitems | 2 + dev/Repeater/RepeaterFactory.cpp | 4 +- dev/Repeater/TestUI/Repeater_TestUI.projitems | 2 +- .../NonVirtualStackLayoutSamplePage.xaml | 14 +- .../NonVirtualStackLayoutSamplePage.xaml.cs | 2 + dev/Repeater/VirtualLayoutContextAdapter.cpp | 48 ++++++ dev/Repeater/VirtualLayoutContextAdapter.h | 139 ++++++++++++++++++ dev/Repeater/VirtualizingLayoutContext.cpp | 13 +- dev/Repeater/VirtualizingLayoutContext.h | 6 +- 18 files changed, 338 insertions(+), 41 deletions(-) create mode 100644 dev/Repeater/APITests/Common/NonVirtualStackLayout.cs create mode 100644 dev/Repeater/VirtualLayoutContextAdapter.cpp create mode 100644 dev/Repeater/VirtualLayoutContextAdapter.h diff --git a/dev/Generated/ItemsRepeater.properties.cpp b/dev/Generated/ItemsRepeater.properties.cpp index d76069d531..3eb08b8a34 100644 --- a/dev/Generated/ItemsRepeater.properties.cpp +++ b/dev/Generated/ItemsRepeater.properties.cpp @@ -86,10 +86,10 @@ void ItemsRepeaterProperties::EnsureProperties() s_LayoutProperty = InitializeDependencyProperty( L"Layout", - winrt::name_of(), + winrt::name_of(), winrt::name_of(), false /* isAttached */, - ValueHelper::BoxedDefaultValue(), + ValueHelper::BoxedDefaultValue(), nullptr); } if (!s_VerticalCacheLengthProperty) @@ -166,14 +166,14 @@ winrt::IInspectable ItemsRepeaterProperties::ItemTemplate() return ValueHelper::CastOrUnbox(static_cast(this)->GetValue(s_ItemTemplateProperty)); } -void ItemsRepeaterProperties::Layout(winrt::VirtualizingLayout const& value) +void ItemsRepeaterProperties::Layout(winrt::Layout const& value) { - static_cast(this)->SetValue(s_LayoutProperty, ValueHelper::BoxValueIfNecessary(value)); + static_cast(this)->SetValue(s_LayoutProperty, ValueHelper::BoxValueIfNecessary(value)); } -winrt::VirtualizingLayout ItemsRepeaterProperties::Layout() +winrt::Layout ItemsRepeaterProperties::Layout() { - return ValueHelper::CastOrUnbox(static_cast(this)->GetValue(s_LayoutProperty)); + return ValueHelper::CastOrUnbox(static_cast(this)->GetValue(s_LayoutProperty)); } void ItemsRepeaterProperties::VerticalCacheLength(double value) diff --git a/dev/Generated/ItemsRepeater.properties.h b/dev/Generated/ItemsRepeater.properties.h index 64e9aa31a6..f89013ce73 100644 --- a/dev/Generated/ItemsRepeater.properties.h +++ b/dev/Generated/ItemsRepeater.properties.h @@ -24,8 +24,8 @@ class ItemsRepeaterProperties void ItemTemplate(winrt::IInspectable const& value); winrt::IInspectable ItemTemplate(); - void Layout(winrt::VirtualizingLayout const& value); - winrt::VirtualizingLayout Layout(); + void Layout(winrt::Layout const& value); + winrt::Layout Layout(); void VerticalCacheLength(double value); double VerticalCacheLength(); diff --git a/dev/Repeater/APITests/Common/NonVirtualStackLayout.cs b/dev/Repeater/APITests/Common/NonVirtualStackLayout.cs new file mode 100644 index 0000000000..4e5b691e20 --- /dev/null +++ b/dev/Repeater/APITests/Common/NonVirtualStackLayout.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +#if !BUILD_WINDOWS +using NonVirtualizingLayoutContext = Microsoft.UI.Xaml.Controls.NonVirtualizingLayoutContext; +using NonVirtualizingLayout = Microsoft.UI.Xaml.Controls.NonVirtualizingLayout; +#endif + +namespace Windows.UI.Xaml.Tests.MUXControls.ApiTests.RepeaterTests.Common +{ + public class NonVirtualStackLayout : NonVirtualizingLayout + { + protected override Size MeasureOverride(NonVirtualizingLayoutContext context, Size availableSize) + { + double extentHeight = 0.0; + double extentWidth = 0.0; + foreach (var element in context.Children) + { + element.Measure(availableSize); + extentHeight += element.DesiredSize.Height; + extentWidth = Math.Max(extentWidth, element.DesiredSize.Width); + } + + return new Size(extentWidth, extentHeight); + } + + protected override Size ArrangeOverride(NonVirtualizingLayoutContext context, Size finalSize) + { + double offset = 0.0; + foreach (var element in context.Children) + { + element.Arrange(new Rect(0, offset, element.DesiredSize.Width, element.DesiredSize.Height)); + offset += element.DesiredSize.Height; + } + + return finalSize; + } + } +} \ No newline at end of file diff --git a/dev/Repeater/APITests/LayoutTests.cs b/dev/Repeater/APITests/LayoutTests.cs index a57351f83c..f17fa03f95 100644 --- a/dev/Repeater/APITests/LayoutTests.cs +++ b/dev/Repeater/APITests/LayoutTests.cs @@ -61,7 +61,7 @@ public void ValidateMappingAndAutoRecycling() var element0 = context.GetOrCreateElementAt(index: 0); // lookup - repeater will give back the same element and note that this element will not // be pinned - i.e it will be auto recycled after a measure pass where GetElementAt(0) is not called. - var element0lookup = context.GetOrCreateElementAt(index: 0, options:ElementRealizationOptions.None); + var element0lookup = context.GetOrCreateElementAt(index: 0, options: ElementRealizationOptions.None); var element1 = context.GetOrCreateElementAt(index: 1, options: ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); // forcing a new element for index 1 that will be pinned (not auto recycled). This will be @@ -100,6 +100,35 @@ public void ValidateMappingAndAutoRecycling() }); } + [TestMethod] + public void ValidateNonVirtualLayoutWithItemsRepeater() + { + RunOnUIThread.Execute(() => + { + var repeater = new ItemsRepeater(); + repeater.Layout = new NonVirtualStackLayout(); + repeater.ItemsSource = Enumerable.Range(0, 10); + repeater.ItemTemplate = (DataTemplate)XamlReader.Load( + @" + - - - - - - + + diff --git a/dev/Repeater/TestUI/Samples/StackLayoutSamples/NonVirtual/NonVirtualStackLayoutSamplePage.xaml.cs b/dev/Repeater/TestUI/Samples/StackLayoutSamples/NonVirtual/NonVirtualStackLayoutSamplePage.xaml.cs index 7800cd8d74..f2bef2a143 100644 --- a/dev/Repeater/TestUI/Samples/StackLayoutSamples/NonVirtual/NonVirtualStackLayoutSamplePage.xaml.cs +++ b/dev/Repeater/TestUI/Samples/StackLayoutSamples/NonVirtual/NonVirtualStackLayoutSamplePage.xaml.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using System.Linq; #if !BUILD_WINDOWS using ItemsSourceView = Microsoft.UI.Xaml.Controls.ItemsSourceView; @@ -21,6 +22,7 @@ public sealed partial class NonVirtualStackLayoutSamplePage : Page public NonVirtualStackLayoutSamplePage() { this.InitializeComponent(); + repeater.ItemsSource = Enumerable.Range(0, 10); } } } diff --git a/dev/Repeater/VirtualLayoutContextAdapter.cpp b/dev/Repeater/VirtualLayoutContextAdapter.cpp new file mode 100644 index 0000000000..be8ce17513 --- /dev/null +++ b/dev/Repeater/VirtualLayoutContextAdapter.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include +#include +#include "ItemsRepeater.common.h" +#include "VirtualizingLayoutContext.h" +#include "VirtualLayoutContextAdapter.h" + +VirtualLayoutContextAdapter::VirtualLayoutContextAdapter(winrt::VirtualizingLayoutContext const& virtualizingContext) +{ + m_virtualizingContext = winrt::make_weak(virtualizingContext); +} + +#pragma region ILayoutContextOverrides + +winrt::IInspectable VirtualLayoutContextAdapter::LayoutStateCore() +{ + if (auto context = m_virtualizingContext.get()) + { + return context.LayoutState(); + } + return nullptr; +} + +void VirtualLayoutContextAdapter::LayoutStateCore(winrt::IInspectable const& state) +{ + if (auto context = m_virtualizingContext.get()) + { + context.LayoutStateCore(state); + } +} + +#pragma endregion + +#pragma region INonVirtualizingLayoutContextOverrides + +winrt::IVectorView VirtualLayoutContextAdapter::ChildrenCore() +{ + if (!m_children) + { + m_children = winrt::make>(m_virtualizingContext.get()); + } + + return m_children; +} + +#pragma endregion diff --git a/dev/Repeater/VirtualLayoutContextAdapter.h b/dev/Repeater/VirtualLayoutContextAdapter.h new file mode 100644 index 0000000000..a51e378da2 --- /dev/null +++ b/dev/Repeater/VirtualLayoutContextAdapter.h @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once + +#include "LayoutContext.h" +#include "VirtualizingLayoutContext.h" +#include "NonvirtualizingLayoutContext.h" + +class VirtualLayoutContextAdapter : + public winrt::implements +{ +public: + VirtualLayoutContextAdapter(winrt::VirtualizingLayoutContext const& virtualizingContext); + +#pragma region ILayoutContextOverrides + winrt::IInspectable LayoutStateCore(); + void LayoutStateCore(winrt::IInspectable const& state); +#pragma endregion + +#pragma region INonVirtualizingLayoutContextOverrides + winrt::IVectorView ChildrenCore(); +#pragma endregion + +private: + winrt::weak_ref m_virtualizingContext{ nullptr }; + winrt::IVectorView m_children{ nullptr }; + + template + class ChildrenCollection : + public ReferenceTracker, + typename reference_tracker_implements_t>::type, + typename winrt::IIterable> + { + public: + ChildrenCollection(winrt::VirtualizingLayoutContext const& context) + { + m_context = context; + } + +#pragma region IVectorView + uint32_t Size() + { + return m_context.ItemCount(); + } + + T GetAt(uint32_t index) + { + return m_context.GetOrCreateElementAt(index, winrt::ElementRealizationOptions::None); + } + + bool IndexOf(T const& value, uint32_t &index) noexcept + { + winrt::throw_hresult(E_NOTIMPL); + } + + uint32_t GetMany(uint32_t startIndex, winrt::array_view const& values) noexcept + { + winrt::throw_hresult(E_NOTIMPL); + } + +#pragma endregion + +#pragma region winrt::IIterable + winrt::IIterator First() + { + return winrt::make::Iterator>(*this); + } +#pragma endregion + + private: + class Iterator : + public ReferenceTracker>::type> + { + public: + Iterator(const winrt::IVectorView& childCollection) + { + m_childCollection = childCollection; + } + + ~Iterator() + { + + } + + T Current() + { + if (m_currentIndex < m_childCollection.Size()) + { + return m_childCollection.GetAt(m_currentIndex); + } + else + { + throw winrt::hresult_out_of_bounds(); + } + } + + bool HasCurrent() + { + return (m_currentIndex < m_childCollection.Size()); + } + + bool MoveNext() + { + if (m_currentIndex < m_childCollection.Size()) + { + ++m_currentIndex; + return (m_currentIndex < m_childCollection.Size()); + } + else + { + throw winrt::hresult_out_of_bounds(); + } + } + + uint32_t GetMany(winrt::array_view values) + { + uint32_t howMany = 0; + if (HasCurrent()) + { + do + { + if (howMany >= values.size()) break; + values[howMany] = Current(); + howMany++; + } while (MoveNext()); + } + + return howMany; + } + + private: + winrt::IVectorView m_childCollection{ nullptr }; + unsigned int m_currentIndex = 0; + }; + + winrt::VirtualizingLayoutContext m_context; + }; +}; diff --git a/dev/Repeater/VirtualizingLayoutContext.cpp b/dev/Repeater/VirtualizingLayoutContext.cpp index b96251f80b..0fcadcb5e6 100644 --- a/dev/Repeater/VirtualizingLayoutContext.cpp +++ b/dev/Repeater/VirtualizingLayoutContext.cpp @@ -5,6 +5,7 @@ #include #include "ItemsRepeater.common.h" #include "VirtualizingLayoutContext.h" +#include "VirtualLayoutContextAdapter.h" CppWinRTActivatableClassWithBasicFactory(VirtualizingLayoutContext); @@ -103,4 +104,14 @@ int32_t VirtualizingLayoutContext::ItemCountCore() throw winrt::hresult_not_implemented(); } -#pragma endregion \ No newline at end of file +#pragma endregion + +winrt::NonVirtualizingLayoutContext VirtualizingLayoutContext::GetNonVirtualizingContextAdapter() +{ + if (!m_contextAdapter) + { + m_contextAdapter = winrt::make(get_strong().as()); + } + + return m_contextAdapter; +} diff --git a/dev/Repeater/VirtualizingLayoutContext.h b/dev/Repeater/VirtualizingLayoutContext.h index 5fe23fd850..5310b8b9c0 100644 --- a/dev/Repeater/VirtualizingLayoutContext.h +++ b/dev/Repeater/VirtualizingLayoutContext.h @@ -40,4 +40,8 @@ class VirtualizingLayoutContext : virtual void LayoutOriginCore(winrt::Point const& value); #pragma endregion -}; \ No newline at end of file + winrt::NonVirtualizingLayoutContext GetNonVirtualizingContextAdapter(); + +private: + winrt::NonVirtualizingLayoutContext m_contextAdapter{ nullptr }; +};