WPF Virtualizing Panel

来源:互联网 发布:淘宝买csgo怎么下载 编辑:程序博客网 时间:2024/06/02 03:42

都是国外的偷笑


VirtualizingWrapPanel:

public class VirtualizingWrapPanel : VirtualizingPanel, IScrollInfo{    #region Fields    UIElementCollection _children;    ItemsControl _itemsControl;    IItemContainerGenerator _generator;    private Point _offset = new Point(0, 0);    private Size _extent = new Size(0, 0);    private Size _viewport = new Size(0, 0);    private int firstIndex = 0;    private Size childSize;    private Size _pixelMeasuredViewport = new Size(0, 0);    Dictionary<UIElement, Rect> _realizedChildLayout = new Dictionary<UIElement, Rect>();    WrapPanelAbstraction _abstractPanel;    #endregion    #region Properties    private Size ChildSlotSize    {        get        {            return new Size(ItemWidth, ItemHeight);        }    }    #endregion    #region Dependency Properties    [TypeConverter(typeof(LengthConverter))]    public double ItemHeight    {        get        {            return (double)base.GetValue(ItemHeightProperty);        }        set        {            base.SetValue(ItemHeightProperty, value);        }    }    [TypeConverter(typeof(LengthConverter))]    public double ItemWidth    {        get        {            return (double)base.GetValue(ItemWidthProperty);        }        set        {            base.SetValue(ItemWidthProperty, value);        }    }    public Orientation Orientation    {        get { return (Orientation)GetValue(OrientationProperty); }        set { SetValue(OrientationProperty, value); }    }    public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));    public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));    public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner(typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(Orientation.Horizontal));    #endregion    #region Methods    public void SetFirstRowViewItemIndex(int index)    {        SetVerticalOffset((index) / Math.Floor((_viewport.Width) / childSize.Width));        SetHorizontalOffset((index) / Math.Floor((_viewport.Height) / childSize.Height));    }    private void Resizing(object sender, EventArgs e)    {        if (_viewport.Width != 0)        {            int firstIndexCache = firstIndex;            _abstractPanel = null;            MeasureOverride(_viewport);            SetFirstRowViewItemIndex(firstIndex);            firstIndex = firstIndexCache;        }    }    public int GetFirstVisibleSection()    {        int section;        var maxSection = _abstractPanel.Max(x => x.Section);        if (Orientation == Orientation.Horizontal)        {            section = (int)_offset.Y;        }        else        {            section = (int)_offset.X;        }        if (section > maxSection)            section = maxSection;        return section;    }    public int GetFirstVisibleIndex()    {        int section = GetFirstVisibleSection();        var item = _abstractPanel.Where(x => x.Section == section).FirstOrDefault();        if (item != null)            return item._index;        return 0;    }    private void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)    {        for (int i = _children.Count - 1; i >= 0; i--)        {            GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);            int itemIndex = _generator.IndexFromGeneratorPosition(childGeneratorPos);            if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)            {                _generator.Remove(childGeneratorPos, 1);                RemoveInternalChildRange(i, 1);            }        }    }    private void ComputeExtentAndViewport(Size pixelMeasuredViewportSize, int visibleSections)    {        if (Orientation == Orientation.Horizontal)        {            _viewport.Height = visibleSections;            _viewport.Width = pixelMeasuredViewportSize.Width;        }        else        {            _viewport.Width = visibleSections;            _viewport.Height = pixelMeasuredViewportSize.Height;        }        if (Orientation == Orientation.Horizontal)        {            _extent.Height = _abstractPanel.SectionCount + ViewportHeight - 1;        }        else        {            _extent.Width = _abstractPanel.SectionCount + ViewportWidth - 1;        }        _owner.InvalidateScrollInfo();    }    private void ResetScrollInfo()    {        _offset.X = 0;        _offset.Y = 0;    }    private int GetNextSectionClosestIndex(int itemIndex)    {        var abstractItem = _abstractPanel[itemIndex];        if (abstractItem.Section < _abstractPanel.SectionCount - 1)        {            var ret = _abstractPanel.                Where(x => x.Section == abstractItem.Section + 1).                OrderBy(x => Math.Abs(x.SectionIndex - abstractItem.SectionIndex)).                First();            return ret._index;        }        else            return itemIndex;    }    private int GetLastSectionClosestIndex(int itemIndex)    {        var abstractItem = _abstractPanel[itemIndex];        if (abstractItem.Section > 0)        {            var ret = _abstractPanel.                Where(x => x.Section == abstractItem.Section - 1).                OrderBy(x => Math.Abs(x.SectionIndex - abstractItem.SectionIndex)).                First();            return ret._index;        }        else            return itemIndex;    }    private void NavigateDown()    {        var gen = _generator.GetItemContainerGeneratorForPanel(this);        UIElement selected = (UIElement)Keyboard.FocusedElement;        int itemIndex = gen.IndexFromContainer(selected);        int depth = 0;        while (itemIndex == -1)        {            selected = (UIElement)VisualTreeHelper.GetParent(selected);            itemIndex = gen.IndexFromContainer(selected);            depth++;        }        DependencyObject next = null;        if (Orientation == Orientation.Horizontal)        {            int nextIndex = GetNextSectionClosestIndex(itemIndex);            next = gen.ContainerFromIndex(nextIndex);            while (next == null)            {                SetVerticalOffset(VerticalOffset + 1);                UpdateLayout();                next = gen.ContainerFromIndex(nextIndex);            }        }        else        {            if (itemIndex == _abstractPanel._itemCount - 1)                return;            next = gen.ContainerFromIndex(itemIndex + 1);            while (next == null)            {                SetHorizontalOffset(HorizontalOffset + 1);                UpdateLayout();                next = gen.ContainerFromIndex(itemIndex + 1);            }        }        while (depth != 0)        {            next = VisualTreeHelper.GetChild(next, 0);            depth--;        }        (next as UIElement).Focus();    }    private void NavigateLeft()    {        var gen = _generator.GetItemContainerGeneratorForPanel(this);        UIElement selected = (UIElement)Keyboard.FocusedElement;        int itemIndex = gen.IndexFromContainer(selected);        int depth = 0;        while (itemIndex == -1)        {            selected = (UIElement)VisualTreeHelper.GetParent(selected);            itemIndex = gen.IndexFromContainer(selected);            depth++;        }        DependencyObject next = null;        if (Orientation == Orientation.Vertical)        {            int nextIndex = GetLastSectionClosestIndex(itemIndex);            next = gen.ContainerFromIndex(nextIndex);            while (next == null)            {                SetHorizontalOffset(HorizontalOffset - 1);                UpdateLayout();                next = gen.ContainerFromIndex(nextIndex);            }        }        else        {            if (itemIndex == 0)                return;            next = gen.ContainerFromIndex(itemIndex - 1);            while (next == null)            {                SetVerticalOffset(VerticalOffset - 1);                UpdateLayout();                next = gen.ContainerFromIndex(itemIndex - 1);            }        }        while (depth != 0)        {            next = VisualTreeHelper.GetChild(next, 0);            depth--;        }        (next as UIElement).Focus();    }    private void NavigateRight()    {        var gen = _generator.GetItemContainerGeneratorForPanel(this);        UIElement selected = (UIElement)Keyboard.FocusedElement;        int itemIndex = gen.IndexFromContainer(selected);        int depth = 0;        while (itemIndex == -1)        {            selected = (UIElement)VisualTreeHelper.GetParent(selected);            itemIndex = gen.IndexFromContainer(selected);            depth++;        }        DependencyObject next = null;        if (Orientation == Orientation.Vertical)        {            int nextIndex = GetNextSectionClosestIndex(itemIndex);            next = gen.ContainerFromIndex(nextIndex);            while (next == null)            {                SetHorizontalOffset(HorizontalOffset + 1);                UpdateLayout();                next = gen.ContainerFromIndex(nextIndex);            }        }        else        {            if (itemIndex == _abstractPanel._itemCount - 1)                return;            next = gen.ContainerFromIndex(itemIndex + 1);            while (next == null)            {                SetVerticalOffset(VerticalOffset + 1);                UpdateLayout();                next = gen.ContainerFromIndex(itemIndex + 1);            }        }        while (depth != 0)        {            next = VisualTreeHelper.GetChild(next, 0);            depth--;        }        (next as UIElement).Focus();    }    private void NavigateUp()    {        var gen = _generator.GetItemContainerGeneratorForPanel(this);        UIElement selected = (UIElement)Keyboard.FocusedElement;        int itemIndex = gen.IndexFromContainer(selected);        int depth = 0;        while (itemIndex == -1)        {            selected = (UIElement)VisualTreeHelper.GetParent(selected);            itemIndex = gen.IndexFromContainer(selected);            depth++;        }        DependencyObject next = null;        if (Orientation == Orientation.Horizontal)        {            int nextIndex = GetLastSectionClosestIndex(itemIndex);            next = gen.ContainerFromIndex(nextIndex);            while (next == null)            {                SetVerticalOffset(VerticalOffset - 1);                UpdateLayout();                next = gen.ContainerFromIndex(nextIndex);            }        }        else        {            if (itemIndex == 0)                return;            next = gen.ContainerFromIndex(itemIndex - 1);            while (next == null)            {                SetHorizontalOffset(HorizontalOffset - 1);                UpdateLayout();                next = gen.ContainerFromIndex(itemIndex - 1);            }        }        while (depth != 0)        {            next = VisualTreeHelper.GetChild(next, 0);            depth--;        }        (next as UIElement).Focus();    }    #endregion    #region Override    protected override void OnKeyDown(KeyEventArgs e)    {        switch (e.Key)        {            case Key.Down:                NavigateDown();                e.Handled = true;                break;            case Key.Left:                NavigateLeft();                e.Handled = true;                break;            case Key.Right:                NavigateRight();                e.Handled = true;                break;            case Key.Up:                NavigateUp();                e.Handled = true;                break;            default:                base.OnKeyDown(e);                break;        }    }    protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)    {        base.OnItemsChanged(sender, args);        _abstractPanel = null;        ResetScrollInfo();    }    protected override void OnInitialized(EventArgs e)    {        this.SizeChanged += new SizeChangedEventHandler(this.Resizing);        base.OnInitialized(e);        _itemsControl = ItemsControl.GetItemsOwner(this);        _children = InternalChildren;        _generator = ItemContainerGenerator;    }    protected override Size MeasureOverride(Size availableSize)    {        if (_itemsControl == null || _itemsControl.Items.Count == 0)            return availableSize;        if (_abstractPanel == null)            _abstractPanel = new WrapPanelAbstraction(_itemsControl.Items.Count);        _pixelMeasuredViewport = availableSize;        _realizedChildLayout.Clear();        Size realizedFrameSize = availableSize;        int itemCount = _itemsControl.Items.Count;        int firstVisibleIndex = GetFirstVisibleIndex();        GeneratorPosition startPos = _generator.GeneratorPositionFromIndex(firstVisibleIndex);        int childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;        int current = firstVisibleIndex;        int visibleSections = 1;        using (_generator.StartAt(startPos, GeneratorDirection.Forward, true))        {            bool stop = false;            bool isHorizontal = Orientation == Orientation.Horizontal;            double currentX = 0;            double currentY = 0;            double maxItemSize = 0;            int currentSection = GetFirstVisibleSection();            while (current < itemCount)            {                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);                    child.Measure(ChildSlotSize);                }                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");                }                childSize = child.DesiredSize;                Rect childRect = new Rect(new Point(currentX, currentY), childSize);                if (isHorizontal)                {                    maxItemSize = Math.Max(maxItemSize, childRect.Height);                    if (childRect.Right > realizedFrameSize.Width) //wrap to a new line                    {                        currentY = currentY + maxItemSize;                        currentX = 0;                        maxItemSize = childRect.Height;                        childRect.X = currentX;                        childRect.Y = currentY;                        currentSection++;                        visibleSections++;                    }                    if (currentY > realizedFrameSize.Height)                        stop = true;                    currentX = childRect.Right;                }                else                {                    maxItemSize = Math.Max(maxItemSize, childRect.Width);                    if (childRect.Bottom > realizedFrameSize.Height) //wrap to a new column                    {                        currentX = currentX + maxItemSize;                        currentY = 0;                        maxItemSize = childRect.Width;                        childRect.X = currentX;                        childRect.Y = currentY;                        currentSection++;                        visibleSections++;                    }                    if (currentX > realizedFrameSize.Width)                        stop = true;                    currentY = childRect.Bottom;                }                _realizedChildLayout.Add(child, childRect);                _abstractPanel.SetItemSection(current, currentSection);                if (stop)                    break;                current++;                childIndex++;            }        }        CleanUpItems(firstVisibleIndex, current - 1);        ComputeExtentAndViewport(availableSize, visibleSections);        return availableSize;    }    protected override Size ArrangeOverride(Size finalSize)    {        if (_children != null)        {            foreach (UIElement child in _children)            {                var layoutInfo = _realizedChildLayout[child];                child.Arrange(layoutInfo);            }        }        return finalSize;    }    #endregion    #region IScrollInfo Members    private bool _canHScroll = false;    public bool CanHorizontallyScroll    {        get { return _canHScroll; }        set { _canHScroll = value; }    }    private bool _canVScroll = false;    public bool CanVerticallyScroll    {        get { return _canVScroll; }        set { _canVScroll = value; }    }    public double ExtentHeight    {        get { return _extent.Height; }    }    public double ExtentWidth    {        get { return _extent.Width; }    }    public double HorizontalOffset    {        get { return _offset.X; }    }    public double VerticalOffset    {        get { return _offset.Y; }    }    public void LineDown()    {        if (Orientation == Orientation.Vertical)            SetVerticalOffset(VerticalOffset + 20);        else            SetVerticalOffset(VerticalOffset + 1);    }    public void LineLeft()    {        if (Orientation == Orientation.Horizontal)            SetHorizontalOffset(HorizontalOffset - 20);        else            SetHorizontalOffset(HorizontalOffset - 1);    }    public void LineRight()    {        if (Orientation == Orientation.Horizontal)            SetHorizontalOffset(HorizontalOffset + 20);        else            SetHorizontalOffset(HorizontalOffset + 1);    }    public void LineUp()    {        if (Orientation == Orientation.Vertical)            SetVerticalOffset(VerticalOffset - 20);        else            SetVerticalOffset(VerticalOffset - 1);    }    public Rect MakeVisible(Visual visual, Rect rectangle)    {        var gen = (ItemContainerGenerator)_generator.GetItemContainerGeneratorForPanel(this);        var element = (UIElement)visual;        int itemIndex = gen.IndexFromContainer(element);        while (itemIndex == -1)        {            element = (UIElement)VisualTreeHelper.GetParent(element);            itemIndex = gen.IndexFromContainer(element);        }        int section = _abstractPanel[itemIndex].Section;        Rect elementRect = _realizedChildLayout[element];        if (Orientation == Orientation.Horizontal)        {            double viewportHeight = _pixelMeasuredViewport.Height;            if (elementRect.Bottom > viewportHeight)                _offset.Y += 1;            else if (elementRect.Top < 0)                _offset.Y -= 1;        }        else        {            double viewportWidth = _pixelMeasuredViewport.Width;            if (elementRect.Right > viewportWidth)                _offset.X += 1;            else if (elementRect.Left < 0)                _offset.X -= 1;        }        InvalidateMeasure();        return elementRect;    }    public void MouseWheelDown()    {        PageDown();    }    public void MouseWheelLeft()    {        PageLeft();    }    public void MouseWheelRight()    {        PageRight();    }    public void MouseWheelUp()    {        PageUp();    }    public void PageDown()    {        SetVerticalOffset(VerticalOffset + _viewport.Height * 0.8);    }    public void PageLeft()    {        SetHorizontalOffset(HorizontalOffset - _viewport.Width * 0.8);    }    public void PageRight()    {        SetHorizontalOffset(HorizontalOffset + _viewport.Width * 0.8);    }    public void PageUp()    {        SetVerticalOffset(VerticalOffset - _viewport.Height * 0.8);    }    private ScrollViewer _owner;    public ScrollViewer ScrollOwner    {        get { return _owner; }        set { _owner = value; }    }    public void SetHorizontalOffset(double offset)    {        if (offset < 0 || _viewport.Width >= _extent.Width)        {            offset = 0;        }        else        {            if (offset + _viewport.Width >= _extent.Width)            {                offset = _extent.Width - _viewport.Width;            }        }        _offset.X = offset;        if (_owner != null)            _owner.InvalidateScrollInfo();        InvalidateMeasure();        firstIndex = GetFirstVisibleIndex();    }    public void SetVerticalOffset(double offset)    {        if (offset < 0 || _viewport.Height >= _extent.Height)        {            offset = 0;        }        else        {            if (offset + _viewport.Height >= _extent.Height)            {                offset = _extent.Height - _viewport.Height;            }        }        _offset.Y = offset;        if (_owner != null)            _owner.InvalidateScrollInfo();        //_trans.Y = -offset;        InvalidateMeasure();        firstIndex = GetFirstVisibleIndex();    }    public double ViewportHeight    {        get { return _viewport.Height; }    }    public double ViewportWidth    {        get { return _viewport.Width; }    }    #endregion    #region helper data structures    class ItemAbstraction    {        public ItemAbstraction(WrapPanelAbstraction panel, int index)        {            _panel = panel;            _index = index;        }        WrapPanelAbstraction _panel;        public readonly int _index;        int _sectionIndex = -1;        public int SectionIndex        {            get            {                if (_sectionIndex == -1)                {                    return _index % _panel._averageItemsPerSection - 1;                }                return _sectionIndex;            }            set            {                if (_sectionIndex == -1)                    _sectionIndex = value;            }        }        int _section = -1;        public int Section        {            get            {                if (_section == -1)                {                    return _index / _panel._averageItemsPerSection;                }                return _section;            }            set            {                if (_section == -1)                    _section = value;            }        }    }    class WrapPanelAbstraction : IEnumerable<ItemAbstraction>    {        public WrapPanelAbstraction(int itemCount)        {            List<ItemAbstraction> items = new List<ItemAbstraction>(itemCount);            for (int i = 0; i < itemCount; i++)            {                ItemAbstraction item = new ItemAbstraction(this, i);                items.Add(item);            }            Items = new ReadOnlyCollection<ItemAbstraction>(items);            _averageItemsPerSection = itemCount;            _itemCount = itemCount;        }        public readonly int _itemCount;        public int _averageItemsPerSection;        private int _currentSetSection = -1;        private int _currentSetItemIndex = -1;        private int _itemsInCurrentSecction = 0;        private object _syncRoot = new object();        public int SectionCount        {            get            {                int ret = _currentSetSection + 1;                if (_currentSetItemIndex + 1 < Items.Count)                {                    int itemsLeft = Items.Count - _currentSetItemIndex;                    ret += itemsLeft / _averageItemsPerSection + 1;                }                return ret;            }        }        private ReadOnlyCollection<ItemAbstraction> Items { get; set; }        public void SetItemSection(int index, int section)        {            lock (_syncRoot)            {                if (section <= _currentSetSection + 1 && index == _currentSetItemIndex + 1)                {                    _currentSetItemIndex++;                    Items[index].Section = section;                    if (section == _currentSetSection + 1)                    {                        _currentSetSection = section;                        if (section > 0)                        {                            _averageItemsPerSection = (index) / (section);                        }                        _itemsInCurrentSecction = 1;                    }                    else                        _itemsInCurrentSecction++;                    Items[index].SectionIndex = _itemsInCurrentSecction - 1;                }            }        }        public ItemAbstraction this[int index]        {            get { return Items[index]; }        }        #region IEnumerable<ItemAbstraction> Members        public IEnumerator<ItemAbstraction> GetEnumerator()        {            return Items.GetEnumerator();        }        #endregion        #region IEnumerable Members        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()        {            return GetEnumerator();        }        #endregion    }    #endregion}


VirtualizingUniformGridPanel

class VirtualizingUniformGridPanel : VirtualizingPanel, IScrollInfo{    private Size _extent = new Size(0, 0);    private Size _viewport = new Size(0, 0);    private Point _offset = new Point(0, 0);    private bool _canHorizontallyScroll = false;    private bool _canVerticallyScroll = false;    private ScrollViewer _owner;    private int _scrollLength = 25;    //-----------------------------------------    //    // Dependency Properties    //    //-----------------------------------------    #region Dependency Properties     /// <summary>    /// Columns DependencyProperty    /// </summary>    public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(VirtualizingUniformGridPanel),        new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));    /// <summary>    /// Rows DependencyProperty    /// </summary>    public static readonly DependencyProperty RowsProperty = DependencyProperty.Register("Rows", typeof(int), typeof(VirtualizingUniformGridPanel),        new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));    /// <summary>    /// Orientation DependencyProperty    /// </summary>    public static readonly DependencyProperty OrientationProperty = DependencyProperty.RegisterAttached("Orientation", typeof(Orientation), typeof(VirtualizingUniformGridPanel),        new FrameworkPropertyMetadata(Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));    #endregion Dependency Properties    //-----------------------------------------    //    // Public Properties    //    //-----------------------------------------    #region Public Properties    /// <summary>    /// Get/Set the amount of columns this grid should have    /// </summary>    public int Columns    {        get { return (int)this.GetValue(ColumnsProperty); }        set { this.SetValue(ColumnsProperty, value); }    }    /// <summary>    /// Get/Set the amount of rows this grid should have    /// </summary>    public int Rows    {        get { return (int)this.GetValue(RowsProperty); }        set { this.SetValue(RowsProperty, value); }    }    /// <summary>    /// Get/Set the orientation of the panel    /// </summary>    public Orientation Orientation    {        get { return (Orientation)this.GetValue(OrientationProperty); }        set { this.SetValue(OrientationProperty, value); }    }    #endregion Public Properties    //-----------------------------------------    //    // Overrides    //    //-----------------------------------------    #region Overrides    /// <summary>    /// When items are removed, remove the corresponding UI if necessary    /// </summary>    /// <param name="sender"></param>    /// <param name="args"></param>    protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)    {        switch (args.Action)        {            case NotifyCollectionChangedAction.Remove:            case NotifyCollectionChangedAction.Replace:            case NotifyCollectionChangedAction.Move:                RemoveInternalChildRange(args.Position.Index, args.ItemUICount);                break;        }    }    /// <summary>    /// Measure the children    /// </summary>    /// <param name="availableSize">Size available</param>    /// <returns>Size desired</returns>    protected override Size MeasureOverride(Size availableSize)    {        UpdateScrollInfo(availableSize);        int firstVisibleItemIndex, lastVisibleItemIndex;        GetVisibleRange(out firstVisibleItemIndex, out lastVisibleItemIndex);        // We need to access InternalChildren before the generator to work around a bug        UIElementCollection children = this.InternalChildren;        IItemContainerGenerator generator = this.ItemContainerGenerator;        // Get the generator position of the first visible data item        GeneratorPosition startPos = generator.GeneratorPositionFromIndex(firstVisibleItemIndex);        // 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 = firstVisibleItemIndex; itemIndex <= lastVisibleItemIndex; ++itemIndex, ++childIndex)            {                bool newlyRealized;                // Get or create the child                UIElement child = generator.GenerateNext(out newlyRealized) as UIElement;                childIndex = Math.Max(0, childIndex);                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);                }                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                child.Measure(GetChildSize(availableSize));            }        }        // Note: this could be deferred to idle time for efficiency        CleanUpItems(firstVisibleItemIndex, lastVisibleItemIndex);        if (availableSize.Height.Equals(double.PositiveInfinity))        {            Debug.WriteLine(_extent);            return new Size(200, 200);        }        return availableSize;    }    /// <summary>    /// Arrange the children    /// </summary>    /// <param name="finalSize">Size available</param>    /// <returns>Size used</returns>    protected override Size ArrangeOverride(Size finalSize)    {        IItemContainerGenerator generator = this.ItemContainerGenerator;        UpdateScrollInfo(finalSize);        for (int i = 0; i < this.Children.Count; i++)        {            UIElement child = this.Children[i];            int itemIndex = generator.IndexFromGeneratorPosition(new GeneratorPosition(i, 0));            ArrangeChild(itemIndex, child, finalSize);        }        return finalSize;    }    #endregion Overrides    //-----------------------------------------    //    // Layout Specific Code    //    //-----------------------------------------    #region Layout Specific Code    /// <summary>    /// Revisualizes items that are no longer visible    /// </summary>    /// <param name="minDesiredGenerated">first item index that should be visible</param>    /// <param name="maxDesiredGenerated">last item index that should be visible</param>    private void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)    {        UIElementCollection children = this.InternalChildren;        IItemContainerGenerator generator = this.ItemContainerGenerator;        for (int i = children.Count - 1; i >= 0; i--)        {            GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);            int itemIndex = generator.IndexFromGeneratorPosition(childGeneratorPos);            if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)            {                generator.Remove(childGeneratorPos, 1);                RemoveInternalChildRange(i, 1);            }        }    }    /// <summary>    /// Calculate the extent of the view based on the available size    /// </summary>    /// <param name="availableSize"></param>    /// <returns></returns>    private Size MeasureExtent(Size availableSize, int itemsCount)    {        Size childSize = GetChildSize(availableSize);        if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)        {            return new Size((this.Columns * childSize.Width) * Math.Ceiling((double)itemsCount / (this.Columns * this.Rows)), _viewport.Height);        }        else        {            var pageHeight = (this.Rows * childSize.Height);            var sizeWidth = _viewport.Width;            var sizeHeight = pageHeight * Math.Ceiling((double)itemsCount / (this.Rows * this.Columns));            return new Size(sizeWidth, sizeHeight);        }    }    /// <summary>    /// Arrange the individual children    /// </summary>    /// <param name="index"></param>    /// <param name="child"></param>    /// <param name="finalSize"></param>    private void ArrangeChild(int index, UIElement child, Size finalSize)    {        int row = index / this.Columns;        int column = index % this.Columns;        double xPosition, yPosition;        int currentPage;        Size childSize = GetChildSize(finalSize);        if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)        {            currentPage = (int)Math.Floor((double)index / (this.Columns * this.Rows));            xPosition = (currentPage * this._viewport.Width) + (column * childSize.Width);            yPosition = (row % this.Rows) * childSize.Height;            xPosition -= this._offset.X;            yPosition -= this._offset.Y;        }        else        {            xPosition = (column * childSize.Width) - this._offset.X;            yPosition = (row * childSize.Height) - this._offset.Y;        }        child.Arrange(new Rect(xPosition, yPosition, childSize.Width, childSize.Height));    }    /// <summary>    /// Get the size of the child element    /// </summary>    /// <param name="availableSize"></param>    /// <returns>Returns the size of the child</returns>    private Size GetChildSize(Size availableSize)    {        double width = availableSize.Width / this.Columns;        double height = availableSize.Height / this.Rows;        return new Size(width, height);    }    /// <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 void GetVisibleRange(out int firstVisibleItemIndex, out int lastVisibleItemIndex)    {        Size childSize = GetChildSize(this._extent);        int pageSize = this.Columns * this.Rows;        int pageNumber = this.Orientation == System.Windows.Controls.Orientation.Horizontal ?            (int)Math.Floor((double)this._offset.X / this._viewport.Width) :            (int)Math.Floor((double)this._offset.Y / this._viewport.Height);        firstVisibleItemIndex = (pageNumber * pageSize);        lastVisibleItemIndex = firstVisibleItemIndex + (pageSize * 2) - 1;        ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);        int itemCount = itemsControl.HasItems ? itemsControl.Items.Count : 0;        if (lastVisibleItemIndex >= itemCount)        {            lastVisibleItemIndex = itemCount - 1;        }    }    #endregion    //-----------------------------------------    //    // IScrollInfo Implementation    //    //-----------------------------------------    #region IScrollInfo Implementation    public bool CanHorizontallyScroll    {        get { return _canHorizontallyScroll; }        set { _canHorizontallyScroll = value; }    }    public bool CanVerticallyScroll    {        get { return _canVerticallyScroll; }        set { _canVerticallyScroll = value; }    }    /// <summary>    /// Get the extent height    /// </summary>    public double ExtentHeight    {        get { return this._extent.Height; }    }    /// <summary>    /// Get the extent width    /// </summary>    public double ExtentWidth    {        get { return this._extent.Width; }    }    /// <summary>    /// Get the current horizontal offset    /// </summary>    public double HorizontalOffset    {        get { return this._offset.X; }    }    /// <summary>    /// Get the current vertical offset    /// </summary>    public double VerticalOffset    {        get { return this._offset.Y; }    }    /// <summary>    /// Get/Set the scrollowner    /// </summary>    public System.Windows.Controls.ScrollViewer ScrollOwner    {        get { return this._owner; }        set { this._owner = value; }    }    /// <summary>    /// Get the Viewport Height    /// </summary>    public double ViewportHeight    {        get { return _viewport.Height; }    }    /// <summary>    /// Get the Viewport Width    /// </summary>    public double ViewportWidth    {        get { return _viewport.Width; }    }    public void LineLeft()    {        this.SetHorizontalOffset(this._offset.X - _scrollLength);    }    public void LineRight()    {        this.SetHorizontalOffset(this._offset.X + _scrollLength);    }    public void LineUp()    {        this.SetVerticalOffset(this._offset.Y - _scrollLength);    }    public void LineDown()    {        this.SetVerticalOffset(this._offset.Y + _scrollLength);    }    public Rect MakeVisible(System.Windows.Media.Visual visual, Rect rectangle)    {        return new Rect();    }    public void MouseWheelDown()    {        if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)        {            this.SetHorizontalOffset(this._offset.X + _scrollLength);        }        else        {            this.SetVerticalOffset(this._offset.Y + _scrollLength);        }    }    public void MouseWheelUp()    {        if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)        {            this.SetHorizontalOffset(this._offset.X - _scrollLength);        }        else        {            this.SetVerticalOffset(this._offset.Y - _scrollLength);        }    }    public void MouseWheelLeft()    {        return;    }    public void MouseWheelRight()    {        return;    }    public void PageDown()    {        this.SetVerticalOffset(this._offset.Y + _viewport.Width);    }    public void PageUp()    {        this.SetVerticalOffset(this._offset.Y - _viewport.Width);    }    public void PageLeft()    {        this.SetHorizontalOffset(this._offset.X - _viewport.Width);    }    public void PageRight()    {        this.SetHorizontalOffset(this._offset.X + _viewport.Width);    }    public void SetHorizontalOffset(double offset)    {        _offset.X = Math.Max(0, offset);        if (_owner != null)        {            _owner.InvalidateScrollInfo();        }        InvalidateMeasure();    }    public void SetVerticalOffset(double offset)    {        _offset.Y = Math.Max(0, offset);        if (_owner != null)        {            _owner.InvalidateScrollInfo();        }        InvalidateMeasure();    }    private void UpdateScrollInfo(Size availableSize)    {        // See how many items there are        ItemsControl itemsControl = ItemsControl.GetItemsOwner(this);        int itemCount = itemsControl.HasItems ? itemsControl.Items.Count : 0;        Size extent = MeasureExtent(availableSize, itemCount);        // Update extent        if (extent != _extent)        {            _extent = extent;            if (_owner != null)                _owner.InvalidateScrollInfo();        }        // Update viewport        if (availableSize != _viewport)        {            _viewport = availableSize;            if (_owner != null)                _owner.InvalidateScrollInfo();        }    }    #endregion IScrollInfo Implementation}


使用

<ListBox ScrollViewer.VerticalScrollBarVisibility="Hidden">    <ListBox.ItemsPanel>        <ItemsPanelTemplate>            <control:VirtualizingUniformGridPanel Rows="2" Columns="3" Orientation="Vertical"/>        </ItemsPanelTemplate>    </ListBox.ItemsPanel>    <ListBox.Items>        <ListBoxItem Margin="1" BorderBrush="Red" BorderThickness="2">1</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="Green" BorderThickness="2">2</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="Blue" BorderThickness="2">3</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="BlueViolet" BorderThickness="2">4</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="RosyBrown" BorderThickness="2">5</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="RoyalBlue" BorderThickness="2">6</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="Orange" BorderThickness="2">7</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="OrangeRed" BorderThickness="2">8</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="GreenYellow" BorderThickness="2">9</ListBoxItem>        <ListBoxItem Margin="1" BorderBrush="YellowGreen" BorderThickness="2">10</ListBoxItem>    </ListBox.Items></ListBox>


原创粉丝点击