internal void RemoveRange(int startIndex, int endIndex) { for (int i = startIndex; i <= endIndex; i++) { if (i > _items.Count) { break; } StaggeredItem item = _items[i]; item.Height = 0; item.Top = 0; // We must recycle all elements to ensure that it gets the correct context RecycleElementAt(i); } foreach (var kvp in _columnLayout) { StaggeredColumnLayout layout = kvp.Value; for (int i = 0; i < layout.Count; i++) { if ((startIndex <= layout[i].Index) && (layout[i].Index <= endIndex)) { int numToRemove = layout.Count - i; layout.RemoveRange(i, numToRemove); break; } } } }
internal void AddItemToColumn(StaggeredItem item, int columnIndex) { if (_columnLayout.TryGetValue(columnIndex, out StaggeredColumnLayout columnLayout) == false) { columnLayout = new StaggeredColumnLayout(); _columnLayout[columnIndex] = columnLayout; } if (columnLayout.Contains(item) == false) { columnLayout.Add(item); } }
internal StaggeredItem GetItemAt(int index) { if (index < 0) { throw new IndexOutOfRangeException(); } if (index <= (_items.Count - 1)) { return(_items[index]); } else { StaggeredItem item = new StaggeredItem(index); _items.Add(item); return(item); } }
/// <inheritdoc/> protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) { return(finalSize); } var state = (StaggeredLayoutState)context.LayoutState; // Cycle through each column and arrange the items that are within the realization bounds for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++) { StaggeredColumnLayout layout = state.GetColumnLayout(columnIndex); for (int i = 0; i < layout.Count; i++) { StaggeredItem item = layout[i]; double bottom = item.Top + item.Height; if (bottom < context.RealizationRect.Top) { // element is above the realization bounds continue; } if (item.Top <= context.RealizationRect.Bottom) { double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (ColumnSpacing * columnIndex); Rect bounds = new Rect(itemHorizontalOffset, item.Top, state.ColumnWidth, item.Height); UIElement element = context.GetOrCreateElementAt(item.Index); element.Arrange(bounds); } else { break; } } } return(finalSize); }
/// <inheritdoc/> protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { if (context.ItemCount == 0) { return(new Size(availableSize.Width, 0)); } if ((context.RealizationRect.Width == 0) && (context.RealizationRect.Height == 0)) { return(new Size(availableSize.Width, 0.0)); } var state = (StaggeredLayoutState)context.LayoutState; double availableWidth = availableSize.Width; double availableHeight = availableSize.Height; double columnWidth = Math.Min(DesiredColumnWidth, availableWidth); if (columnWidth != state.ColumnWidth) { // The items will need to be remeasured state.Clear(); } state.ColumnWidth = Math.Min(DesiredColumnWidth, availableWidth); int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / state.ColumnWidth)); // adjust for column spacing on all columns expect the first double totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing)); if (totalWidth > availableWidth) { numColumns--; } else if (double.IsInfinity(availableWidth)) { availableWidth = totalWidth; } if (numColumns != state.NumberOfColumns) { // The items will not need to be remeasured, but they will need to go into new columns state.ClearColumns(); } if (RowSpacing != state.RowSpacing) { // If the RowSpacing changes the height of the rows will be different. // The columns stores the height so we'll want to clear them out to // get the proper height state.ClearColumns(); state.RowSpacing = RowSpacing; } var columnHeights = new double[numColumns]; var itemsPerColumn = new int[numColumns]; var deadColumns = new HashSet <int>(); for (int i = 0; i < context.ItemCount; i++) { var columnIndex = GetColumnIndex(columnHeights); bool measured = false; StaggeredItem item = state.GetItemAt(i); if (item.Height == 0) { // Item has not been measured yet. Get the element and store the values item.Element = context.GetOrCreateElementAt(i); item.Element.Measure(new Size(state.ColumnWidth, availableHeight)); item.Height = item.Element.DesiredSize.Height; measured = true; } double spacing = itemsPerColumn[columnIndex] > 0 ? RowSpacing : 0; item.Top = columnHeights[columnIndex] + spacing; double bottom = item.Top + item.Height; columnHeights[columnIndex] = bottom; itemsPerColumn[columnIndex]++; state.AddItemToColumn(item, columnIndex); if (bottom < context.RealizationRect.Top) { // The bottom of the element is above the realization area if (item.Element != null) { context.RecycleElement(item.Element); item.Element = null; } } else if (item.Top > context.RealizationRect.Bottom) { // The top of the element is below the realization area if (item.Element != null) { context.RecycleElement(item.Element); item.Element = null; } deadColumns.Add(columnIndex); } else if (measured == false) { // We ALWAYS want to measure an item that will be in the bounds item.Element = context.GetOrCreateElementAt(i); item.Element.Measure(new Size(state.ColumnWidth, availableHeight)); if (item.Height != item.Element.DesiredSize.Height) { // this item changed size; we need to recalculate layout for everything after this state.RemoveFromIndex(i + 1); item.Height = item.Element.DesiredSize.Height; columnHeights[columnIndex] = item.Top + item.Height; } } if (deadColumns.Count == numColumns) { break; } } double desiredHeight = state.GetHeight(); return(new Size(availableWidth, desiredHeight)); }