• 备份一个支持虚拟化的wrappanel


        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
        }
  • 相关阅读:
    Python实现网络多人聊天室
    C实现string字符串
    C getchar()
    C++ 数组遍历的两种方式
    Java 发红包
    Java递归实现阶乘
    Go defer语句
    bash shell——sum
    C——letterCounter
    数据结构——链队列(linked queue)
  • 原文地址:https://www.cnblogs.com/nocanstillbb/p/10675902.html
Copyright © 2020-2023  润新知