/// <summary> /// Get the range of children that are visible /// </summary> /// <param name="firstVisibleItemIndex">The item index of the first visible item</param> /// <param name="lastVisibleItemIndex">The item index of the last visible item</param> private VisibleRange GetVisibleRange() { ItemsControl itemsControl = ItemsControl.GetItemsOwner(this); int itemCount = itemsControl.HasItems ? itemsControl.Items.Count : 0; if (itemCount == 0) { return VisibleRange.Empty; } else { int columns = DataSource.Source.Columns.Length; VisibleRange range = new VisibleRange(); range.firstRow = Math.Max(0,(int)Math.Floor(_offset.Y / PropertiesGridControl.RowHeight) - CREATE_MARGIN_ELEMENTS); range.firstCol = (int)Math.Floor(_offset.X / PropertiesGridControl.DataItemWidth); range.rowCount = (int)Math.Ceiling((_viewport.Height) / PropertiesGridControl.RowHeight) + (CREATE_MARGIN_ELEMENTS*2) + 1; //Anstatt +1 wäre korrekterweise der ausgeblendete Teil der ersten Zeile zu beachten range.colCount = Math.Min(columns-range.firstCol,(int)Math.Ceiling((_viewport.Width) / PropertiesGridControl.DataItemWidth) + 1); //--||-- int lastIndex = (range.firstRow + range.rowCount) * columns + range.firstCol + range.colCount; if (lastIndex >= itemCount) { //Anzeige kann nicht komplett ausgefüllt werden int maxRowCount = Math.Max(0, itemCount / columns - range.firstRow); range.rowCount = Math.Min(maxRowCount, range.rowCount); } return range; } }
/// <summary> /// Revirtualize items that are no longer visible /// </summary> private void CleanUpItems(VisibleRange visibleRange, int columCount) { UIElementCollection children = this.InternalChildren; if (children.Count > 0) { ItemsControl itemsControl = ItemsControl.GetItemsOwner(this); IItemContainerGenerator generator = this.ItemContainerGenerator; int firstVisibleIndex = visibleRange.firstRow * columCount + visibleRange.firstCol; int lastVisibleIndex = (visibleRange.firstRow + visibleRange.rowCount) * columCount + visibleRange.firstCol + visibleRange.colCount; for (int i = children.Count - 1; i >= 0; i--) { GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0); int itemIndex = generator.IndexFromGeneratorPosition(childGeneratorPos); if (itemIndex < firstVisibleIndex || itemIndex > lastVisibleIndex) { if (!((ItemViewModel)itemsControl.Items.GetItemAt(itemIndex)).InEditMode) { generator.Remove(childGeneratorPos, 1); RemoveInternalChildRange(i, 1); } } } } }
protected override Size MeasureOverride(Size availableSize) { UpdateScrollInfo(availableSize); // Figure out range that's visible based on layout algorithm VisibleRange visibleRange = GetVisibleRange(); _prevVisibleRange = visibleRange; if (SCROLL_CREATE_ITEMS_DELAY_MS == 0) { CalculateChildren(visibleRange); } else { Task.Delay(SCROLL_CREATE_ITEMS_DELAY_MS).ContinueWith((t) => { //Wartet für die angegeben Zeitspanne //Falls sich der angezeigte Bereich in dieser Zeit nicht geändert hat // -> Child-Element erzeugen if (visibleRange.Equals(_prevVisibleRange) && !visibleRange.Equals(_calculatedVisibleRange)) { _calculatedVisibleRange = visibleRange; CalculateChildren(visibleRange); } }, TaskScheduler.FromCurrentSynchronizationContext()); } return availableSize; }
private void CalculateChildren(VisibleRange visibleRange) { // We need to access InternalChildren before the generator to work around a bug UIElementCollection children = this.InternalChildren; IItemContainerGenerator generator = this.ItemContainerGenerator; int columCount = DataSource.Source.Columns.Length; ItemsControl itemsControl = ItemsControl.GetItemsOwner(this); int lastRow = visibleRange.firstRow + visibleRange.rowCount; for (int r = visibleRange.firstRow; r < lastRow; r++) { int firstVisibleItemInRow = r * columCount + visibleRange.firstCol; int lastVisibleItemInRow = firstVisibleItemInRow + visibleRange.colCount - 1; GeneratorPosition startPos = generator.GeneratorPositionFromIndex(firstVisibleItemInRow); // Get index where we'd insert the child for this position. If the item is realized // (position.Offset == 0), it's just position.Index, otherwise we have to add one to // insert after the corresponding child int childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1; using (generator.StartAt(startPos, GeneratorDirection.Forward, true)) { for (int itemIndex = firstVisibleItemInRow; itemIndex <= lastVisibleItemInRow; ++itemIndex, ++childIndex) { if (((ItemViewModel)itemsControl.Items[itemIndex]).Item != null) { bool newlyRealized; // Get or create the child UIElement child = generator.GenerateNext(out newlyRealized) as UIElement; if (newlyRealized) { // Figure out if we need to insert the child at the end or somewhere in the middle if (childIndex >= children.Count) { base.AddInternalChild(child); } else { base.InsertInternalChild(childIndex, child); } generator.PrepareItemContainer(child); this.DataSource.HoverManager.RegisterItem(child); } else { // The child has already been created, let's be sure it's in the right spot //Debug.Assert(child == children[childIndex], "Wrong child was generated"); } // Measurements will depend on layout algorithm if (child != null) child.Measure(GetChildSize()); } } } } // Note: this could be deferred to idle time for efficiency CleanUpItems(visibleRange, columCount); }
public void Refresh() { _calculatedVisibleRange = VisibleRange.Empty; }