private void Generate( GenerateDirection direction, int anchorIndex, Size availableSize, double minItemSpacing, double lineSpacing, uint maxItemsPerLine, bool disableVirtualization, string layoutId) { if (anchorIndex != -1) { int step = (direction == GenerateDirection.Forward) ? 1 : -1; int previousIndex = anchorIndex; int currentIndex = anchorIndex + step; var anchorBounds = m_elementManager.GetLayoutBoundsForDataIndex(anchorIndex); double lineOffset = OM.MajorStart(anchorBounds); double lineMajorSize = OM.MajorSize(anchorBounds); uint countInLine = 1; bool lineNeedsReposition = false; while (m_elementManager.IsIndexValidInData(currentIndex) && (disableVirtualization || ShouldContinueFillingUpSpace(previousIndex, direction))) { // Ensure layout element. m_elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId); var currentElement = m_elementManager.GetRealizedElement(currentIndex); var desiredSize = MeasureElement(currentElement, currentIndex, availableSize, m_context); // Lay it out. var previousElement = m_elementManager.GetRealizedElement(previousIndex); Rect currentBounds = new Rect(0, 0, desiredSize.Width, desiredSize.Height); var previousElementBounds = m_elementManager.GetLayoutBoundsForDataIndex(previousIndex); if (direction == GenerateDirection.Forward) { double remainingSpace = OM.Minor(availableSize) - (OM.MinorStart(previousElementBounds) + OM.MinorSize(previousElementBounds) + minItemSpacing + OM.Minor(desiredSize)); if (countInLine >= maxItemsPerLine || m_algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // No more space in this row. wrap to next row. OM.SetMinorStart(ref currentBounds, 0); OM.SetMajorStart(ref currentBounds, OM.MajorStart(previousElementBounds) + lineMajorSize + lineSpacing); if (lineNeedsReposition) { // reposition the previous line (countInLine items) for (uint i = 0; i < countInLine; i++) { var dataIndex = currentIndex - 1 - i; var bounds = m_elementManager.GetLayoutBoundsForDataIndex((int)dataIndex); OM.SetMajorSize(ref bounds, lineMajorSize); m_elementManager.SetLayoutBoundsForDataIndex((int)dataIndex, bounds); } } // Setup for next line. lineMajorSize = OM.MajorSize(currentBounds); lineOffset = OM.MajorStart(currentBounds); lineNeedsReposition = false; countInLine = 1; } else { // More space is available in this row. OM.SetMinorStart(ref currentBounds, OM.MinorStart(previousElementBounds) + OM.MinorSize(previousElementBounds) + (minItemSpacing)); OM.SetMajorStart(ref currentBounds, lineOffset); lineMajorSize = Math.Max(lineMajorSize, OM.MajorSize(currentBounds)); lineNeedsReposition = OM.MajorSize(previousElementBounds) != OM.MajorSize(currentBounds); countInLine++; } } else { // Backward double remainingSpace = OM.MinorStart(previousElementBounds) - (OM.Minor(desiredSize) + minItemSpacing); if (countInLine >= maxItemsPerLine || m_algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) { // Does not fit, wrap to the previous row var availableSizeMinor = OM.Minor(availableSize); OM.SetMinorStart(ref currentBounds, !double.IsInfinity(availableSizeMinor) ? availableSizeMinor - OM.Minor(desiredSize) : 0.0); OM.SetMajorStart(ref currentBounds, lineOffset - OM.Major(desiredSize) - lineSpacing); if (lineNeedsReposition) { var previousLineOffset = OM.MajorStart(m_elementManager.GetLayoutBoundsForDataIndex((int)(currentIndex + countInLine + 1))); // reposition the previous line (countInLine items) for (uint i = 0; i < countInLine; i++) { var dataIndex = currentIndex + 1 + (int)i; if (dataIndex != anchorIndex) { var bounds = m_elementManager.GetLayoutBoundsForDataIndex(dataIndex); OM.SetMajorStart(ref bounds, previousLineOffset - lineMajorSize - lineSpacing); OM.SetMajorSize(ref bounds, lineMajorSize); m_elementManager.SetLayoutBoundsForDataIndex(dataIndex, bounds); } } } // Setup for next line. lineMajorSize = OM.MajorSize(currentBounds); lineOffset = OM.MajorStart(currentBounds); lineNeedsReposition = false; countInLine = 1; } else { // Fits in this row. put it in the previous position OM.SetMinorStart(ref currentBounds, OM.MinorStart(previousElementBounds) - OM.Minor(desiredSize) - minItemSpacing); OM.SetMajorStart(ref currentBounds, lineOffset); lineMajorSize = Math.Max(lineMajorSize, OM.MajorSize(currentBounds)); lineNeedsReposition = OM.MajorSize(previousElementBounds) != OM.MajorSize(currentBounds); countInLine++; } } m_elementManager.SetLayoutBoundsForDataIndex(currentIndex, currentBounds); previousIndex = currentIndex; currentIndex += step; } // 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) { int dataCount = m_context.ItemCount; m_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1; m_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, m_lastRealizedDataIndexInsideRealizationWindow); } else { int dataCount = m_context.ItemCount; m_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1; m_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, m_firstRealizedDataIndexInsideRealizationWindow); } m_elementManager.DiscardElementsOutsideWindow(direction == GenerateDirection.Forward, currentIndex); } }