diff --git a/dev/Generated/UniformGridLayout.properties.cpp b/dev/Generated/UniformGridLayout.properties.cpp index e1b703519d..13c7936ded 100644 --- a/dev/Generated/UniformGridLayout.properties.cpp +++ b/dev/Generated/UniformGridLayout.properties.cpp @@ -10,6 +10,7 @@ CppWinRTActivatableClassWithDPFactory(UniformGridLayout) GlobalDependencyProperty UniformGridLayoutProperties::s_ItemsJustificationProperty{ nullptr }; GlobalDependencyProperty UniformGridLayoutProperties::s_ItemsStretchProperty{ nullptr }; +GlobalDependencyProperty UniformGridLayoutProperties::s_MaximumRowsOrColumnsProperty{ nullptr }; GlobalDependencyProperty UniformGridLayoutProperties::s_MinColumnSpacingProperty{ nullptr }; GlobalDependencyProperty UniformGridLayoutProperties::s_MinItemHeightProperty{ nullptr }; GlobalDependencyProperty UniformGridLayoutProperties::s_MinItemWidthProperty{ nullptr }; @@ -45,6 +46,17 @@ void UniformGridLayoutProperties::EnsureProperties() ValueHelper::BoxValueIfNecessary(winrt::UniformGridLayoutItemsStretch::None), winrt::PropertyChangedCallback(&OnItemsStretchPropertyChanged)); } + if (!s_MaximumRowsOrColumnsProperty) + { + s_MaximumRowsOrColumnsProperty = + InitializeDependencyProperty( + L"MaximumRowsOrColumns", + winrt::name_of(), + winrt::name_of(), + false /* isAttached */, + ValueHelper::BoxValueIfNecessary(-1), + winrt::PropertyChangedCallback(&OnMaximumRowsOrColumnsPropertyChanged)); + } if (!s_MinColumnSpacingProperty) { s_MinColumnSpacingProperty = @@ -106,6 +118,7 @@ void UniformGridLayoutProperties::ClearProperties() { s_ItemsJustificationProperty = nullptr; s_ItemsStretchProperty = nullptr; + s_MaximumRowsOrColumnsProperty = nullptr; s_MinColumnSpacingProperty = nullptr; s_MinItemHeightProperty = nullptr; s_MinItemWidthProperty = nullptr; @@ -129,6 +142,14 @@ void UniformGridLayoutProperties::OnItemsStretchPropertyChanged( winrt::get_self(owner)->OnPropertyChanged(args); } +void UniformGridLayoutProperties::OnMaximumRowsOrColumnsPropertyChanged( + winrt::DependencyObject const& sender, + winrt::DependencyPropertyChangedEventArgs const& args) +{ + auto owner = sender.as(); + winrt::get_self(owner)->OnPropertyChanged(args); +} + void UniformGridLayoutProperties::OnMinColumnSpacingPropertyChanged( winrt::DependencyObject const& sender, winrt::DependencyPropertyChangedEventArgs const& args) @@ -189,6 +210,16 @@ winrt::UniformGridLayoutItemsStretch UniformGridLayoutProperties::ItemsStretch() return ValueHelper::CastOrUnbox(static_cast(this)->GetValue(s_ItemsStretchProperty)); } +void UniformGridLayoutProperties::MaximumRowsOrColumns(int value) +{ + static_cast(this)->SetValue(s_MaximumRowsOrColumnsProperty, ValueHelper::BoxValueIfNecessary(value)); +} + +int UniformGridLayoutProperties::MaximumRowsOrColumns() +{ + return ValueHelper::CastOrUnbox(static_cast(this)->GetValue(s_MaximumRowsOrColumnsProperty)); +} + void UniformGridLayoutProperties::MinColumnSpacing(double value) { static_cast(this)->SetValue(s_MinColumnSpacingProperty, ValueHelper::BoxValueIfNecessary(value)); diff --git a/dev/Generated/UniformGridLayout.properties.h b/dev/Generated/UniformGridLayout.properties.h index 792d6314e0..efef108d54 100644 --- a/dev/Generated/UniformGridLayout.properties.h +++ b/dev/Generated/UniformGridLayout.properties.h @@ -15,6 +15,9 @@ class UniformGridLayoutProperties void ItemsStretch(winrt::UniformGridLayoutItemsStretch const& value); winrt::UniformGridLayoutItemsStretch ItemsStretch(); + void MaximumRowsOrColumns(int value); + int MaximumRowsOrColumns(); + void MinColumnSpacing(double value); double MinColumnSpacing(); @@ -32,6 +35,7 @@ class UniformGridLayoutProperties static winrt::DependencyProperty ItemsJustificationProperty() { return s_ItemsJustificationProperty; } static winrt::DependencyProperty ItemsStretchProperty() { return s_ItemsStretchProperty; } + static winrt::DependencyProperty MaximumRowsOrColumnsProperty() { return s_MaximumRowsOrColumnsProperty; } static winrt::DependencyProperty MinColumnSpacingProperty() { return s_MinColumnSpacingProperty; } static winrt::DependencyProperty MinItemHeightProperty() { return s_MinItemHeightProperty; } static winrt::DependencyProperty MinItemWidthProperty() { return s_MinItemWidthProperty; } @@ -40,6 +44,7 @@ class UniformGridLayoutProperties static GlobalDependencyProperty s_ItemsJustificationProperty; static GlobalDependencyProperty s_ItemsStretchProperty; + static GlobalDependencyProperty s_MaximumRowsOrColumnsProperty; static GlobalDependencyProperty s_MinColumnSpacingProperty; static GlobalDependencyProperty s_MinItemHeightProperty; static GlobalDependencyProperty s_MinItemWidthProperty; @@ -57,6 +62,10 @@ class UniformGridLayoutProperties winrt::DependencyObject const& sender, winrt::DependencyPropertyChangedEventArgs const& args); + static void OnMaximumRowsOrColumnsPropertyChanged( + winrt::DependencyObject const& sender, + winrt::DependencyPropertyChangedEventArgs const& args); + static void OnMinColumnSpacingPropertyChanged( winrt::DependencyObject const& sender, winrt::DependencyPropertyChangedEventArgs const& args); diff --git a/dev/Repeater/APITests/Common/OrientationBasedMeasures.cs b/dev/Repeater/APITests/Common/OrientationBasedMeasures.cs index 0e49e6ef10..b85c6e5058 100644 --- a/dev/Repeater/APITests/Common/OrientationBasedMeasures.cs +++ b/dev/Repeater/APITests/Common/OrientationBasedMeasures.cs @@ -1,69 +1,79 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +using Microsoft.Graphics.Canvas.Effects; +using MUXControlsTestApp.Utilities; using Windows.Foundation; using Windows.UI.Xaml.Controls; +using DisplayInformation = Windows.Graphics.Display.DisplayInformation; namespace Windows.UI.Xaml.Tests.MUXControls.ApiTests.RepeaterTests.Common { public class OrientationBasedMeasures { public ScrollOrientation ScrollOrientation { get; set; } + private double m_rawPixelsPerViewPixel = 1.0; + private bool m_useLayoutRounding; public bool IsVerical { get { return ScrollOrientation == ScrollOrientation.Vertical; } } - public OrientationBasedMeasures(ScrollOrientation o) + public OrientationBasedMeasures(ScrollOrientation o, bool useLayoutRounding = true) { ScrollOrientation = o; + m_useLayoutRounding = useLayoutRounding; + + bool? hasThreadAccess = Window.Current?.Dispatcher?.HasThreadAccess; + if (useLayoutRounding && hasThreadAccess.HasValue && hasThreadAccess.Value) + m_rawPixelsPerViewPixel = DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel; } public double Major(Size size) { - return IsVerical ? size.Height : size.Width; + return RoundForLayout(IsVerical ? size.Height : size.Width); } public double Minor(Size size) { - return IsVerical ? size.Width : size.Height; + return RoundForLayout(IsVerical ? size.Width : size.Height); } public double MajorSize(Rect rect) { - return IsVerical ? rect.Height : rect.Width; + return RoundForLayout(IsVerical ? rect.Height : rect.Width); } public double MinorSize(Rect rect) { - return IsVerical ? rect.Width : rect.Height; + return RoundForLayout(IsVerical ? rect.Width : rect.Height); } public double MajorStart(Rect rect) { - return IsVerical ? rect.Top : rect.Left; + return RoundForLayout(IsVerical ? rect.Top : rect.Left); } public double MajorEnd(Rect rect) { - return IsVerical ? rect.Bottom : rect.Right; + return RoundForLayout(IsVerical ? rect.Bottom : rect.Right); } public double MinorStart(Rect rect) { - return IsVerical ? rect.Left : rect.Top; + return RoundForLayout(IsVerical ? rect.Left : rect.Top); } public void SetMajorSize(ref Rect rect, double value) { if (IsVerical) { - rect.Height = value; + rect.Height = RoundForLayout(value); } else { - rect.Width = value; + rect.Width = RoundForLayout(value); } } @@ -71,11 +81,11 @@ public void SetMajorStart(ref Rect rect, double value) { if (IsVerical) { - rect.Y = value; + rect.Y = RoundForLayout(value); } else { - rect.X = value; + rect.X = RoundForLayout(value); } } @@ -83,36 +93,52 @@ public void SetMinorStart(ref Rect rect, double value) { if (IsVerical) { - rect.X = value; + rect.X = RoundForLayout(value); } else { - rect.Y = value; + rect.Y = RoundForLayout(value); } } public Rect MinorMajorRect(double minor, double major, double minorSize, double majorSize) { + var min = RoundForLayout(minor); + var maj = RoundForLayout(major); + var minSize = RoundForLayout(minorSize); + var majSize = RoundForLayout(majorSize); return IsVerical ? - new Rect(minor, major, minorSize, majorSize) : - new Rect(major, minor, majorSize, minorSize); + new Rect(min, maj, minSize, majSize) : + new Rect(maj, min, majSize, minSize); } public Point MinorMajorPoint(double minor, double major) { + var min = RoundForLayout(minor); + var maj = RoundForLayout(major); return IsVerical ? - new Point(minor, major) : - new Point(major, minor); + new Point(min, maj) : + new Point(maj, min); } public Size MinorMajorSize(double minor, double major) { + var min = RoundForLayout(minor); + var maj = RoundForLayout(major); return IsVerical ? - new Size(minor, major) : - new Size(major, minor); + new Size(min, maj) : + new Size(maj, min); + } + + private double RoundForLayout(double value) + { + if (m_useLayoutRounding) + return global::System.Math.Floor((m_rawPixelsPerViewPixel * value) + 0.5) / m_rawPixelsPerViewPixel; + else + return value; } } } diff --git a/dev/Repeater/APITests/FlowLayoutTests.cs b/dev/Repeater/APITests/FlowLayoutTests.cs index 193f051fc1..70f31ad7c9 100644 --- a/dev/Repeater/APITests/FlowLayoutTests.cs +++ b/dev/Repeater/APITests/FlowLayoutTests.cs @@ -368,7 +368,7 @@ public void ValidateGridLayoutJustificationAndStretch() foreach (ScrollOrientation scrollOrientation in Enum.GetValues(typeof(ScrollOrientation))) { Log.Comment(string.Format("ScrollOrientation: {0}", scrollOrientation)); - var om = new OrientationBasedMeasures(scrollOrientation); + var om = new OrientationBasedMeasures(scrollOrientation, useLayoutRounding: true); const int panelMinorSize = 500; const int numItems = 2; const int itemMinorSize = 200; @@ -441,13 +441,14 @@ public void ValidateGridLayoutJustificationAndStretch() Log.Comment("UniformGridLayoutItemsJustification.SpaceEvenly"); layout.ItemsJustification = UniformGridLayoutItemsJustification.SpaceEvenly; + panel.UpdateLayout(); ValidateChildBounds( panel, new List() { - om.MinorMajorRect(33, 0, itemMinorSize, itemMajorSize), - om.MinorMajorRect(267, 0, itemMinorSize, itemMajorSize) + om.MinorMajorRect(33.3333, 0, itemMinorSize, itemMajorSize), + om.MinorMajorRect(266.6667, 0, itemMinorSize, itemMajorSize) }); Log.Comment("UniformGridLayoutItemsJustification.Start + UniformGridLayoutItemsStretch.Fill"); @@ -460,8 +461,8 @@ public void ValidateGridLayoutJustificationAndStretch() panel, new List() { - om.MinorMajorRect(0, 0, itemMinorSize + 40, itemMajorSize), - om.MinorMajorRect(250, 0, itemMinorSize + 40, itemMajorSize) + om.MinorMajorRect(0, 0, itemMinorSize + 45, itemMajorSize), + om.MinorMajorRect(255, 0, itemMinorSize + 45, itemMajorSize) }); Log.Comment("UniformGridLayoutItemsJustification.Start + UniformGridLayoutItemsStretch.Uniform"); @@ -474,13 +475,112 @@ public void ValidateGridLayoutJustificationAndStretch() panel, new List() { - om.MinorMajorRect(0, 0, itemMinorSize + 40, itemMajorSize + 19), - om.MinorMajorRect(250, 0, itemMinorSize + 40, itemMajorSize + 19) + om.MinorMajorRect(0, 0, itemMinorSize + 45, itemMajorSize + 22.5), + om.MinorMajorRect(255, 0, itemMinorSize + 45, itemMajorSize + 22.5) }); } }); } + [TestMethod] + public void ValidateGridLayoutMaximumRowsOrColumns() + { + RunOnUIThread.Execute(() => + { + foreach (ScrollOrientation scrollOrientation in Enum.GetValues(typeof(ScrollOrientation))) + { + Log.Comment(string.Format("ScrollOrientation: {0}", scrollOrientation)); + var om = new OrientationBasedMeasures(scrollOrientation, useLayoutRounding: true); + const int panelMinorSize = 500; + const int numItems = 4; + const int itemMinorSize = 150; + const int itemMajorSize = 100; + Log.Comment("UniformGridLayoutItemsJustification.Start + UniformGridLayoutItemsStretch.None"); + LayoutPanel panel = new LayoutPanel(); + var layout = new UniformGridLayout() + { + Orientation = scrollOrientation.ToOrthogonalLayoutOrientation(), + ItemsJustification = UniformGridLayoutItemsJustification.Start, + MinItemWidth = om.IsVerical ? itemMinorSize : itemMajorSize, + MinItemHeight = om.IsVerical ? itemMajorSize : itemMinorSize + }; + + SetPanelMinorSize(panel, om, panelMinorSize); + for (int i = 0; i < numItems; i++) + { + panel.Children.Add(new Button() { Content = i }); + } + + panel.Layout = layout; + Content = panel; + panel.UpdateLayout(); + + Log.Comment($"MaximumRowsOrColumns = {layout.MaximumRowsOrColumns}"); + ValidateChildBounds( + panel, + new List() + { + om.MinorMajorRect(0, 0, itemMinorSize, itemMajorSize), + om.MinorMajorRect(150, 0, itemMinorSize, itemMajorSize), + om.MinorMajorRect(300, 0, itemMinorSize, itemMajorSize), + om.MinorMajorRect(0, 100, itemMinorSize, itemMajorSize) + }); + + layout.MaximumRowsOrColumns = 0; + Log.Comment($"MaximumRowsOrColumns = {layout.MaximumRowsOrColumns}"); + panel.UpdateLayout(); + ValidateChildBounds( + panel, + new List() + { + om.MinorMajorRect(0, 0, itemMinorSize, itemMajorSize), + om.MinorMajorRect(0, 100, itemMinorSize, itemMajorSize), + om.MinorMajorRect(0, 200, itemMinorSize, itemMajorSize), + om.MinorMajorRect(0, 300, itemMinorSize, itemMajorSize) + }); + + layout.MaximumRowsOrColumns = 1; + Log.Comment($"MaximumRowsOrColumns = {layout.MaximumRowsOrColumns} + UniformGridLayoutItemsJustification.Start + UniformGridLayoutItemsStretch.Uniform"); + layout.ItemsJustification = UniformGridLayoutItemsJustification.Start; + layout.ItemsStretch = UniformGridLayoutItemsStretch.Uniform; + layout.MinRowSpacing = 10; + layout.MinColumnSpacing = 10; + var aspectRatio = itemMinorSize / (double)itemMajorSize; + var extraMajorSize = (panelMinorSize - itemMinorSize) / aspectRatio; + var majorOffset = itemMajorSize + extraMajorSize + layout.MinRowSpacing; + panel.UpdateLayout(); + ValidateChildBounds( + panel, + new List() + { + om.MinorMajorRect(0, 0, panelMinorSize, itemMajorSize + extraMajorSize), + om.MinorMajorRect(0, majorOffset, panelMinorSize, itemMajorSize + extraMajorSize), + om.MinorMajorRect(0, majorOffset * 2, panelMinorSize, itemMajorSize + extraMajorSize), + om.MinorMajorRect(0, majorOffset * 3, panelMinorSize, itemMajorSize + extraMajorSize), + }); + + layout.MaximumRowsOrColumns = 2; + Log.Comment($"MaximumRowsOrColumns = {layout.MaximumRowsOrColumns} + UniformGridLayoutItemsJustification.Start + UniformGridLayoutItemsStretch.Fill"); + layout.ItemsJustification = UniformGridLayoutItemsJustification.Start; + layout.ItemsStretch = UniformGridLayoutItemsStretch.Fill; + layout.MinRowSpacing = 10; + layout.MinColumnSpacing = 10; + panel.UpdateLayout(); + ValidateChildBounds( + panel, + new List() + { + om.MinorMajorRect(0, 0, itemMinorSize + 95, itemMajorSize), + om.MinorMajorRect(255, 0, itemMinorSize + 95, itemMajorSize), + om.MinorMajorRect(0, itemMajorSize + 10, itemMinorSize + 95, itemMajorSize), + om.MinorMajorRect(255, itemMajorSize + 10, itemMinorSize + 95, itemMajorSize), + }); + + } + }); + } + + [TestMethod] public void ValidateFlowLayout() { @@ -1352,7 +1452,7 @@ private void ValidateChildBounds(LayoutPanel panel, List list) for (int i = 0; i < list.Count; i++) { var child = (FrameworkElement)panel.Children[i]; - Verify.AreEqual(LayoutInformation.GetLayoutSlot(child), list[i]); + Verify.AreEqual(list[i], LayoutInformation.GetLayoutSlot(child)); } } diff --git a/dev/Repeater/FlowLayout.cpp b/dev/Repeater/FlowLayout.cpp index e342d6f7d3..982fbd1d7f 100644 --- a/dev/Repeater/FlowLayout.cpp +++ b/dev/Repeater/FlowLayout.cpp @@ -57,6 +57,7 @@ winrt::Size FlowLayout::MeasureOverride( true, /* isWrapping*/ MinItemSpacing(), LineSpacing(), + std::numeric_limits::max() /* maxItemsPerLine */, OrientationBasedMeasures::GetScrollOrientation(), LayoutId()); return desiredSize; @@ -185,7 +186,7 @@ winrt::Rect FlowLayout::GetExtent( UNREFERENCED_PARAMETER(lastRealized); auto extent = winrt::Rect{}; - + const int itemsCount = context.ItemCount(); if (itemsCount > 0) @@ -223,7 +224,7 @@ winrt::Rect FlowLayout::GetExtent( // We dont have anything realized. make an educated guess. int numLines = (int)std::ceil(itemsCount / averageItemsPerLine); extent = - std::isfinite(availableSizeMinor) ? + std::isfinite(availableSizeMinor) ? MinorMajorRect(0, 0, availableSizeMinor, std::max(0.0f, static_cast(numLines * averageLineSize - lineSpacing))) : MinorMajorRect( 0, @@ -268,7 +269,7 @@ void FlowLayout::OnLineArranged( REPEATER_TRACE_INFO(L"%*s: \tOnLineArranged startIndex:%d Count:%d LineHeight:%d \n", winrt::get_self(context)->Indent(), LayoutId().data(), startIndex, countInLine, lineSize); - + const auto flowState = GetAsFlowState(context.LayoutState()); flowState->OnLineArranged(startIndex, countInLine, lineSize, context); } @@ -413,7 +414,7 @@ double FlowLayout::GetAverageLineInfo( flowState->OnLineArranged(0, estimatedCountInLine, desiredSize.*Major(), context); flowState->SpecialElementDesiredSize(desiredSize); } - + avgCountInLine = std::max(1.0, flowState->TotalItemsPerLine() / flowState->TotalLinesMeasured()); avgLineSize = round(flowState->TotalLineSize() / flowState->TotalLinesMeasured()); diff --git a/dev/Repeater/FlowLayoutAlgorithm.cpp b/dev/Repeater/FlowLayoutAlgorithm.cpp index 3fd92f0872..ae6d479bde 100644 --- a/dev/Repeater/FlowLayoutAlgorithm.cpp +++ b/dev/Repeater/FlowLayoutAlgorithm.cpp @@ -33,6 +33,7 @@ winrt::Size FlowLayoutAlgorithm::Measure( bool isWrapping, double minItemSpacing, double lineSpacing, + unsigned int maxItemsPerLine, const ScrollOrientation& orientation, const wstring_view& layoutId) { @@ -59,15 +60,15 @@ winrt::Size FlowLayoutAlgorithm::Measure( m_elementManager.OnBeginMeasure(orientation); int anchorIndex = GetAnchorIndex(availableSize, isWrapping, minItemSpacing, layoutId); - Generate(GenerateDirection::Forward, anchorIndex, availableSize, minItemSpacing, lineSpacing, layoutId); - Generate(GenerateDirection::Backward, anchorIndex, availableSize, minItemSpacing, lineSpacing, layoutId); + Generate(GenerateDirection::Forward, anchorIndex, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, layoutId); + Generate(GenerateDirection::Backward, anchorIndex, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, layoutId); if (isWrapping && IsReflowRequired()) { REPEATER_TRACE_INFO(L"%*s: \tReflow Pass \n", winrt::get_self(context)->Indent(), layoutId.data()); auto firstElementBounds = m_elementManager.GetLayoutBoundsForRealizedIndex(0); firstElementBounds.*MinorStart() = 0; m_elementManager.SetLayoutBoundsForRealizedIndex(0, firstElementBounds); - Generate(GenerateDirection::Forward, 0 /*anchorIndex*/, availableSize, minItemSpacing, lineSpacing, layoutId); + Generate(GenerateDirection::Forward, 0 /*anchorIndex*/, availableSize, minItemSpacing, lineSpacing, maxItemsPerLine, layoutId); } RaiseLineArranged(); @@ -135,7 +136,7 @@ winrt::Size FlowLayoutAlgorithm::MeasureElement( auto provisionalArrangeSize = m_algorithmCallbacks->Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize(), context); m_algorithmCallbacks->Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize(), provisionalArrangeSize, context); - return provisionalArrangeSize; + return provisionalArrangeSize; } #pragma region Measure related private methods @@ -156,10 +157,10 @@ int FlowLayoutAlgorithm::GetAnchorIndex( anchorIndex = context.ItemCountCore() > 0 ? 0 : -1; } else - { + { bool isRealizationWindowConnected = m_elementManager.IsWindowConnected(RealizationRect(), GetScrollOrientation(), m_scrollOrientationSameAsFlow); // Item spacing and size in non-virtualizing direction change can cause elements to reflow - // and get a new column position. In that case we need the anchor to be positioned in the + // and get a new column position. In that case we need the anchor to be positioned in the // correct column. bool needAnchorColumnRevaluation = isWrapping && ( m_lastAvailableSize.*Minor() != availableSize.*Minor() || @@ -197,7 +198,7 @@ int FlowLayoutAlgorithm::GetAnchorIndex( else { // It is possible to end up in a situation during a collection change where GetAnchorForTargetElement returns an index - // which is not in the realized range. Eg. insert one item at index 0 for a grid layout. + // which is not in the realized range. Eg. insert one item at index 0 for a grid layout. // SuggestedAnchor will be 1 (used to be 0) and GetAnchorForTargetElement will return 0 (left most item in row). However 0 is not in the // realized range yet. In this case we realize the gap between the target anchor and the suggested anchor. int firstRealizedDataIndex = m_elementManager.GetDataIndexFromRealizedRangeIndex(0); @@ -281,6 +282,7 @@ void FlowLayoutAlgorithm::Generate( const winrt::Size& availableSize, double minItemSpacing, double lineSpacing, + unsigned int maxItemsPerLine, const wstring_view& layoutId) { if (anchorIndex != -1) @@ -298,7 +300,7 @@ void FlowLayoutAlgorithm::Generate( auto anchorBounds = m_elementManager.GetLayoutBoundsForDataIndex(anchorIndex); float lineOffset = anchorBounds.*MajorStart(); float lineMajorSize = anchorBounds.*MajorSize(); - int countInLine = 1; + unsigned int countInLine = 1; bool lineNeedsReposition = false; while (m_elementManager.IsIndexValidInData(currentIndex) && @@ -317,7 +319,7 @@ void FlowLayoutAlgorithm::Generate( if (direction == GenerateDirection::Forward) { double remainingSpace = availableSize.*Minor() - (previousElementBounds.*MinorStart() + previousElementBounds.*MinorSize() + minItemSpacing + desiredSize.*Minor()); - if (m_algorithmCallbacks->Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) + if (countInLine >= maxItemsPerLine || m_algorithmCallbacks->Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // No more space in this row. wrap to next row. currentBounds.*MinorStart() = 0; @@ -326,7 +328,7 @@ void FlowLayoutAlgorithm::Generate( if (lineNeedsReposition) { // reposition the previous line (countInLine items) - for (int i = 0; i < countInLine; i++) + for (unsigned int i = 0; i < countInLine; i++) { auto dataIndex = currentIndex - 1 - i; auto bounds = m_elementManager.GetLayoutBoundsForDataIndex(dataIndex); @@ -353,9 +355,9 @@ void FlowLayoutAlgorithm::Generate( } else { - // Backward + // Backward double remainingSpace = previousElementBounds.*MinorStart() - (desiredSize.*Minor() + static_cast(minItemSpacing)); - if (m_algorithmCallbacks->Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) + if (countInLine >= maxItemsPerLine || m_algorithmCallbacks->Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // Does not fit, wrap to the previous row const auto availableSizeMinor = availableSize.*Minor(); @@ -366,9 +368,9 @@ void FlowLayoutAlgorithm::Generate( { auto previousLineOffset = m_elementManager.GetLayoutBoundsForDataIndex(currentIndex + countInLine + 1).*MajorStart(); // reposition the previous line (countInLine items) - for (int i = 0; i < countInLine; i++) + for (unsigned int i = 0; i < countInLine; i++) { - auto dataIndex = currentIndex + 1 + i; + auto dataIndex = currentIndex + 1 + (int)i; if (dataIndex != anchorIndex) { auto bounds = m_elementManager.GetLayoutBoundsForDataIndex(dataIndex); @@ -412,7 +414,7 @@ void FlowLayoutAlgorithm::Generate( currentIndex += step; } - // If we did not reach the top or bottom of the extent, we realized one + // If we did not reach the top or bottom of the extent, we realized one // extra item before we knew we were outside the realization window. Do not // account for that element in the indicies inside the realization window. if (direction == GenerateDirection::Forward) @@ -555,7 +557,7 @@ void FlowLayoutAlgorithm::ArrangeVirtualizingLayout( FlowLayoutAlgorithm::LineAlignment lineAlignment, const wstring_view& layoutId) { - // Walk through the realized elements one line at a time and + // Walk through the realized elements one line at a time and // align them, Then call element.Arrange with the arranged bounds. int realizedElementCount = m_elementManager.GetRealizedElementCount(); if (realizedElementCount > 0) @@ -696,7 +698,7 @@ void FlowLayoutAlgorithm::SetLayoutOrigin() } else { - // Should have 0 origin for non-virtualizing layout since we always start from + // Should have 0 origin for non-virtualizing layout since we always start from // the first item MUX_ASSERT(m_lastExtent.X == 0 && m_lastExtent.Y == 0); } diff --git a/dev/Repeater/FlowLayoutAlgorithm.h b/dev/Repeater/FlowLayoutAlgorithm.h index 79783fec75..380c5ab147 100644 --- a/dev/Repeater/FlowLayoutAlgorithm.h +++ b/dev/Repeater/FlowLayoutAlgorithm.h @@ -39,6 +39,7 @@ class FlowLayoutAlgorithm : OrientationBasedMeasures bool isWrapping, double minItemSpacing, double lineSpacing, + unsigned int maxItemsPerLine, const ScrollOrientation& orientation, const wstring_view& layoutId); winrt::Size Arrange( @@ -68,7 +69,7 @@ class FlowLayoutAlgorithm : OrientationBasedMeasures Backward }; - // Methods + // Methods #pragma region Measure related private methods int GetAnchorIndex( const winrt::Size& availableSize, @@ -81,6 +82,7 @@ class FlowLayoutAlgorithm : OrientationBasedMeasures const winrt::Size& availableSize, double minItemSpacing, double lineSpacing, + unsigned int maxItemsPerLine, const wstring_view& layoutId); void MakeAnchor( const winrt::VirtualizingLayoutContext& context, @@ -133,6 +135,6 @@ class FlowLayoutAlgorithm : OrientationBasedMeasures // we do not want to align the line. We could potentially switch the // meaning of line alignment in this case, but I'll hold off on that // feature until someone asks for it - This is not a common scenario - // anyway. + // anyway. bool m_scrollOrientationSameAsFlow{ false }; }; diff --git a/dev/Repeater/ItemsRepeater.idl b/dev/Repeater/ItemsRepeater.idl index ad072043cd..78e0bf5456 100644 --- a/dev/Repeater/ItemsRepeater.idl +++ b/dev/Repeater/ItemsRepeater.idl @@ -126,13 +126,13 @@ unsealed runtimeclass ItemsRepeater : Windows.UI.Xaml.FrameworkElement [MUX_PROPERTY_CHANGED_CALLBACK(TRUE)] [MUX_DEFAULT_VALUE("winrt::StackLayout()")] Layout Layout { get; set; }; - + [WUXC_VERSION_PREVIEW] { [MUX_PROPERTY_CHANGED_CALLBACK(TRUE)] ElementAnimator Animator{ get; set; }; } - + [MUX_PROPERTY_CHANGED_CALLBACK(TRUE)] [MUX_DEFAULT_VALUE("2.0")] Double HorizontalCacheLength { get; set; }; @@ -396,6 +396,8 @@ unsealed runtimeclass UniformGridLayout : VirtualizingLayout UniformGridLayoutItemsJustification ItemsJustification{ get; set; }; [MUX_DEFAULT_VALUE("winrt::UniformGridLayoutItemsStretch::None")] UniformGridLayoutItemsStretch ItemsStretch{ get; set; }; + [MUX_DEFAULT_VALUE("-1")] + Int32 MaximumRowsOrColumns{ get; set; }; static Windows.UI.Xaml.DependencyProperty OrientationProperty { get; }; static Windows.UI.Xaml.DependencyProperty MinItemWidthProperty { get; }; @@ -404,6 +406,7 @@ unsealed runtimeclass UniformGridLayout : VirtualizingLayout static Windows.UI.Xaml.DependencyProperty MinColumnSpacingProperty { get; }; static Windows.UI.Xaml.DependencyProperty ItemsJustificationProperty { get; }; static Windows.UI.Xaml.DependencyProperty ItemsStretchProperty{ get; }; + static Windows.UI.Xaml.DependencyProperty MaximumRowsOrColumnsProperty{ get; }; } [WUXC_VERSION_PREVIEW] @@ -602,4 +605,4 @@ unsealed runtimeclass SelectionModel : Windows.UI.Xaml.Data.INotifyPropertyChang protected void OnPropertyChanged(String propertyName); } -} \ No newline at end of file +} diff --git a/dev/Repeater/StackLayout.cpp b/dev/Repeater/StackLayout.cpp index 02549e93ea..26fe355a09 100644 --- a/dev/Repeater/StackLayout.cpp +++ b/dev/Repeater/StackLayout.cpp @@ -63,6 +63,7 @@ winrt::Size StackLayout::MeasureOverride( false, /* isWrapping*/ 0 /* minItemSpacing */, m_itemSpacing, + MAXUINT /* maxItemsPerLine */, GetScrollOrientation(), LayoutId()); return { desiredSize.Width, desiredSize.Height }; @@ -325,7 +326,7 @@ double StackLayout::GetAverageElementSize( const winrt::com_ptr& stackLayoutState) { double averageElementSize = 0; - + if (context.ItemCount() > 0) { if (stackLayoutState->TotalElementsMeasured() == 0) diff --git a/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml b/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml index f0376a2c2d..4061b4c45f 100644 --- a/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml +++ b/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml @@ -133,11 +133,11 @@ - + - - - + + + Start Center End @@ -145,12 +145,13 @@ SpaceAround SpaceBetween - + None Fill Uniform + Animate diff --git a/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml.cs b/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml.cs index b2f629dd3d..b8a1e061fe 100644 --- a/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml.cs +++ b/dev/Repeater/TestUI/Samples/ItemsViewWithDataPage.xaml.cs @@ -138,39 +138,46 @@ private void OnMaintainViewportSliderValueChanged(object sender, RangeBaseValueC tracker.HorizontalAnchorRatio = maintainViewportSlider.Value; } - private void OnItemSpacingSliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) + private void OnRowSpacingSliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) { if (_pageInfo.Level0Layout != null) { - LayoutHelper.SetMinItemSpacing(_pageInfo.Level0Layout, itemSpacingSlider.Value); ; + LayoutHelper.SetMinRowSpacing(_pageInfo.Level0Layout, rowSpacingSlider.Value); ; } if (_pageInfo.Level1Layout != null) { - LayoutHelper.SetMinItemSpacing(_pageInfo.Level1Layout, itemSpacingSlider.Value); + LayoutHelper.SetMinRowSpacing(_pageInfo.Level1Layout, rowSpacingSlider.Value); } if (_pageInfo.Level2Layout != null) { - LayoutHelper.SetMinItemSpacing(_pageInfo.Level2Layout, itemSpacingSlider.Value); + LayoutHelper.SetMinRowSpacing(_pageInfo.Level2Layout, rowSpacingSlider.Value); + } + } + private void OnMaxRowsOrColumnsSliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) + { + if (_pageInfo.Level0Layout != null) + { + LayoutHelper.SetMaxRowsOrColumns(_pageInfo.Level0Layout, (int)(maxRowsOrColumnsSlider.Value)); } } - private void OnLineSpacingSliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) + private void OnColumnSpacingSliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) { if (_pageInfo.Level0Layout != null) { - LayoutHelper.SetLineSpacing(_pageInfo.Level0Layout, lineSpacingSlider.Value); + LayoutHelper.SetMinColumnSpacing(_pageInfo.Level0Layout, colSpacingSlider.Value); } if (_pageInfo.Level1Layout != null) { - LayoutHelper.SetLineSpacing(_pageInfo.Level1Layout, lineSpacingSlider.Value); + LayoutHelper.SetMinColumnSpacing(_pageInfo.Level1Layout, colSpacingSlider.Value); } if (_pageInfo.Level2Layout != null) { - LayoutHelper.SetLineSpacing(_pageInfo.Level2Layout, lineSpacingSlider.Value); + LayoutHelper.SetMinColumnSpacing(_pageInfo.Level2Layout, colSpacingSlider.Value); } } diff --git a/dev/Repeater/TestUI/Samples/LayoutHelper.cs b/dev/Repeater/TestUI/Samples/LayoutHelper.cs index 444dba55b3..4fb55962b5 100644 --- a/dev/Repeater/TestUI/Samples/LayoutHelper.cs +++ b/dev/Repeater/TestUI/Samples/LayoutHelper.cs @@ -16,7 +16,15 @@ namespace MUXControlsTestApp.Samples { public static class LayoutHelper { - public static void SetMinItemSpacing(Layout layout, double value) + public static void SetMaxRowsOrColumns(Layout layout, int value) + { + if (layout is UniformGridLayout) + { + ((UniformGridLayout)layout).MaximumRowsOrColumns = value; + } + } + + public static void SetMinRowSpacing(Layout layout, double value) { if (layout is UniformGridLayout) { @@ -32,7 +40,7 @@ public static void SetMinItemSpacing(Layout layout, double value) } } - public static void SetLineSpacing(Layout layout, double value) + public static void SetMinColumnSpacing(Layout layout, double value) { if (layout is UniformGridLayout) { diff --git a/dev/Repeater/UniformGridLayout.cpp b/dev/Repeater/UniformGridLayout.cpp index f7210bad1f..50a83398c7 100644 --- a/dev/Repeater/UniformGridLayout.cpp +++ b/dev/Repeater/UniformGridLayout.cpp @@ -56,10 +56,10 @@ winrt::Size UniformGridLayout::MeasureOverride( winrt::VirtualizingLayoutContext const& context, winrt::Size const& availableSize) { - // Set the width and height on the grid state. If the user already set them then use the preset. + // Set the width and height on the grid state. If the user already set them then use the preset. // If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items. auto gridState = GetAsGridState(context.LayoutState()); - gridState->EnsureElementSize(availableSize, context, m_minItemWidth, m_minItemHeight, m_itemsStretch, Orientation(), MinRowSpacing(), MinColumnSpacing()); + gridState->EnsureElementSize(availableSize, context, m_minItemWidth, m_minItemHeight, m_itemsStretch, Orientation(), MinRowSpacing(), MinColumnSpacing(), m_maximumRowsOrColumns); auto desiredSize = GetFlowAlgorithm(context).Measure( availableSize, @@ -67,6 +67,7 @@ winrt::Size UniformGridLayout::MeasureOverride( true, /* isWrapping*/ MinItemSpacing(), LineSpacing(), + m_maximumRowsOrColumns /* maxItemsPerLine */, OrientationBasedMeasures::GetScrollOrientation(), LayoutId()); @@ -135,9 +136,11 @@ winrt::FlowLayoutAnchorInfo UniformGridLayout::Algorithm_GetAnchorForRealization { const auto gridState = GetAsGridState(context.LayoutState()); const auto lastExtent = gridState->FlowAlgorithm().LastExtent(); - const int itemsPerLine = std::max(1, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))); - const double majorSize = (itemsCount / itemsPerLine) * GetMajorSizeWithSpacing(context); - const double realizationWindowStartWithinExtent = realizationRect.*MajorStart() - lastExtent.*MajorStart(); + const int itemsPerLine = std::min( // note use of unsigned ints + std::max(1u, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))), + std::max(1u, m_maximumRowsOrColumns)); + const double majorSize = (itemsCount / itemsPerLine) * (double)(GetMajorSizeWithSpacing(context)); + const double realizationWindowStartWithinExtent = (double)(realizationRect.*MajorStart() - lastExtent.*MajorStart()); if ((realizationWindowStartWithinExtent + realizationRect.*MajorSize()) >= 0 && realizationWindowStartWithinExtent <= majorSize) { const double offset = std::max(0.0f, realizationRect.*MajorStart() - lastExtent.*MajorStart()); @@ -165,7 +168,9 @@ winrt::FlowLayoutAnchorInfo UniformGridLayout::Algorithm_GetAnchorForTargetEleme int count = context.ItemCount(); if (targetIndex >= 0 && targetIndex < count) { - int itemsPerLine = std::max(1, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))); + int itemsPerLine = std::min( // note use of unsigned ints + std::max(1u, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))), + std::max(1u, m_maximumRowsOrColumns)); int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine; index = indexOfFirstInLine; auto state = GetAsGridState(context.LayoutState()); @@ -197,8 +202,12 @@ winrt::Rect UniformGridLayout::Algorithm_GetExtent( // Constants const int itemsCount = context.ItemCount(); const float availableSizeMinor = availableSize.*Minor(); - const int itemsPerLine = std::max(1, std::isfinite(availableSizeMinor) ? - static_cast(availableSizeMinor / GetMinorSizeWithSpacing(context)) : itemsCount); + const int itemsPerLine = + std::min( // note use of unsigned ints + std::max(1u, std::isfinite(availableSizeMinor) + ? static_cast(availableSizeMinor / GetMinorSizeWithSpacing(context)) + : itemsCount), + std::max(1u, m_maximumRowsOrColumns)); const float lineSize = GetMajorSizeWithSpacing(context); if (itemsCount > 0) @@ -271,6 +280,10 @@ void UniformGridLayout::OnPropertyChanged(const winrt::DependencyPropertyChanged { m_minItemHeight = unbox_value(args.NewValue()); } + else if (property == s_MaximumRowsOrColumnsProperty) + { + m_maximumRowsOrColumns = static_cast(unbox_value(args.NewValue())); + } InvalidateLayout(); } @@ -298,10 +311,12 @@ float UniformGridLayout::GetMajorSizeWithSpacing(winrt::VirtualizingLayoutContex winrt::Rect UniformGridLayout::GetLayoutRectForDataIndex( const winrt::Size& availableSize, int index, - const winrt::Rect& lastExtent, + const winrt::Rect& lastExtent, const winrt::VirtualizingLayoutContext& context) { - int itemsPerLine = std::max(1, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))); + int itemsPerLine = std::min( //note use of unsigned ints + std::max(1u, static_cast(availableSize.*Minor() / GetMinorSizeWithSpacing(context))), + std::max(1u, m_maximumRowsOrColumns)); int rowIndex = static_cast(index / itemsPerLine); int indexInRow = index - (rowIndex * itemsPerLine); diff --git a/dev/Repeater/UniformGridLayout.h b/dev/Repeater/UniformGridLayout.h index aec58e5f3b..26903c1993 100644 --- a/dev/Repeater/UniformGridLayout.h +++ b/dev/Repeater/UniformGridLayout.h @@ -111,7 +111,8 @@ class UniformGridLayout : double m_minColumnSpacing{}; winrt::UniformGridLayoutItemsJustification m_itemsJustification{ winrt::UniformGridLayoutItemsJustification::Start }; winrt::UniformGridLayoutItemsStretch m_itemsStretch{ winrt::UniformGridLayoutItemsStretch::None }; + unsigned int m_maximumRowsOrColumns{MAXUINT}; // !!! WARNING !!! - // Any storage here needs to be related to layout configuration. + // Any storage here needs to be related to layout configuration. // layout specific state needs to be stored in UniformGridLayoutState. }; diff --git a/dev/Repeater/UniformGridLayoutState.cpp b/dev/Repeater/UniformGridLayoutState.cpp index c27f2b7a1c..292b0a0246 100644 --- a/dev/Repeater/UniformGridLayoutState.cpp +++ b/dev/Repeater/UniformGridLayoutState.cpp @@ -36,15 +36,21 @@ void UniformGridLayoutState::EnsureElementSize( const winrt::UniformGridLayoutItemsStretch& stretch, const winrt::Orientation& orientation, double minRowSpacing, - double minColumnSpacing) + double minColumnSpacing, + unsigned int maxItemsPerLine) { + if (maxItemsPerLine == 0) + { + maxItemsPerLine = 1; + } + if (context.ItemCount() > 0) { // If the first element is realized we don't need to cache it or to get it from the context if (auto realizedElement = m_flowAlgorithm.GetElementIfRealized(0)) { realizedElement.Measure(availableSize); - SetSize(realizedElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing); + SetSize(realizedElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine); m_cachedFirstElement = nullptr; } else @@ -56,7 +62,7 @@ void UniformGridLayoutState::EnsureElementSize( } m_cachedFirstElement.Measure(availableSize); - SetSize(m_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing); + SetSize(m_cachedFirstElement, layoutItemWidth, LayoutItemHeight, availableSize, stretch, orientation, minRowSpacing, minColumnSpacing, maxItemsPerLine); // See if we can move ownership to the flow algorithm. If we can, we do not need a local cache. bool added = m_flowAlgorithm.TryAddElement0(m_cachedFirstElement); @@ -76,8 +82,14 @@ void UniformGridLayoutState::SetSize( const winrt::UniformGridLayoutItemsStretch& stretch, const winrt::Orientation& orientation, double minRowSpacing, - double minColumnSpacing) + double minColumnSpacing, + unsigned int maxItemsPerLine) { + if (maxItemsPerLine == 0) + { + maxItemsPerLine = 1; + } + m_effectiveItemWidth = (std::isnan(layoutItemWidth) ? UIElement.DesiredSize().Width : layoutItemWidth); m_effectiveItemHeight = (std::isnan(LayoutItemHeight) ? UIElement.DesiredSize().Height : LayoutItemHeight); @@ -85,11 +97,17 @@ void UniformGridLayoutState::SetSize( auto minorItemSpacing = orientation == winrt::Orientation::Vertical ? minRowSpacing : minColumnSpacing; auto itemSizeMinor = orientation == winrt::Orientation::Horizontal ? m_effectiveItemWidth : m_effectiveItemHeight; - itemSizeMinor += minorItemSpacing; - auto numItemsPerColumn = static_cast(std::max(1.0, availableSizeMinor / itemSizeMinor)); - auto remainingSpace = ((int)availableSizeMinor) % ((int)itemSizeMinor); - auto extraMinorPixelsForEachItem = remainingSpace / numItemsPerColumn; + double extraMinorPixelsForEachItem = 0.0; + if (std::isfinite(availableSizeMinor)) + { + auto numItemsPerColumn = std::min( + maxItemsPerLine, + static_cast(std::max(1.0, availableSizeMinor / (itemSizeMinor + minorItemSpacing)))); + auto usedSpace = (numItemsPerColumn * (itemSizeMinor + minorItemSpacing)) - minorItemSpacing; + auto remainingSpace = ((int)(availableSizeMinor - usedSpace)); + extraMinorPixelsForEachItem = remainingSpace / ((int)numItemsPerColumn); + } if (stretch == winrt::UniformGridLayoutItemsStretch::Fill) { diff --git a/dev/Repeater/UniformGridLayoutState.h b/dev/Repeater/UniformGridLayoutState.h index bd7b9590ef..cd441bffca 100644 --- a/dev/Repeater/UniformGridLayoutState.h +++ b/dev/Repeater/UniformGridLayoutState.h @@ -32,7 +32,8 @@ class UniformGridLayoutState : const winrt::UniformGridLayoutItemsStretch& stretch, const winrt::Orientation& orientation, double minRowSpacing, - double minColumnSpacing); + double minColumnSpacing, + unsigned int maxItemsPerLine); void ClearElementOnDataSourceChange(winrt::VirtualizingLayoutContext const& context, winrt::NotifyCollectionChangedEventArgs const& args); private: @@ -47,12 +48,13 @@ class UniformGridLayoutState : const winrt::UniformGridLayoutItemsStretch& stretch, const winrt::Orientation& orientation, double minRowSpacing, - double minColumnSpacing); + double minColumnSpacing, + unsigned int maxItemsPerLine); - // We need to measure the element at index 0 to know what size to measure all other items. - // If FlowlayoutAlgorithm has already realized element 0 then we can use that. + // We need to measure the element at index 0 to know what size to measure all other items. + // If FlowlayoutAlgorithm has already realized element 0 then we can use that. // If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it. - // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. + // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. // If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm. winrt::UIElement m_cachedFirstElement = nullptr; }; diff --git a/dev/Scroller/APITests/ScrollerLayoutTests.cs b/dev/Scroller/APITests/ScrollerLayoutTests.cs index d9b98363cb..ba76e2ba39 100644 --- a/dev/Scroller/APITests/ScrollerLayoutTests.cs +++ b/dev/Scroller/APITests/ScrollerLayoutTests.cs @@ -366,7 +366,7 @@ private void ViewportHeight( RunOnUIThread.Execute(() => { - double expectedViewportHeight = + double expectedViewportHeight = rectangle.Height + stackPanel.BorderThickness.Top + stackPanel.BorderThickness.Bottom + stackPanel.Margin.Top + stackPanel.Margin.Bottom; @@ -543,7 +543,7 @@ public void StretchedImage() scroller = new Scroller(); scroller.Content = imageScrollerContent; - SetupDefaultUI(scroller, rectangleScrollerContent: null, scrollerLoadedEvent); + SetupDefaultUI(scroller, rectangleScrollerContent: null, scrollerLoadedEvent: scrollerLoadedEvent); }); WaitForEvent("Waiting for Loaded event", scrollerLoadedEvent);