1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Windows.Input;
21 using System.ComponentModel;
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding;
25 namespace Tizen.NUI.Components
28 /// This class provides a View that can layouting items in list and grid with high performance.
30 [EditorBrowsable(EditorBrowsableState.Never)]
31 public class CollectionView : RecyclerView
34 /// Binding Property of selected item in single selection.
36 [EditorBrowsable(EditorBrowsableState.Never)]
37 public static readonly BindableProperty SelectedItemProperty =
38 BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(CollectionView), null,
39 propertyChanged: (bindable, oldValue, newValue) =>
41 var colView = (CollectionView)bindable;
42 oldValue = colView.selectedItem;
43 colView.selectedItem = newValue;
44 var args = new SelectionChangedEventArgs(oldValue, newValue);
46 foreach (RecyclerViewItem item in colView.ContentContainer.Children.Where((item) => item is RecyclerViewItem))
48 if (item.BindingContext == null) continue;
49 if (item.BindingContext == oldValue) item.IsSelected = false;
50 else if (item.BindingContext == newValue) item.IsSelected = true;
53 SelectionPropertyChanged(colView, args);
55 defaultValueCreator: (bindable) =>
57 var colView = (CollectionView)bindable;
58 return colView.selectedItem;
62 /// Binding Property of selected items list in multiple selection.
64 [EditorBrowsable(EditorBrowsableState.Never)]
65 public static readonly BindableProperty SelectedItemsProperty =
66 BindableProperty.Create(nameof(SelectedItems), typeof(IList<object>), typeof(CollectionView), null,
67 propertyChanged: (bindable, oldValue, newValue) =>
69 var colView = (CollectionView)bindable;
70 var oldSelection = colView.selectedItems ?? selectEmpty;
71 //FIXME : CoerceSelectedItems calls only isCreatedByXaml
72 var newSelection = (SelectionList)CoerceSelectedItems(colView, newValue);
73 colView.selectedItems = newSelection;
74 colView.SelectedItemsPropertyChanged(oldSelection, newSelection);
76 defaultValueCreator: (bindable) =>
78 var colView = (CollectionView)bindable;
79 colView.selectedItems = colView.selectedItems ?? new SelectionList(colView);
80 return colView.selectedItems;
84 /// Binding Property of selected items list in multiple selection.
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 public static readonly BindableProperty SelectionModeProperty =
88 BindableProperty.Create(nameof(SelectionMode), typeof(ItemSelectionMode), typeof(CollectionView), ItemSelectionMode.None,
89 propertyChanged: (bindable, oldValue, newValue) =>
91 var colView = (CollectionView)bindable;
92 oldValue = colView.selectionMode;
93 colView.selectionMode = (ItemSelectionMode)newValue;
94 SelectionModePropertyChanged(colView, oldValue, newValue);
96 defaultValueCreator: (bindable) =>
98 var colView = (CollectionView)bindable;
99 return colView.selectionMode;
103 private static readonly IList<object> selectEmpty = new List<object>(0);
104 private DataTemplate itemTemplate = null;
105 private IEnumerable itemsSource = null;
106 private ItemsLayouter itemsLayouter = null;
107 private DataTemplate groupHeaderTemplate;
108 private DataTemplate groupFooterTemplate;
109 private bool isGrouped;
110 private bool wasRelayouted = false;
111 private bool needInitalizeLayouter = false;
112 private object selectedItem;
113 private SelectionList selectedItems;
114 private bool suppressSelectionChangeNotification;
115 private ItemSelectionMode selectionMode = ItemSelectionMode.None;
116 private RecyclerViewItem header;
117 private RecyclerViewItem footer;
118 private View focusedView;
119 private int prevFocusedDataIndex = 0;
120 private List<RecyclerViewItem> recycleGroupHeaderCache { get; } = new List<RecyclerViewItem>();
121 private List<RecyclerViewItem> recycleGroupFooterCache { get; } = new List<RecyclerViewItem>();
124 /// Base constructor.
126 [EditorBrowsable(EditorBrowsableState.Never)]
127 public CollectionView() : base()
130 SetKeyboardNavigationSupport(true);
134 /// Base constructor with ItemsSource
136 /// <param name="itemsSource">item's data source</param>
137 [EditorBrowsable(EditorBrowsableState.Never)]
138 public CollectionView(IEnumerable itemsSource) : this()
140 ItemsSource = itemsSource;
144 /// Base constructor with ItemsSource, ItemsLayouter and ItemTemplate
146 /// <param name="itemsSource">item's data source</param>
147 /// <param name="layouter">item's layout manager</param>
148 /// <param name="template">item's view template with data bindings</param>
149 [EditorBrowsable(EditorBrowsableState.Never)]
150 public CollectionView(IEnumerable itemsSource, ItemsLayouter layouter, DataTemplate template) : this()
152 ItemsSource = itemsSource;
153 ItemTemplate = template;
154 ItemsLayouter = layouter;
158 /// Event of Selection changed.
159 /// old selection list and new selection will be provided.
161 [EditorBrowsable(EditorBrowsableState.Never)]
162 public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
165 /// Align item in the viewport when ScrollTo() calls.
167 [EditorBrowsable(EditorBrowsableState.Never)]
168 public enum ItemScrollTo
171 /// Scroll to show item in nearest viewport on scroll direction.
172 /// item is above the scroll viewport, item will be came into front,
173 /// item is under the scroll viewport, item will be came into end,
174 /// item is in the scroll viewport, no scroll.
178 /// Scroll to show item in start of the viewport.
182 /// Scroll to show item in center of the viewport.
186 /// Scroll to show item in end of the viewport.
192 /// Item's source data.
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public override IEnumerable ItemsSource
207 if (InternalItemSource != null) InternalItemSource.Dispose();
211 if (InternalItemSource != null) InternalItemSource.Dispose();
212 InternalItemSource = ItemsSourceFactory.Create(this);
214 if (itemsLayouter == null) return;
216 needInitalizeLayouter = true;
222 /// DataTemplate for items.
224 [EditorBrowsable(EditorBrowsableState.Never)]
225 public override DataTemplate ItemTemplate
233 itemTemplate = value;
240 needInitalizeLayouter = true;
248 [EditorBrowsable(EditorBrowsableState.Never)]
249 public virtual ItemsLayouter ItemsLayouter
253 return itemsLayouter;
257 itemsLayouter = value;
258 base.InternalItemsLayouter = ItemsLayouter;
261 needInitalizeLayouter = false;
265 needInitalizeLayouter = true;
267 var styleName = "Tizen.NUI.Components." + (itemsLayouter is LinearLayouter? "LinearLayouter" : (itemsLayouter is GridLayouter ? "GridLayouter" : "ItemsLayouter"));
268 using (ViewStyle layouterStyle = ThemeManager.GetStyle(styleName))
270 if (layouterStyle != null)
272 itemsLayouter.Padding = new Extents(layouterStyle.Padding);
280 /// Scrolling direction to display items layout.
282 [EditorBrowsable(EditorBrowsableState.Never)]
283 public new Direction ScrollingDirection
287 return base.ScrollingDirection;
291 base.ScrollingDirection = value;
293 if (ScrollingDirection == Direction.Horizontal)
295 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
299 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
305 /// Selected item in single selection.
307 [EditorBrowsable(EditorBrowsableState.Never)]
308 public object SelectedItem
310 get => GetValue(SelectedItemProperty);
311 set => SetValue(SelectedItemProperty, value);
315 /// Selected items list in multiple selection.
317 [EditorBrowsable(EditorBrowsableState.Never)]
318 public IList<object> SelectedItems
320 get => (IList<object>)GetValue(SelectedItemsProperty);
321 // set => SetValue(SelectedItemsProperty, new SelectionList(this, value));
325 /// Selection mode to handle items selection. See ItemSelectionMode for details.
327 [EditorBrowsable(EditorBrowsableState.Never)]
328 public ItemSelectionMode SelectionMode
330 get => (ItemSelectionMode)GetValue(SelectionModeProperty);
331 set => SetValue(SelectionModeProperty, value);
335 /// Command of selection changed.
337 [EditorBrowsable(EditorBrowsableState.Never)]
338 public ICommand SelectionChangedCommand { set; get; }
341 /// Command parameter of selection changed.
343 [EditorBrowsable(EditorBrowsableState.Never)]
344 public object SelectionChangedCommandParameter { set; get; }
347 /// Size strategy of measuring scroll content. see details in ItemSizingStrategy.
349 [EditorBrowsable(EditorBrowsableState.Never)]
350 public ItemSizingStrategy SizingStrategy { get; set; }
353 /// Header item which placed in top-most position.
354 /// note : internal index and count will be increased.
356 [EditorBrowsable(EditorBrowsableState.Never)]
357 public RecyclerViewItem Header
364 //ContentContainer.Remove(header);
365 Utility.Dispose(header);
370 value.ParentItemsView = this;
371 value.IsHeader = true;
372 ContentContainer.Add(value);
375 needInitalizeLayouter = true;
381 /// Footer item which placed in bottom-most position.
382 /// note : internal count will be increased.
384 [EditorBrowsable(EditorBrowsableState.Never)]
385 public RecyclerViewItem Footer
392 //ContentContainer.Remove(footer);
393 Utility.Dispose(footer);
397 value.Index = InternalItemSource?.Count ?? 0;
398 value.ParentItemsView = this;
399 value.IsFooter = true;
400 ContentContainer.Add(value);
403 needInitalizeLayouter = true;
409 /// Boolean flag of group feature existence.
411 [EditorBrowsable(EditorBrowsableState.Never)]
412 public bool IsGrouped
418 needInitalizeLayouter = true;
419 //Need to re-intialize Internal Item Source.
420 if (InternalItemSource != null)
422 InternalItemSource.Dispose();
423 InternalItemSource = null;
425 if (ItemsSource != null)
426 InternalItemSource = ItemsSourceFactory.Create(this);
432 /// DataTemplate of group header. Group feature is not supported yet.
434 [EditorBrowsable(EditorBrowsableState.Never)]
435 public DataTemplate GroupHeaderTemplate
439 return groupHeaderTemplate;
443 groupHeaderTemplate = value;
444 needInitalizeLayouter = true;
450 /// DataTemplate of group footer. Group feature is not supported yet.
452 [EditorBrowsable(EditorBrowsableState.Never)]
453 public DataTemplate GroupFooterTemplate
457 return groupFooterTemplate;
461 groupFooterTemplate = value;
462 needInitalizeLayouter = true;
469 /// Internal encapsulated items data source.
471 internal new IGroupableItemSource InternalItemSource
475 return (base.InternalItemSource as IGroupableItemSource);
479 base.InternalItemSource = value;
483 [EditorBrowsable(EditorBrowsableState.Never)]
484 public override void OnRelayout(Vector2 size, RelayoutContainer container)
486 base.OnRelayout(size, container);
488 wasRelayouted = true;
489 if (needInitalizeLayouter) Init();
493 [EditorBrowsable(EditorBrowsableState.Never)]
494 public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
496 View nextFocusedView = null;
498 if (focusedView == null)
500 // If focusedView is null, find child which has previous data index
501 if (ContentContainer.Children.Count > 0 && InternalItemSource.Count > 0)
503 for (int i = 0; i < ContentContainer.Children.Count; i++)
505 RecyclerViewItem item = Children[i] as RecyclerViewItem;
506 if (item?.Index == prevFocusedDataIndex)
508 nextFocusedView = item;
516 // If this is not first focus, request next focus to Layouter
517 nextFocusedView = ItemsLayouter.RequestNextFocusableView(currentFocusedView, direction, loopEnabled);
520 if (nextFocusedView != null)
522 // Check next focused view is inside of visible area.
523 // If it is not, move scroll position to make it visible.
524 Position scrollPosition = ContentContainer.CurrentPosition;
525 float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
527 float left = nextFocusedView.Position.X;
528 float right = nextFocusedView.Position.X + nextFocusedView.Size.Width;
529 float top = nextFocusedView.Position.Y;
530 float bottom = nextFocusedView.Position.Y + nextFocusedView.Size.Height;
532 float visibleRectangleLeft = -scrollPosition.X;
533 float visibleRectangleRight = -scrollPosition.X + Size.Width;
534 float visibleRectangleTop = -scrollPosition.Y;
535 float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
537 if (ScrollingDirection == Direction.Horizontal)
539 if ((direction == View.FocusDirection.Left || direction == View.FocusDirection.Up) && left < visibleRectangleLeft)
541 targetPosition = left;
543 else if ((direction == View.FocusDirection.Right || direction == View.FocusDirection.Down) && right > visibleRectangleRight)
545 targetPosition = right - Size.Width;
550 if ((direction == View.FocusDirection.Up || direction == View.FocusDirection.Left) && top < visibleRectangleTop)
552 targetPosition = top;
554 else if ((direction == View.FocusDirection.Down || direction == View.FocusDirection.Right) && bottom > visibleRectangleBottom)
556 targetPosition = bottom - Size.Height;
560 focusedView = nextFocusedView;
561 prevFocusedDataIndex = (nextFocusedView as RecyclerViewItem)?.Index ?? -1;
563 ScrollTo(targetPosition, true);
567 // If nextView is null, it means that we should move focus to outside of Control.
568 // Return FocusableView depending on direction.
571 case View.FocusDirection.Left:
573 nextFocusedView = LeftFocusableView;
576 case View.FocusDirection.Right:
578 nextFocusedView = RightFocusableView;
581 case View.FocusDirection.Up:
583 nextFocusedView = UpFocusableView;
586 case View.FocusDirection.Down:
588 nextFocusedView = DownFocusableView;
593 if (nextFocusedView != null)
599 //If FocusableView doesn't exist, not move focus.
600 nextFocusedView = focusedView;
604 return nextFocusedView;
608 /// Update selected items list in multiple selection.
610 /// <param name="newSelection">updated selection list by user</param>
611 [EditorBrowsable(EditorBrowsableState.Never)]
612 public void UpdateSelectedItems(IList<object> newSelection)
614 var oldSelection = new List<object>(SelectedItems);
616 suppressSelectionChangeNotification = true;
618 SelectedItems.Clear();
620 if (newSelection?.Count > 0)
622 for (int n = 0; n < newSelection.Count; n++)
624 SelectedItems.Add(newSelection[n]);
628 suppressSelectionChangeNotification = false;
630 SelectedItemsPropertyChanged(oldSelection, newSelection);
634 /// Scroll to specific position with or without animation.
636 /// <param name="position">Destination.</param>
637 /// <param name="animate">Scroll with or without animation</param>
638 [EditorBrowsable(EditorBrowsableState.Never)]
639 public new void ScrollTo(float position, bool animate) => base.ScrollTo(position, animate);
642 /// Scroll to specific item's aligned position with or without animation.
644 /// <param name="index">Target item index of dataset.</param>
645 /// <param name="animate">Boolean flag of animation.</param>
646 /// <param name="align">Align state of item. see details in ItemScrollTo.</param>
647 [EditorBrowsable(EditorBrowsableState.Never)]
648 public virtual void ScrollTo(int index, bool animate = false, ItemScrollTo align = ItemScrollTo.Nearest)
650 if (ItemsLayouter == null) throw new Exception("Item Layouter must exist.");
652 float scrollPos, curPos, curSize, curItemSize;
653 (float x, float y) = ItemsLayouter.GetItemPosition(index);
654 (float width, float height) = ItemsLayouter.GetItemSize(index);
655 if (ScrollingDirection == Direction.Horizontal)
658 curPos = ScrollPosition.X;
659 curSize = Size.Width;
665 curPos = ScrollPosition.Y;
666 curSize = Size.Height;
667 curItemSize = height;
670 //Console.WriteLine("[NUI] ScrollTo [{0}:{1}], curPos{2}, itemPos{3}, curSize{4}, itemSize{5}", InternalItemSource.GetPosition(item), align, curPos, scrollPos, curSize, curItemSize);
673 case ItemScrollTo.Start:
676 case ItemScrollTo.Center:
677 scrollPos = scrollPos - (curSize / 2) + (curItemSize / 2);
679 case ItemScrollTo.End:
680 scrollPos = scrollPos - curSize + curItemSize;
682 case ItemScrollTo.Nearest:
683 if (scrollPos < curPos - curItemSize)
685 // item is placed before the current screen. scrollTo.Top
687 else if (scrollPos >= curPos + curSize + curItemSize)
689 // item is placed after the current screen. scrollTo.End
690 scrollPos = scrollPos - curSize + curItemSize;
694 // item is in the scroller. ScrollTo() is ignored.
700 //Console.WriteLine("[NUI] ScrollTo [{0}]-------------------", scrollPos);
701 base.ScrollTo(scrollPos, animate);
705 /// Apply style to CollectionView
707 /// <param name="viewStyle">The style to apply.</param>
708 [EditorBrowsable(EditorBrowsableState.Never)]
709 public override void ApplyStyle(ViewStyle viewStyle)
711 base.ApplyStyle(viewStyle);
712 if (viewStyle != null)
714 //Extension = RecyclerViewItemStyle.CreateExtension();
716 if (itemsLayouter != null)
718 string styleName = "Tizen.NUI.Compoenents." + (itemsLayouter is LinearLayouter? "LinearLayouter" : (itemsLayouter is GridLayouter ? "GridLayouter" : "ItemsLayouter"));
719 using (ViewStyle layouterStyle = ThemeManager.GetStyle(styleName))
721 itemsLayouter.Padding = new Extents(layouterStyle.Padding);
726 // Realize and Decorate the item.
727 internal override RecyclerViewItem RealizeItem(int index)
729 if (index == 0 && Header != null)
735 if (index == InternalItemSource.Count - 1 && Footer != null)
743 var context = InternalItemSource.GetItem(index);
744 if (InternalItemSource.IsGroupHeader(index))
746 DataTemplate templ = (groupHeaderTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupHeaderTemplate;
748 RecyclerViewItem groupHeader = PopRecycleGroupCache(templ, true);
749 if (groupHeader == null)
751 groupHeader = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupHeaderTemplate, context, this);
753 groupHeader.ParentItemsView = this;
754 groupHeader.Template = templ;
755 groupHeader.isGroupHeader = true;
756 groupHeader.isGroupFooter = false;
757 ContentContainer.Add(groupHeader);
759 groupHeader.Index = index;
760 groupHeader.ParentGroup = context;
761 groupHeader.BindingContext = context;
765 else if (InternalItemSource.IsGroupFooter(index))
767 DataTemplate templ = (groupFooterTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupFooterTemplate;
769 RecyclerViewItem groupFooter = PopRecycleGroupCache(templ, false);
770 if (groupFooter == null)
772 groupFooter = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupFooterTemplate, context, this);
774 groupFooter.ParentItemsView = this;
775 groupFooter.Template = templ;
776 groupFooter.isGroupHeader = false;
777 groupFooter.isGroupFooter = true;
778 ContentContainer.Add(groupFooter);
780 groupFooter.Index = index;
781 groupFooter.ParentGroup = context;
782 groupFooter.BindingContext = context;
789 RecyclerViewItem item = base.RealizeItem(index);
794 item.ParentGroup = InternalItemSource.GetGroupParent(index);
797 switch (SelectionMode)
799 case ItemSelectionMode.SingleSelection:
800 if (item.BindingContext != null && item.BindingContext == SelectedItem)
802 item.IsSelected = true;
806 case ItemSelectionMode.MultipleSelections:
807 if ((item.BindingContext != null) && (SelectedItems?.Contains(item.BindingContext) ?? false))
809 item.IsSelected = true;
812 case ItemSelectionMode.None:
813 item.IsSelectable = false;
820 // Unrealize and caching the item.
821 internal override void UnrealizeItem(RecyclerViewItem item, bool recycle = true)
833 if (item.isGroupHeader || item.isGroupFooter)
835 if (!recycle || !PushRecycleGroupCache(item))
836 Utility.Dispose(item);
840 item.IsSelected = false;
841 base.UnrealizeItem(item, recycle);
844 internal void SelectedItemsPropertyChanged(IList<object> oldSelection, IList<object> newSelection)
846 if (suppressSelectionChangeNotification)
851 foreach (RecyclerViewItem item in ContentContainer.Children.Where((item) => item is RecyclerViewItem))
853 if (item.BindingContext == null) continue;
854 if (newSelection.Contains(item.BindingContext))
856 if (!item.IsSelected) item.IsSelected = true;
860 if (item.IsSelected) item.IsSelected = false;
863 SelectionPropertyChanged(this, new SelectionChangedEventArgs(oldSelection, newSelection));
865 OnPropertyChanged(SelectedItemsProperty.PropertyName);
869 /// Internal selection callback.
871 [EditorBrowsable(EditorBrowsableState.Never)]
872 protected virtual void OnSelectionChanged(SelectionChangedEventArgs args)
878 /// Adjust scrolling position by own scrolling rules.
879 /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
881 /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
882 /// <returns>Adjusted scroll destination</returns>
883 [EditorBrowsable(EditorBrowsableState.Never)]
884 protected override float AdjustTargetPositionOfScrollAnimation(float position)
886 // Destination is depending on implementation of layout manager.
887 // Get destination from layout manager.
888 return ItemsLayouter.CalculateCandidateScrollPosition(position);
892 /// OnScroll event callback.
894 /// <param name="source">Scroll source object</param>
895 /// <param name="args">Scroll event argument</param>
896 [EditorBrowsable(EditorBrowsableState.Never)]
897 protected override void OnScrolling(object source, ScrollEventArgs args)
899 if (disposed) return;
901 if (needInitalizeLayouter)
903 ItemsLayouter.Initialize(this);
904 needInitalizeLayouter = false;
906 base.OnScrolling(source, args);
910 /// Dispose ItemsView and all children on it.
912 /// <param name="type">Dispose type.</param>
913 protected override void Dispose(DisposeTypes type)
920 if (type == DisposeTypes.Explicit)
923 if (InternalItemSource != null)
925 InternalItemSource.Dispose();
926 InternalItemSource = null;
930 Utility.Dispose(Header);
935 Utility.Dispose(Footer);
938 groupHeaderTemplate = null;
939 groupFooterTemplate = null;
946 private static void SelectionPropertyChanged(CollectionView colView, SelectionChangedEventArgs args)
948 var command = colView.SelectionChangedCommand;
952 var commandParameter = colView.SelectionChangedCommandParameter;
954 if (command.CanExecute(commandParameter))
956 command.Execute(commandParameter);
959 colView.SelectionChanged?.Invoke(colView, args);
960 colView.OnSelectionChanged(args);
963 private static object CoerceSelectedItems(BindableObject bindable, object value)
967 return new SelectionList((CollectionView)bindable);
970 if (value is SelectionList)
975 return new SelectionList((CollectionView)bindable, value as IList<object>);
978 private static void SelectionModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
980 var colView = (CollectionView)bindable;
982 var oldMode = (ItemSelectionMode)oldValue;
983 var newMode = (ItemSelectionMode)newValue;
985 IList<object> previousSelection = new List<object>();
986 IList<object> newSelection = new List<object>();
990 case ItemSelectionMode.None:
992 case ItemSelectionMode.SingleSelection:
993 if (colView.SelectedItem != null)
995 previousSelection.Add(colView.SelectedItem);
998 case ItemSelectionMode.MultipleSelections:
999 previousSelection = colView.SelectedItems;
1005 case ItemSelectionMode.None:
1007 case ItemSelectionMode.SingleSelection:
1008 if (colView.SelectedItem != null)
1010 newSelection.Add(colView.SelectedItem);
1013 case ItemSelectionMode.MultipleSelections:
1014 newSelection = colView.SelectedItems;
1018 if (previousSelection.Count == newSelection.Count)
1020 if (previousSelection.Count == 0 || (previousSelection[0] == newSelection[0]))
1022 // Both selections are empty or have the same single item; no reason to signal a change
1027 var args = new SelectionChangedEventArgs(previousSelection, newSelection);
1028 SelectionPropertyChanged(colView, args);
1033 if (ItemsSource == null) return;
1034 if (ItemsLayouter == null) return;
1035 if (ItemTemplate == null) return;
1037 if (disposed) return;
1038 if (needInitalizeLayouter)
1040 if (InternalItemSource == null) return;
1042 InternalItemSource.HasHeader = (header != null);
1043 InternalItemSource.HasFooter = (footer != null);
1046 if (!wasRelayouted) return;
1048 if (needInitalizeLayouter)
1050 ItemsLayouter.Initialize(this);
1051 needInitalizeLayouter = false;
1053 ItemsLayouter.RequestLayout(0.0f, true);
1055 if (ScrollingDirection == Direction.Horizontal)
1057 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
1061 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
1065 private bool PushRecycleGroupCache(RecyclerViewItem item)
1067 if (item == null) throw new ArgumentNullException(nameof(item));
1068 if (RecycleCache.Count >= 20) return false;
1069 if (item.Template == null) return false;
1070 if (item.isGroupHeader)
1072 recycleGroupHeaderCache.Add(item);
1074 else if (item.isGroupFooter)
1076 recycleGroupFooterCache.Add(item);
1084 private RecyclerViewItem PopRecycleGroupCache(DataTemplate Template, bool isHeader)
1086 RecyclerViewItem viewItem = null;
1088 var Cache = (isHeader ? recycleGroupHeaderCache : recycleGroupFooterCache);
1089 for (int i = 0; i < Cache.Count; i++)
1091 viewItem = Cache[i];
1092 if (Template == viewItem.Template) break;
1095 if (viewItem != null)
1097 Cache.Remove(viewItem);