备份一个 VirtualizingWrapPanel ,支持虚拟化

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Collections.ObjectModel;
  4 using System.ComponentModel;
  5 using System.Diagnostics;
  6 using System.Linq;
  7 using System.Windows;
  8 using System.Windows.Controls;
  9 using System.Windows.Controls.Primitives;
 10 using System.Windows.Input;
 11 using System.Windows.Media;
 12 
 13 namespace TreeViewDemo
 14 {
 15     /// <summary>
 16     /// 支持虚拟化的wrappanel
 17     /// </summary>
 18     public class VirtualizingWrapPanel : VirtualizingPanel, IScrollInfo
 19     {
 20         #region Fields
 21 
 22         UIElementCollection _children;
 23         ItemsControl _itemsControl;
 24         IItemContainerGenerator _generator;
 25         private Point _offset = new Point(0, 0);
 26         private Size _extent = new Size(0, 0);
 27         private Size _viewport = new Size(0, 0);
 28         private int firstIndex = 0;
 29         private Size childSize;
 30         private Size _pixelMeasuredViewport = new Size(0, 0);
 31         Dictionary<UIElement, Rect> _realizedChildLayout = new Dictionary<UIElement, Rect>();
 32         WrapPanelAbstraction _abstractPanel;
 33 
 34 
 35         #endregion
 36 
 37         #region Properties
 38 
 39         private Size ChildSlotSize
 40         {
 41             get
 42             {
 43                 return new Size(ItemWidth, ItemHeight);
 44             }
 45         }
 46 
 47         #endregion
 48 
 49         #region Dependency Properties
 50 
 51         [TypeConverter(typeof(LengthConverter))]
 52         public double ItemHeight
 53         {
 54             get
 55             {
 56                 return (double)base.GetValue(ItemHeightProperty);
 57             }
 58             set
 59             {
 60                 base.SetValue(ItemHeightProperty, value);
 61             }
 62         }
 63 
 64         [TypeConverter(typeof(LengthConverter))]
 65         public double ItemWidth
 66         {
 67             get
 68             {
 69                 return (double)base.GetValue(ItemWidthProperty);
 70             }
 71             set
 72             {
 73                 base.SetValue(ItemWidthProperty, value);
 74             }
 75         }
 76 
 77         public Orientation Orientation
 78         {
 79             get { return (Orientation)GetValue(OrientationProperty); }
 80             set { SetValue(OrientationProperty, value); }
 81         }
 82 
 83         public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));
 84         public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));
 85         public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner(typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(Orientation.Horizontal));
 86 
 87         #endregion
 88 
 89         #region Methods
 90 
 91         public void SetFirstRowViewItemIndex(int index)
 92         {
 93             SetVerticalOffset((index) / Math.Floor((_viewport.Width) / childSize.Width));
 94             SetHorizontalOffset((index) / Math.Floor((_viewport.Height) / childSize.Height));
 95         }
 96 
 97         private void Resizing(object sender, EventArgs e)
 98         {
 99             if (_viewport.Width != 0)
100             {
101                 int firstIndexCache = firstIndex;
102                 _abstractPanel = null;
103                 MeasureOverride(_viewport);
104                 SetFirstRowViewItemIndex(firstIndex);
105                 firstIndex = firstIndexCache;
106             }
107         }
108 
109         public int GetFirstVisibleSection()
110         {
111             int section;
112             var maxSection = _abstractPanel.Max(x => x.Section);
113             if (Orientation == Orientation.Horizontal)
114             {
115                 section = (int)_offset.Y;
116             }
117             else
118             {
119                 section = (int)_offset.X;
120             }
121             if (section > maxSection)
122                 section = maxSection;
123             return section;
124         }
125 
126         public int GetFirstVisibleIndex()
127         {
128             int section = GetFirstVisibleSection();
129             var item = _abstractPanel.Where(x => x.Section == section).FirstOrDefault();
130             if (item != null)
131                 return item._index;
132             return 0;
133         }
134 
135         private void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)
136         {
137             for (int i = _children.Count - 1; i >= 0; i--)
138             {
139                 GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);
140                 int itemIndex = _generator.IndexFromGeneratorPosition(childGeneratorPos);
141                 if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)
142                 {
143                     _generator.Remove(childGeneratorPos, 1);
144                     RemoveInternalChildRange(i, 1);
145                 }
146             }
147         }
148 
149         private void ComputeExtentAndViewport(Size pixelMeasuredViewportSize, int visibleSections)
150         {
151             if (Orientation == Orientation.Horizontal)
152             {
153                 _viewport.Height = visibleSections;
154                 _viewport.Width = pixelMeasuredViewportSize.Width;
155             }
156             else
157             {
158                 _viewport.Width = visibleSections;
159                 _viewport.Height = pixelMeasuredViewportSize.Height;
160             }
161 
162             if (Orientation == Orientation.Horizontal)
163             {
164                 _extent.Height = _abstractPanel.SectionCount + ViewportHeight - 1;
165 
166             }
167             else
168             {
169                 _extent.Width = _abstractPanel.SectionCount + ViewportWidth - 1;
170             }
171             _owner.InvalidateScrollInfo();
172         }
173 
174         private void ResetScrollInfo()
175         {
176             _offset.X = 0;
177             _offset.Y = 0;
178         }
179 
180         private int GetNextSectionClosestIndex(int itemIndex)
181         {
182             var abstractItem = _abstractPanel[itemIndex];
183             if (abstractItem.Section < _abstractPanel.SectionCount - 1)
184             {
185                 var ret = _abstractPanel.
186                     Where(x => x.Section == abstractItem.Section + 1).
187                     OrderBy(x => Math.Abs(x.SectionIndex - abstractItem.SectionIndex)).
188                     First();
189                 return ret._index;
190             }
191             else
192                 return itemIndex;
193         }
194 
195         private int GetLastSectionClosestIndex(int itemIndex)
196         {
197             var abstractItem = _abstractPanel[itemIndex];
198             if (abstractItem.Section > 0)
199             {
200                 var ret = _abstractPanel.
201                     Where(x => x.Section == abstractItem.Section - 1).
202                     OrderBy(x => Math.Abs(x.SectionIndex - abstractItem.SectionIndex)).
203                     First();
204                 return ret._index;
205             }
206             else
207                 return itemIndex;
208         }
209 
210         private void NavigateDown()
211         {
212             var gen = _generator.GetItemContainerGeneratorForPanel(this);
213             UIElement selected = (UIElement)Keyboard.FocusedElement;
214             int itemIndex = gen.IndexFromContainer(selected);
215             int depth = 0;
216             while (itemIndex == -1)
217             {
218                 selected = (UIElement)VisualTreeHelper.GetParent(selected);
219                 itemIndex = gen.IndexFromContainer(selected);
220                 depth++;
221             }
222             DependencyObject next = null;
223             if (Orientation == Orientation.Horizontal)
224             {
225                 int nextIndex = GetNextSectionClosestIndex(itemIndex);
226                 next = gen.ContainerFromIndex(nextIndex);
227                 while (next == null)
228                 {
229                     SetVerticalOffset(VerticalOffset + 1);
230                     UpdateLayout();
231                     next = gen.ContainerFromIndex(nextIndex);
232                 }
233             }
234             else
235             {
236                 if (itemIndex == _abstractPanel._itemCount - 1)
237                     return;
238                 next = gen.ContainerFromIndex(itemIndex + 1);
239                 while (next == null)
240                 {
241                     SetHorizontalOffset(HorizontalOffset + 1);
242                     UpdateLayout();
243                     next = gen.ContainerFromIndex(itemIndex + 1);
244                 }
245             }
246             while (depth != 0)
247             {
248                 next = VisualTreeHelper.GetChild(next, 0);
249                 depth--;
250             }
251             (next as UIElement).Focus();
252         }
253 
254         private void NavigateLeft()
255         {
256             var gen = _generator.GetItemContainerGeneratorForPanel(this);
257 
258             UIElement selected = (UIElement)Keyboard.FocusedElement;
259             int itemIndex = gen.IndexFromContainer(selected);
260             int depth = 0;
261             while (itemIndex == -1)
262             {
263                 selected = (UIElement)VisualTreeHelper.GetParent(selected);
264                 itemIndex = gen.IndexFromContainer(selected);
265                 depth++;
266             }
267             DependencyObject next = null;
268             if (Orientation == Orientation.Vertical)
269             {
270                 int nextIndex = GetLastSectionClosestIndex(itemIndex);
271                 next = gen.ContainerFromIndex(nextIndex);
272                 while (next == null)
273                 {
274                     SetHorizontalOffset(HorizontalOffset - 1);
275                     UpdateLayout();
276                     next = gen.ContainerFromIndex(nextIndex);
277                 }
278             }
279             else
280             {
281                 if (itemIndex == 0)
282                     return;
283                 next = gen.ContainerFromIndex(itemIndex - 1);
284                 while (next == null)
285                 {
286                     SetVerticalOffset(VerticalOffset - 1);
287                     UpdateLayout();
288                     next = gen.ContainerFromIndex(itemIndex - 1);
289                 }
290             }
291             while (depth != 0)
292             {
293                 next = VisualTreeHelper.GetChild(next, 0);
294                 depth--;
295             }
296             (next as UIElement).Focus();
297         }
298 
299         private void NavigateRight()
300         {
301             var gen = _generator.GetItemContainerGeneratorForPanel(this);
302             UIElement selected = (UIElement)Keyboard.FocusedElement;
303             int itemIndex = gen.IndexFromContainer(selected);
304             int depth = 0;
305             while (itemIndex == -1)
306             {
307                 selected = (UIElement)VisualTreeHelper.GetParent(selected);
308                 itemIndex = gen.IndexFromContainer(selected);
309                 depth++;
310             }
311             DependencyObject next = null;
312             if (Orientation == Orientation.Vertical)
313             {
314                 int nextIndex = GetNextSectionClosestIndex(itemIndex);
315                 next = gen.ContainerFromIndex(nextIndex);
316                 while (next == null)
317                 {
318                     SetHorizontalOffset(HorizontalOffset + 1);
319                     UpdateLayout();
320                     next = gen.ContainerFromIndex(nextIndex);
321                 }
322             }
323             else
324             {
325                 if (itemIndex == _abstractPanel._itemCount - 1)
326                     return;
327                 next = gen.ContainerFromIndex(itemIndex + 1);
328                 while (next == null)
329                 {
330                     SetVerticalOffset(VerticalOffset + 1);
331                     UpdateLayout();
332                     next = gen.ContainerFromIndex(itemIndex + 1);
333                 }
334             }
335             while (depth != 0)
336             {
337                 next = VisualTreeHelper.GetChild(next, 0);
338                 depth--;
339             }
340             (next as UIElement).Focus();
341         }
342 
343         private void NavigateUp()
344         {
345             var gen = _generator.GetItemContainerGeneratorForPanel(this);
346             UIElement selected = (UIElement)Keyboard.FocusedElement;
347             int itemIndex = gen.IndexFromContainer(selected);
348             int depth = 0;
349             while (itemIndex == -1)
350             {
351                 selected = (UIElement)VisualTreeHelper.GetParent(selected);
352                 itemIndex = gen.IndexFromContainer(selected);
353                 depth++;
354             }
355             DependencyObject next = null;
356             if (Orientation == Orientation.Horizontal)
357             {
358                 int nextIndex = GetLastSectionClosestIndex(itemIndex);
359                 next = gen.ContainerFromIndex(nextIndex);
360                 while (next == null)
361                 {
362                     SetVerticalOffset(VerticalOffset - 1);
363                     UpdateLayout();
364                     next = gen.ContainerFromIndex(nextIndex);
365                 }
366             }
367             else
368             {
369                 if (itemIndex == 0)
370                     return;
371                 next = gen.ContainerFromIndex(itemIndex - 1);
372                 while (next == null)
373                 {
374                     SetHorizontalOffset(HorizontalOffset - 1);
375                     UpdateLayout();
376                     next = gen.ContainerFromIndex(itemIndex - 1);
377                 }
378             }
379             while (depth != 0)
380             {
381                 next = VisualTreeHelper.GetChild(next, 0);
382                 depth--;
383             }
384             (next as UIElement).Focus();
385         }
386 
387 
388         #endregion
389 
390         #region Override
391 
392         protected override void OnKeyDown(KeyEventArgs e)
393         {
394             switch (e.Key)
395             {
396                 case Key.Down:
397                     NavigateDown();
398                     e.Handled = true;
399                     break;
400                 case Key.Left:
401                     NavigateLeft();
402                     e.Handled = true;
403                     break;
404                 case Key.Right:
405                     NavigateRight();
406                     e.Handled = true;
407                     break;
408                 case Key.Up:
409                     NavigateUp();
410                     e.Handled = true;
411                     break;
412                 default:
413                     base.OnKeyDown(e);
414                     break;
415             }
416         }
417 
418 
419         protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)
420         {
421             base.OnItemsChanged(sender, args);
422             _abstractPanel = null;
423             ResetScrollInfo();
424         }
425 
426         protected override void OnInitialized(EventArgs e)
427         {
428             this.SizeChanged += new SizeChangedEventHandler(this.Resizing);
429             base.OnInitialized(e);
430             _itemsControl = ItemsControl.GetItemsOwner(this);
431             _children = InternalChildren;
432             _generator = ItemContainerGenerator;
433         }
434 
435         protected override Size MeasureOverride(Size availableSize)
436         {
437             if (_itemsControl == null || _itemsControl.Items.Count == 0)
438                 return availableSize;
439             if (_abstractPanel == null)
440                 _abstractPanel = new WrapPanelAbstraction(_itemsControl.Items.Count);
441 
442             _pixelMeasuredViewport = availableSize;
443 
444             _realizedChildLayout.Clear();
445 
446             Size realizedFrameSize = availableSize;
447 
448             int itemCount = _itemsControl.Items.Count;
449             int firstVisibleIndex = GetFirstVisibleIndex();
450 
451             GeneratorPosition startPos = _generator.GeneratorPositionFromIndex(firstVisibleIndex);
452 
453             int childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;
454             int current = firstVisibleIndex;
455             int visibleSections = 1;
456             using (_generator.StartAt(startPos, GeneratorDirection.Forward, true))
457             {
458                 bool stop = false;
459                 bool isHorizontal = Orientation == Orientation.Horizontal;
460                 double currentX = 0;
461                 double currentY = 0;
462                 double maxItemSize = 0;
463                 int currentSection = GetFirstVisibleSection();
464                 while (current < itemCount)
465                 {
466                     bool newlyRealized;
467 
468                     // Get or create the child                    
469                     UIElement child = _generator.GenerateNext(out newlyRealized) as UIElement;
470                     if (newlyRealized)
471                     {
472                         // Figure out if we need to insert the child at the end or somewhere in the middle
473                         if (childIndex >= _children.Count)
474                         {
475                             base.AddInternalChild(child);
476                         }
477                         else
478                         {
479                             base.InsertInternalChild(childIndex, child);
480                         }
481                         _generator.PrepareItemContainer(child);
482                         child.Measure(ChildSlotSize);
483                     }
484                     else
485                     {
486                         // The child has already been created, let's be sure it's in the right spot
487                         Debug.Assert(child == _children[childIndex], "Wrong child was generated");
488                     }
489                     childSize = child.DesiredSize;
490                     Rect childRect = new Rect(new Point(currentX, currentY), childSize);
491                     if (isHorizontal)
492                     {
493                         maxItemSize = Math.Max(maxItemSize, childRect.Height);
494                         if (childRect.Right > realizedFrameSize.Width) //wrap to a new line
495                         {
496                             currentY = currentY + maxItemSize;
497                             currentX = 0;
498                             maxItemSize = childRect.Height;
499                             childRect.X = currentX;
500                             childRect.Y = currentY;
501                             currentSection++;
502                             visibleSections++;
503                         }
504                         if (currentY > realizedFrameSize.Height)
505                             stop = true;
506                         currentX = childRect.Right;
507                     }
508                     else
509                     {
510                         maxItemSize = Math.Max(maxItemSize, childRect.Width);
511                         if (childRect.Bottom > realizedFrameSize.Height) //wrap to a new column
512                         {
513                             currentX = currentX + maxItemSize;
514                             currentY = 0;
515                             maxItemSize = childRect.Width;
516                             childRect.X = currentX;
517                             childRect.Y = currentY;
518                             currentSection++;
519                             visibleSections++;
520                         }
521                         if (currentX > realizedFrameSize.Width)
522                             stop = true;
523                         currentY = childRect.Bottom;
524                     }
525                     _realizedChildLayout.Add(child, childRect);
526                     _abstractPanel.SetItemSection(current, currentSection);
527 
528                     if (stop)
529                         break;
530                     current++;
531                     childIndex++;
532                 }
533             }
534             CleanUpItems(firstVisibleIndex, current - 1);
535 
536             ComputeExtentAndViewport(availableSize, visibleSections);
537 
538             return availableSize;
539         }
540         protected override Size ArrangeOverride(Size finalSize)
541         {
542             if (_children != null)
543             {
544                 foreach (UIElement child in _children)
545                 {
546                     var layoutInfo = _realizedChildLayout[child];
547                     child.Arrange(layoutInfo);
548                 }
549             }
550             return finalSize;
551         }
552 
553         #endregion
554 
555         #region IScrollInfo Members
556 
557         private bool _canHScroll = false;
558         public bool CanHorizontallyScroll
559         {
560             get { return _canHScroll; }
561             set { _canHScroll = value; }
562         }
563 
564         private bool _canVScroll = false;
565         public bool CanVerticallyScroll
566         {
567             get { return _canVScroll; }
568             set { _canVScroll = value; }
569         }
570 
571         public double ExtentHeight
572         {
573             get { return _extent.Height; }
574         }
575 
576         public double ExtentWidth
577         {
578             get { return _extent.Width; }
579         }
580 
581         public double HorizontalOffset
582         {
583             get { return _offset.X; }
584         }
585 
586         public double VerticalOffset
587         {
588             get { return _offset.Y; }
589         }
590 
591         public void LineDown()
592         {
593             if (Orientation == Orientation.Vertical)
594                 SetVerticalOffset(VerticalOffset + 20);
595             else
596                 SetVerticalOffset(VerticalOffset + 1);
597         }
598 
599         public void LineLeft()
600         {
601             if (Orientation == Orientation.Horizontal)
602                 SetHorizontalOffset(HorizontalOffset - 20);
603             else
604                 SetHorizontalOffset(HorizontalOffset - 1);
605         }
606 
607         public void LineRight()
608         {
609             if (Orientation == Orientation.Horizontal)
610                 SetHorizontalOffset(HorizontalOffset + 20);
611             else
612                 SetHorizontalOffset(HorizontalOffset + 1);
613         }
614 
615         public void LineUp()
616         {
617             if (Orientation == Orientation.Vertical)
618                 SetVerticalOffset(VerticalOffset - 20);
619             else
620                 SetVerticalOffset(VerticalOffset - 1);
621         }
622 
623         public Rect MakeVisible(Visual visual, Rect rectangle)
624         {
625             var gen = (ItemContainerGenerator)_generator.GetItemContainerGeneratorForPanel(this);
626             var element = (UIElement)visual;
627             int itemIndex = gen.IndexFromContainer(element);
628             while (itemIndex == -1)
629             {
630                 element = (UIElement)VisualTreeHelper.GetParent(element);
631                 itemIndex = gen.IndexFromContainer(element);
632             }
633             int section = _abstractPanel[itemIndex].Section;
634             Rect elementRect = _realizedChildLayout[element];
635             if (Orientation == Orientation.Horizontal)
636             {
637                 double viewportHeight = _pixelMeasuredViewport.Height;
638                 if (elementRect.Bottom > viewportHeight)
639                     _offset.Y += 1;
640                 else if (elementRect.Top < 0)
641                     _offset.Y -= 1;
642             }
643             else
644             {
645                 double viewportWidth = _pixelMeasuredViewport.Width;
646                 if (elementRect.Right > viewportWidth)
647                     _offset.X += 1;
648                 else if (elementRect.Left < 0)
649                     _offset.X -= 1;
650             }
651             InvalidateMeasure();
652             return elementRect;
653         }
654 
655         public void MouseWheelDown()
656         {
657             PageDown();
658         }
659 
660         public void MouseWheelLeft()
661         {
662             PageLeft();
663         }
664 
665         public void MouseWheelRight()
666         {
667             PageRight();
668         }
669 
670         public void MouseWheelUp()
671         {
672             PageUp();
673         }
674 
675         public void PageDown()
676         {
677             SetVerticalOffset(VerticalOffset + _viewport.Height * 0.8);
678         }
679 
680         public void PageLeft()
681         {
682             SetHorizontalOffset(HorizontalOffset - _viewport.Width * 0.8);
683         }
684 
685         public void PageRight()
686         {
687             SetHorizontalOffset(HorizontalOffset + _viewport.Width * 0.8);
688         }
689 
690         public void PageUp()
691         {
692             SetVerticalOffset(VerticalOffset - _viewport.Height * 0.8);
693         }
694 
695         private ScrollViewer _owner;
696         public ScrollViewer ScrollOwner
697         {
698             get { return _owner; }
699             set { _owner = value; }
700         }
701 
702         public void SetHorizontalOffset(double offset)
703         {
704             if (offset < 0 || _viewport.Width >= _extent.Width)
705             {
706                 offset = 0;
707             }
708             else
709             {
710                 if (offset + _viewport.Width >= _extent.Width)
711                 {
712                     offset = _extent.Width - _viewport.Width;
713                 }
714             }
715 
716             _offset.X = offset;
717 
718             if (_owner != null)
719                 _owner.InvalidateScrollInfo();
720 
721             InvalidateMeasure();
722             firstIndex = GetFirstVisibleIndex();
723         }
724 
725         public void SetVerticalOffset(double offset)
726         {
727             if (offset < 0 || _viewport.Height >= _extent.Height)
728             {
729                 offset = 0;
730             }
731             else
732             {
733                 if (offset + _viewport.Height >= _extent.Height)
734                 {
735                     offset = _extent.Height - _viewport.Height;
736                 }
737             }
738 
739             _offset.Y = offset;
740 
741             if (_owner != null)
742                 _owner.InvalidateScrollInfo();
743 
744             //_trans.Y = -offset;
745 
746             InvalidateMeasure();
747             firstIndex = GetFirstVisibleIndex();
748         }
749 
750         public double ViewportHeight
751         {
752             get { return _viewport.Height; }
753         }
754 
755         public double ViewportWidth
756         {
757             get { return _viewport.Width; }
758         }
759 
760         #endregion
761 
762         #region helper data structures
763 
764         class ItemAbstraction
765         {
766             public ItemAbstraction(WrapPanelAbstraction panel, int index)
767             {
768                 _panel = panel;
769                 _index = index;
770             }
771 
772             WrapPanelAbstraction _panel;
773 
774             public readonly int _index;
775 
776             int _sectionIndex = -1;
777             public int SectionIndex
778             {
779                 get
780                 {
781                     if (_sectionIndex == -1)
782                     {
783                         return _index % _panel._averageItemsPerSection - 1;
784                     }
785                     return _sectionIndex;
786                 }
787                 set
788                 {
789                     if (_sectionIndex == -1)
790                         _sectionIndex = value;
791                 }
792             }
793 
794             int _section = -1;
795             public int Section
796             {
797                 get
798                 {
799                     if (_section == -1)
800                     {
801                         return _index / _panel._averageItemsPerSection;
802                     }
803                     return _section;
804                 }
805                 set
806                 {
807                     if (_section == -1)
808                         _section = value;
809                 }
810             }
811         }
812 
813         class WrapPanelAbstraction : IEnumerable<ItemAbstraction>
814         {
815             public WrapPanelAbstraction(int itemCount)
816             {
817                 List<ItemAbstraction> items = new List<ItemAbstraction>(itemCount);
818                 for (int i = 0; i < itemCount; i++)
819                 {
820                     ItemAbstraction item = new ItemAbstraction(this, i);
821                     items.Add(item);
822                 }
823 
824                 Items = new ReadOnlyCollection<ItemAbstraction>(items);
825                 _averageItemsPerSection = itemCount;
826                 _itemCount = itemCount;
827             }
828 
829             public readonly int _itemCount;
830             public int _averageItemsPerSection;
831             private int _currentSetSection = -1;
832             private int _currentSetItemIndex = -1;
833             private int _itemsInCurrentSecction = 0;
834             private object _syncRoot = new object();
835 
836             public int SectionCount
837             {
838                 get
839                 {
840                     int ret = _currentSetSection + 1;
841                     if (_currentSetItemIndex + 1 < Items.Count)
842                     {
843                         int itemsLeft = Items.Count - _currentSetItemIndex;
844                         ret += itemsLeft / _averageItemsPerSection + 1;
845                     }
846                     return ret;
847                 }
848             }
849 
850             private ReadOnlyCollection<ItemAbstraction> Items { get; set; }
851 
852             public void SetItemSection(int index, int section)
853             {
854                 lock (_syncRoot)
855                 {
856                     if (section <= _currentSetSection + 1 && index == _currentSetItemIndex + 1)
857                     {
858                         _currentSetItemIndex++;
859                         Items[index].Section = section;
860                         if (section == _currentSetSection + 1)
861                         {
862                             _currentSetSection = section;
863                             if (section > 0)
864                             {
865                                 _averageItemsPerSection = (index) / (section);
866                             }
867                             _itemsInCurrentSecction = 1;
868                         }
869                         else
870                             _itemsInCurrentSecction++;
871                         Items[index].SectionIndex = _itemsInCurrentSecction - 1;
872                     }
873                 }
874             }
875 
876             public ItemAbstraction this[int index]
877             {
878                 get { return Items[index]; }
879             }
880 
881             #region IEnumerable<ItemAbstraction> Members
882 
883             public IEnumerator<ItemAbstraction> GetEnumerator()
884             {
885                 return Items.GetEnumerator();
886             }
887 
888             #endregion
889 
890             #region IEnumerable Members
891 
892             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
893             {
894                 return GetEnumerator();
895             }
896 
897             #endregion
898         }
899 
900         #endregion
901     }
902 }
原文地址:https://www.cnblogs.com/xuling-297769461/p/12768982.html