2fdbc35f6f8ebe720b496a437969a241719363ed
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / RecyclerView / CollectionView.cs
1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  *
15  */
16 using System;
17 using System.Linq;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Collections.Specialized;
21 using System.Windows.Input;
22 using System.ComponentModel;
23 using Tizen.NUI.BaseComponents;
24 using Tizen.NUI.Binding;
25
26 namespace Tizen.NUI.Components
27 {
28     /// <summary>
29     /// This class provides a View that can layouting items in list and grid with high performance.
30     /// </summary>
31     [EditorBrowsable(EditorBrowsableState.Never)]
32     public class CollectionView : RecyclerView
33     {
34         /// <summary>
35         /// Binding Property of selected item in single selection.
36         /// </summary>
37         [EditorBrowsable(EditorBrowsableState.Never)]
38         public static readonly BindableProperty SelectedItemProperty =
39             BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(CollectionView), null,
40                 propertyChanged: (bindable, oldValue, newValue) =>
41                 {
42                     var colView = (CollectionView)bindable;
43                     oldValue = colView.selectedItem;
44                     colView.selectedItem = newValue;
45                     var args = new SelectionChangedEventArgs(oldValue, newValue);
46
47                     foreach (RecyclerViewItem item in colView.ContentContainer.Children.Where((item) => item is RecyclerViewItem))
48                     {
49                         if (item.BindingContext == null) continue;
50                         if (item.BindingContext == oldValue) item.IsSelected = false;
51                         else if (item.BindingContext == newValue) item.IsSelected = true;
52                     }
53
54                     SelectionPropertyChanged(colView, args);
55                 },
56                 defaultValueCreator: (bindable) =>
57                 {
58                     var colView = (CollectionView)bindable;
59                     return colView.selectedItem;
60                 });
61
62         /// <summary>
63         /// Binding Property of selected items list in multiple selection.
64         /// </summary>
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         public static readonly BindableProperty SelectedItemsProperty =
67             BindableProperty.Create(nameof(SelectedItems), typeof(IList<object>), typeof(CollectionView), null,
68                 propertyChanged: (bindable, oldValue, newValue) =>
69                 {
70                     var colView = (CollectionView)bindable;
71                     var oldSelection = colView.selectedItems ?? selectEmpty;
72                     //FIXME : CoerceSelectedItems calls only isCreatedByXaml
73                     var newSelection = (SelectionList)CoerceSelectedItems(colView, newValue);
74                     colView.selectedItems = newSelection;
75                     colView.SelectedItemsPropertyChanged(oldSelection, newSelection);
76                 },
77                 defaultValueCreator: (bindable) =>
78                 {
79                     var colView = (CollectionView)bindable;
80                     colView.selectedItems = colView.selectedItems ?? new SelectionList(colView);
81                     return colView.selectedItems;
82                 });
83
84         /// <summary>
85         /// Binding Property of selected items list in multiple selection.
86         /// </summary>
87         [EditorBrowsable(EditorBrowsableState.Never)]
88         public static readonly BindableProperty SelectionModeProperty =
89             BindableProperty.Create(nameof(SelectionMode), typeof(ItemSelectionMode), typeof(CollectionView), ItemSelectionMode.None,
90                 propertyChanged: (bindable, oldValue, newValue) =>
91                 {
92                     var colView = (CollectionView)bindable;
93                     oldValue = colView.selectionMode;
94                     colView.selectionMode = (ItemSelectionMode)newValue;
95                     SelectionModePropertyChanged(colView, oldValue, newValue);
96                 },
97                 defaultValueCreator: (bindable) =>
98                 {
99                     var colView = (CollectionView)bindable;
100                     return colView.selectionMode;
101                 });
102
103
104         private static readonly IList<object> selectEmpty = new List<object>(0);
105         private DataTemplate itemTemplate = null;
106         private IEnumerable itemsSource = null;
107         private ItemsLayouter itemsLayouter = null;
108         private DataTemplate groupHeaderTemplate;
109         private DataTemplate groupFooterTemplate;
110         private bool isGrouped;
111         private bool wasRelayouted = false;
112         private bool needInitalizeLayouter = false;
113         private object selectedItem;
114         private SelectionList selectedItems;
115         private bool suppressSelectionChangeNotification;
116         private ItemSelectionMode selectionMode = ItemSelectionMode.None;
117         private RecyclerViewItem header;
118         private RecyclerViewItem footer;
119         private View focusedView;
120         private int prevFocusedDataIndex = 0;
121         private List<RecyclerViewItem> recycleGroupHeaderCache { get; } = new List<RecyclerViewItem>();
122         private List<RecyclerViewItem> recycleGroupFooterCache { get; } = new List<RecyclerViewItem>();
123
124         /// <summary>
125         /// Base constructor.
126         /// </summary>
127         [EditorBrowsable(EditorBrowsableState.Never)]
128         public CollectionView() : base()
129         {
130             FocusGroup = true;
131             SetKeyboardNavigationSupport(true);
132         }
133
134         /// <summary>
135         /// Base constructor with ItemsSource
136         /// </summary>
137         /// <param name="itemsSource">item's data source</param>
138         [EditorBrowsable(EditorBrowsableState.Never)]
139         public CollectionView(IEnumerable itemsSource) : this()
140         {
141             ItemsSource = itemsSource;
142         }
143
144         /// <summary>
145         /// Base constructor with ItemsSource, ItemsLayouter and ItemTemplate
146         /// </summary>
147         /// <param name="itemsSource">item's data source</param>
148         /// <param name="layouter">item's layout manager</param>
149         /// <param name="template">item's view template with data bindings</param>
150         [EditorBrowsable(EditorBrowsableState.Never)]
151         public CollectionView(IEnumerable itemsSource, ItemsLayouter layouter, DataTemplate template) : this()
152         {
153             ItemsSource = itemsSource;
154             ItemTemplate = template;
155             ItemsLayouter = layouter;
156         }
157
158         /// <summary>
159         /// Event of Selection changed.
160         /// old selection list and new selection will be provided.
161         /// </summary>
162         [EditorBrowsable(EditorBrowsableState.Never)]
163         public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
164
165         /// <summary>
166         /// Align item in the viewport when ScrollTo() calls.
167         /// </summary>
168         [EditorBrowsable(EditorBrowsableState.Never)]
169         public enum ItemScrollTo
170         {
171             /// <summary>
172             /// Scroll to show item in nearest viewport on scroll direction.
173             /// item is above the scroll viewport, item will be came into front,
174             /// item is under the scroll viewport, item will be came into end,
175             /// item is in the scroll viewport, no scroll.
176             /// </summary>
177             Nearest,
178             /// <summary>
179             /// Scroll to show item in start of the viewport.
180             /// </summary>
181             Start,
182             /// <summary>
183             /// Scroll to show item in center of the viewport.
184             /// </summary>
185             Center,
186             /// <summary>
187             /// Scroll to show item in end of the viewport.
188             /// </summary>
189             End,
190         }
191
192         /// <summary>
193         /// Item's source data.
194         /// </summary>
195         [EditorBrowsable(EditorBrowsableState.Never)]
196         public override IEnumerable ItemsSource
197         {
198             get
199             {
200                 return itemsSource;
201             }
202             set
203             {
204                 if (itemsSource != null)
205                 {
206                     // Clearing old data!
207                     if (itemsSource is INotifyCollectionChanged prevNotifyCollectionChanged)
208                     {
209                         prevNotifyCollectionChanged.CollectionChanged -= CollectionChanged;
210                     }
211                     itemsLayouter.Clear();
212                     if (selectedItem != null) selectedItem = null;
213                     if (selectedItems != null)
214                     {
215                         selectedItems.Clear();
216                     }
217                 }
218
219                 itemsSource = value;
220                 if (value == null)
221                 {
222                     if (InternalItemSource != null) InternalItemSource.Dispose();
223                     //layouter.Clear()
224                     return;
225                 }
226                 if (itemsSource is INotifyCollectionChanged newNotifyCollectionChanged)
227                 {
228                     newNotifyCollectionChanged.CollectionChanged += CollectionChanged;
229                 }
230
231                 if (InternalItemSource != null) InternalItemSource.Dispose();
232                 InternalItemSource = ItemsSourceFactory.Create(this);
233
234                 if (itemsLayouter == null) return;
235
236                 needInitalizeLayouter = true;
237                 Init();
238             }
239         }
240
241         /// <summary>
242         /// DataTemplate for items.
243         /// </summary>
244         [EditorBrowsable(EditorBrowsableState.Never)]
245         public override DataTemplate ItemTemplate
246         {
247             get
248             {
249                 return itemTemplate;
250             }
251             set
252             {
253                 itemTemplate = value;
254                 if (value == null)
255                 {
256                     //layouter.clear()
257                     return;
258                 }
259
260                 needInitalizeLayouter = true;
261                 Init();
262             }
263         }
264
265         /// <summary>
266         /// Items Layouter.
267         /// </summary>
268         [EditorBrowsable(EditorBrowsableState.Never)]
269         public virtual ItemsLayouter ItemsLayouter
270         {
271             get
272             {
273                 return itemsLayouter;
274             }
275             set
276             {
277                 itemsLayouter = value;
278                 base.InternalItemsLayouter = ItemsLayouter;
279                 if (value == null)
280                 {
281                     needInitalizeLayouter = false;
282                     return;
283                 }
284
285                 needInitalizeLayouter = true;
286
287                 var styleName = "Tizen.NUI.Components." + (itemsLayouter is LinearLayouter? "LinearLayouter" : (itemsLayouter is GridLayouter ? "GridLayouter" : "ItemsLayouter"));
288                 using (ViewStyle layouterStyle = ThemeManager.GetStyle(styleName))
289                 {
290                     if (layouterStyle != null)
291                     {
292                         if (layouterStyle.Padding != null)
293                             itemsLayouter.Padding = new Extents(layouterStyle.Padding);
294                     }
295                 }
296                 Init();
297             }
298         }
299
300         /// <summary>
301         /// Scrolling direction to display items layout.
302         /// </summary>
303         [EditorBrowsable(EditorBrowsableState.Never)]
304         public new Direction ScrollingDirection
305         {
306             get
307             {
308                 return base.ScrollingDirection;
309             }
310             set
311             {
312                 base.ScrollingDirection = value;
313
314                 if (ScrollingDirection == Direction.Horizontal)
315                 {
316                     ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
317                 }
318                 else
319                 {
320                     ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
321                 }
322             }
323         }
324
325         /// <summary>
326         /// Selected item in single selection.
327         /// </summary>
328         [EditorBrowsable(EditorBrowsableState.Never)]
329         public object SelectedItem
330         {
331             get => GetValue(SelectedItemProperty);
332             set => SetValue(SelectedItemProperty, value);
333         }
334
335         /// <summary>
336         /// Selected items list in multiple selection.
337         /// </summary>
338         [EditorBrowsable(EditorBrowsableState.Never)]
339         public IList<object> SelectedItems
340         {
341             get => (IList<object>)GetValue(SelectedItemsProperty);
342             // set => SetValue(SelectedItemsProperty, new SelectionList(this, value));
343         }
344
345         /// <summary>
346         /// Selection mode to handle items selection. See ItemSelectionMode for details.
347         /// </summary>
348         [EditorBrowsable(EditorBrowsableState.Never)]
349         public ItemSelectionMode SelectionMode
350         {
351             get => (ItemSelectionMode)GetValue(SelectionModeProperty);
352             set => SetValue(SelectionModeProperty, value);
353         }
354
355         /// <summary>
356         /// Command of selection changed.
357         /// </summary>
358         [EditorBrowsable(EditorBrowsableState.Never)]
359         public ICommand SelectionChangedCommand { set; get; }
360
361         /// <summary>
362         /// Command parameter of selection changed.
363         /// </summary>
364         [EditorBrowsable(EditorBrowsableState.Never)]
365         public object SelectionChangedCommandParameter { set; get; }
366
367         /// <summary>
368         /// Header item which placed in top-most position.
369         /// note : internal index and count will be increased.
370         /// </summary>
371         [EditorBrowsable(EditorBrowsableState.Never)]
372         public RecyclerViewItem Header
373         {
374             get => header;
375             set
376             {
377                 if (header != null)
378                 {
379                     //ContentContainer.Remove(header);
380                     Utility.Dispose(header);
381                 }
382                 if (value != null)
383                 {
384                     value.Index = 0;
385                     value.ParentItemsView = this;
386                     value.IsHeader = true;
387                     ContentContainer.Add(value);
388                 }
389                 header = value;
390                 needInitalizeLayouter = true;
391                 Init();
392             }
393         }
394
395         /// <summary>
396         /// Footer item which placed in bottom-most position.
397         /// note : internal count will be increased.
398         /// </summary>
399         [EditorBrowsable(EditorBrowsableState.Never)]
400         public RecyclerViewItem Footer
401         {
402             get => footer;
403             set
404             {
405                 if (footer != null)
406                 {
407                     //ContentContainer.Remove(footer);
408                     Utility.Dispose(footer);
409                 }
410                 if (value != null)
411                 {
412                     value.Index = InternalItemSource?.Count ?? 0;
413                     value.ParentItemsView = this;
414                     value.IsFooter = true;
415                     ContentContainer.Add(value);
416                 }
417                 footer = value;
418                 needInitalizeLayouter = true;
419                 Init();
420             }
421         }
422
423         /// <summary>
424         /// Boolean flag of group feature existence.
425         /// </summary>
426         [EditorBrowsable(EditorBrowsableState.Never)]
427         public bool IsGrouped
428         {
429             get => isGrouped;
430             set
431             {
432                 isGrouped = value;
433                 needInitalizeLayouter = true;
434                 //Need to re-intialize Internal Item Source.
435                 if (InternalItemSource != null)
436                 {
437                     InternalItemSource.Dispose();
438                     InternalItemSource = null;
439                 }
440                 if (ItemsSource != null)
441                     InternalItemSource = ItemsSourceFactory.Create(this);
442                 Init();
443             }
444         }
445
446         /// <summary>
447         ///  DataTemplate of group header. Group feature is not supported yet.
448         /// </summary>
449         [EditorBrowsable(EditorBrowsableState.Never)]
450         public DataTemplate GroupHeaderTemplate
451         {
452             get
453             {
454                 return groupHeaderTemplate;
455             }
456             set
457             {
458                 groupHeaderTemplate = value;
459                 needInitalizeLayouter = true;
460                 Init();
461             }
462         }
463
464         /// <summary>
465         /// DataTemplate of group footer. Group feature is not supported yet.
466         /// </summary>
467         [EditorBrowsable(EditorBrowsableState.Never)]
468         public DataTemplate GroupFooterTemplate
469         {
470             get
471             {
472                 return groupFooterTemplate;
473             }
474             set
475             {
476                 groupFooterTemplate = value;
477                 needInitalizeLayouter = true;
478                 Init();
479             }
480         }
481
482         /// <summary>
483         /// Internal encapsulated items data source.
484         /// </summary>
485         internal new IGroupableItemSource InternalItemSource
486         {
487             get
488             {
489                 return (base.InternalItemSource as IGroupableItemSource);
490             }
491             set
492             {
493                 base.InternalItemSource = value;
494             }
495         }
496
497         /// <summary>
498         /// Size strategy of measuring scroll content. see details in ItemSizingStrategy.
499         /// </summary>
500         [EditorBrowsable(EditorBrowsableState.Never)]
501         internal ItemSizingStrategy SizingStrategy { get; set; }
502
503         /// <inheritdoc/>
504         [EditorBrowsable(EditorBrowsableState.Never)]
505         public override void OnRelayout(Vector2 size, RelayoutContainer container)
506         {
507             base.OnRelayout(size, container);
508
509             wasRelayouted = true;
510             if (needInitalizeLayouter) Init();
511         }
512
513         /// <inheritdoc/>
514         [EditorBrowsable(EditorBrowsableState.Never)]
515         public override void NotifyDataSetChanged()
516         {
517             if (selectedItem != null)
518             {
519                 selectedItem = null;
520             }
521             if (selectedItems != null)
522             {
523                 selectedItems.Clear();
524             }
525
526             base.NotifyDataSetChanged();
527         }
528
529         /// <inheritdoc/>
530         [EditorBrowsable(EditorBrowsableState.Never)]
531         public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
532         {
533             View nextFocusedView = null;
534
535             if (focusedView == null)
536             {
537                 // If focusedView is null, find child which has previous data index
538                 if (ContentContainer.Children.Count > 0 && InternalItemSource.Count > 0)
539                 {
540                     for (int i = 0; i < ContentContainer.Children.Count; i++)
541                     {
542                         RecyclerViewItem item = Children[i] as RecyclerViewItem;
543                         if (item?.Index == prevFocusedDataIndex)
544                         {
545                             nextFocusedView = item;
546                             break;
547                         }
548                     }
549                 }
550             }
551             else
552             {
553                 // If this is not first focus, request next focus to Layouter
554                 nextFocusedView = ItemsLayouter.RequestNextFocusableView(currentFocusedView, direction, loopEnabled);
555             }
556
557             if (nextFocusedView != null)
558             {
559                 // Check next focused view is inside of visible area.
560                 // If it is not, move scroll position to make it visible.
561                 Position scrollPosition = ContentContainer.CurrentPosition;
562                 float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
563
564                 float left = nextFocusedView.Position.X;
565                 float right = nextFocusedView.Position.X + nextFocusedView.Size.Width;
566                 float top = nextFocusedView.Position.Y;
567                 float bottom = nextFocusedView.Position.Y + nextFocusedView.Size.Height;
568
569                 float visibleRectangleLeft = -scrollPosition.X;
570                 float visibleRectangleRight = -scrollPosition.X + Size.Width;
571                 float visibleRectangleTop = -scrollPosition.Y;
572                 float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
573
574                 if (ScrollingDirection == Direction.Horizontal)
575                 {
576                     if ((direction == View.FocusDirection.Left || direction == View.FocusDirection.Up) && left < visibleRectangleLeft)
577                     {
578                         targetPosition = left;
579                     }
580                     else if ((direction == View.FocusDirection.Right || direction == View.FocusDirection.Down) && right > visibleRectangleRight)
581                     {
582                         targetPosition = right - Size.Width;
583                     }
584                 }
585                 else
586                 {
587                     if ((direction == View.FocusDirection.Up || direction == View.FocusDirection.Left) && top < visibleRectangleTop)
588                     {
589                         targetPosition = top;
590                     }
591                     else if ((direction == View.FocusDirection.Down || direction == View.FocusDirection.Right) && bottom > visibleRectangleBottom)
592                     {
593                         targetPosition = bottom - Size.Height;
594                     }
595                 }
596
597                 focusedView = nextFocusedView;
598                 prevFocusedDataIndex = (nextFocusedView as RecyclerViewItem)?.Index ?? -1;
599
600                 ScrollTo(targetPosition, true);
601             }
602             else
603             {
604                 // If nextView is null, it means that we should move focus to outside of Control.
605                 // Return FocusableView depending on direction.
606                 switch (direction)
607                 {
608                     case View.FocusDirection.Left:
609                         {
610                             nextFocusedView = LeftFocusableView;
611                             break;
612                         }
613                     case View.FocusDirection.Right:
614                         {
615                             nextFocusedView = RightFocusableView;
616                             break;
617                         }
618                     case View.FocusDirection.Up:
619                         {
620                             nextFocusedView = UpFocusableView;
621                             break;
622                         }
623                     case View.FocusDirection.Down:
624                         {
625                             nextFocusedView = DownFocusableView;
626                             break;
627                         }
628                 }
629
630                 if (nextFocusedView != null)
631                 {
632                     focusedView = null;
633                 }
634                 else
635                 {
636                     //If FocusableView doesn't exist, not move focus.
637                     nextFocusedView = focusedView;
638                 }
639             }
640
641             return nextFocusedView;
642         }
643
644         /// <summary>
645         /// Update selected items list in multiple selection.
646         /// </summary>
647         /// <param name="newSelection">updated selection list by user</param>
648         [EditorBrowsable(EditorBrowsableState.Never)]
649         public void UpdateSelectedItems(IList<object> newSelection)
650         {
651             var oldSelection = new List<object>(SelectedItems);
652
653             suppressSelectionChangeNotification = true;
654
655             SelectedItems.Clear();
656
657             if (newSelection?.Count > 0)
658             {
659                 for (int n = 0; n < newSelection.Count; n++)
660                 {
661                     SelectedItems.Add(newSelection[n]);
662                 }
663             }
664
665             suppressSelectionChangeNotification = false;
666
667             SelectedItemsPropertyChanged(oldSelection, newSelection);
668         }
669
670         /// <summary>
671         /// Scroll to specific position with or without animation.
672         /// </summary>
673         /// <param name="position">Destination.</param>
674         /// <param name="animate">Scroll with or without animation</param>
675         [EditorBrowsable(EditorBrowsableState.Never)]
676         public new void ScrollTo(float position, bool animate) => base.ScrollTo(position, animate);
677
678         /// <summary>
679         /// Scroll to specific item's aligned position with or without animation.
680         /// </summary>
681         /// <param name="index">Target item index of dataset.</param>
682         /// <param name="animate">Boolean flag of animation.</param>
683         /// <param name="align">Align state of item. see details in ItemScrollTo.</param>
684         [EditorBrowsable(EditorBrowsableState.Never)]
685         public virtual void ScrollTo(int index, bool animate = false, ItemScrollTo align = ItemScrollTo.Nearest)
686         {
687             if (ItemsLayouter == null) throw new Exception("Item Layouter must exist.");
688
689             float scrollPos, curPos, curSize, curItemSize;
690             (float x, float y) = ItemsLayouter.GetItemPosition(index);
691             (float width, float height) = ItemsLayouter.GetItemSize(index);
692             if (ScrollingDirection == Direction.Horizontal)
693             {
694                 scrollPos = x;
695                 curPos = ScrollPosition.X;
696                 curSize = Size.Width;
697                 curItemSize = width;
698             }
699             else
700             {
701                 scrollPos = y;
702                 curPos = ScrollPosition.Y;
703                 curSize = Size.Height;
704                 curItemSize = height;
705             }
706
707             //Console.WriteLine("[NUI] ScrollTo [{0}:{1}], curPos{2}, itemPos{3}, curSize{4}, itemSize{5}", InternalItemSource.GetPosition(item), align, curPos, scrollPos, curSize, curItemSize);
708             switch (align)
709             {
710                 case ItemScrollTo.Start:
711                     //nothing necessary.
712                     break;
713                 case ItemScrollTo.Center:
714                     scrollPos = scrollPos - (curSize / 2) + (curItemSize / 2);
715                     break;
716                 case ItemScrollTo.End:
717                     scrollPos = scrollPos - curSize + curItemSize;
718                     break;
719                 case ItemScrollTo.Nearest:
720                     if (scrollPos < curPos - curItemSize)
721                     {
722                         // item is placed before the current screen. scrollTo.Top
723                     }
724                     else if (scrollPos >= curPos + curSize + curItemSize)
725                     {
726                         // item is placed after the current screen. scrollTo.End
727                         scrollPos = scrollPos - curSize + curItemSize;
728                     }
729                     else
730                     {
731                         // item is in the scroller. ScrollTo() is ignored.
732                         return;
733                     }
734                     break;
735             }
736
737             //Console.WriteLine("[NUI] ScrollTo [{0}]-------------------", scrollPos);
738             base.ScrollTo(scrollPos, animate);
739         }
740
741         /// <summary>
742         /// Apply style to CollectionView
743         /// </summary>
744         /// <param name="viewStyle">The style to apply.</param>
745         [EditorBrowsable(EditorBrowsableState.Never)]
746         public override void ApplyStyle(ViewStyle viewStyle)
747         {
748             base.ApplyStyle(viewStyle);
749             if (viewStyle != null)
750             {
751                 //Extension = RecyclerViewItemStyle.CreateExtension();
752             }
753             if (itemsLayouter != null)
754             {
755                 string styleName = "Tizen.NUI.Compoenents." + (itemsLayouter is LinearLayouter? "LinearLayouter" : (itemsLayouter is GridLayouter ? "GridLayouter" : "ItemsLayouter"));
756                 using (ViewStyle layouterStyle = ThemeManager.GetStyle(styleName))
757                 {
758                     if (layouterStyle != null)
759                         itemsLayouter.Padding = new Extents(layouterStyle.Padding);
760                 }
761             }
762         }
763
764         // Realize and Decorate the item.
765         internal override RecyclerViewItem RealizeItem(int index)
766         {
767             RecyclerViewItem item;
768             if (index == 0 && Header != null)
769             {
770                 Header.Show();
771                 return Header;
772             }
773
774             if (index == InternalItemSource.Count - 1 && Footer != null)
775             {
776                 Footer.Show();
777                 return Footer;
778             }
779
780             if (isGrouped)
781             {
782                 var context = InternalItemSource.GetItem(index);
783                 if (InternalItemSource.IsGroupHeader(index))
784                 {
785                     DataTemplate templ = (groupHeaderTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupHeaderTemplate;
786
787                     RecyclerViewItem groupHeader = PopRecycleGroupCache(templ, true);
788                     if (groupHeader == null)
789                     {
790                         groupHeader = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupHeaderTemplate, context, this);
791
792                         groupHeader.Template = templ;
793                         groupHeader.isGroupHeader = true;
794                         groupHeader.isGroupFooter = false;
795                         ContentContainer.Add(groupHeader);
796                     }
797                     groupHeader.ParentItemsView = this;
798                     groupHeader.Index = index;
799                     groupHeader.ParentGroup = context;
800                     groupHeader.BindingContext = context;
801                     //group selection?
802                     item = groupHeader;
803                 }
804                 else if (InternalItemSource.IsGroupFooter(index))
805                 {
806                     DataTemplate templ = (groupFooterTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupFooterTemplate;
807
808                     RecyclerViewItem groupFooter = PopRecycleGroupCache(templ, false);
809                     if (groupFooter == null)
810                     {
811                         groupFooter = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupFooterTemplate, context, this);
812
813                         groupFooter.Template = templ;
814                         groupFooter.isGroupHeader = false;
815                         groupFooter.isGroupFooter = true;
816                         ContentContainer.Add(groupFooter);
817                     }
818                     groupFooter.ParentItemsView = this;
819                     groupFooter.Index = index;
820                     groupFooter.ParentGroup = context;
821                     groupFooter.BindingContext = context;
822
823                     //group selection?
824                     item = groupFooter;
825                 }
826                 else
827                 {
828                     item = base.RealizeItem(index);
829                     item.ParentGroup = InternalItemSource.GetGroupParent(index);
830                 }
831             }
832             else
833             {
834                 item = base.RealizeItem(index);
835             }
836
837             switch (SelectionMode)
838             {
839                 case ItemSelectionMode.SingleSelection:
840                     if (item.BindingContext != null && item.BindingContext == SelectedItem)
841                     {
842                         item.IsSelected = true;
843                     }
844                     break;
845
846                 case ItemSelectionMode.MultipleSelections:
847                     if ((item.BindingContext != null) && (SelectedItems?.Contains(item.BindingContext) ?? false))
848                     {
849                         item.IsSelected = true;
850                     }
851                     break;
852                 case ItemSelectionMode.None:
853                     item.IsSelectable = false;
854                     break;
855             }
856             return item;
857         }
858
859         // Unrealize and caching the item.
860         internal override void UnrealizeItem(RecyclerViewItem item, bool recycle = true)
861         {
862             if (item == Header)
863             {
864                 item.Hide();
865                 return;
866             }
867             if (item == Footer)
868             {
869                 item.Hide();
870                 return;
871             }
872             if (item.isGroupHeader || item.isGroupFooter)
873             {
874                 item.Index = -1;
875                 item.ParentItemsView = null;
876                 item.BindingContext = null; 
877                 item.IsPressed = false;
878                 item.IsSelected = false;
879                 item.IsEnabled = true;
880                 item.UpdateState();
881                 //item.Relayout -= OnItemRelayout;
882                 if (!recycle || !PushRecycleGroupCache(item))
883                     Utility.Dispose(item);
884                 return;
885             }
886
887             base.UnrealizeItem(item, recycle);
888         }
889
890         internal void SelectedItemsPropertyChanged(IList<object> oldSelection, IList<object> newSelection)
891         {
892             if (suppressSelectionChangeNotification)
893             {
894                 return;
895             }
896
897             foreach (RecyclerViewItem item in ContentContainer.Children.Where((item) => item is RecyclerViewItem))
898             {
899                 if (item.BindingContext == null) continue;
900                 if (newSelection.Contains(item.BindingContext))
901                 {
902                     if (!item.IsSelected) item.IsSelected = true;
903                 }
904                 else
905                 {
906                     if (item.IsSelected) item.IsSelected = false;
907                 }
908             }
909             SelectionPropertyChanged(this, new SelectionChangedEventArgs(oldSelection, newSelection));
910
911             OnPropertyChanged(SelectedItemsProperty.PropertyName);
912         }
913
914         /// <summary>
915         /// Internal selection callback.
916         /// </summary>
917         [EditorBrowsable(EditorBrowsableState.Never)]
918         protected virtual void OnSelectionChanged(SelectionChangedEventArgs args)
919         {
920             //Selection Callback
921         }
922
923         /// <summary>
924         /// Adjust scrolling position by own scrolling rules.
925         /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
926         /// </summary>
927         /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
928         /// <returns>Adjusted scroll destination</returns>
929         [EditorBrowsable(EditorBrowsableState.Never)]
930         protected override float AdjustTargetPositionOfScrollAnimation(float position)
931         {
932             // Destination is depending on implementation of layout manager.
933             // Get destination from layout manager.
934             return ItemsLayouter.CalculateCandidateScrollPosition(position);
935         }
936
937         /// <summary>
938         /// OnScroll event callback.
939         /// </summary>
940         /// <param name="source">Scroll source object</param>
941         /// <param name="args">Scroll event argument</param>
942         [EditorBrowsable(EditorBrowsableState.Never)]
943         protected override void OnScrolling(object source, ScrollEventArgs args)
944         {
945             if (disposed) return;
946
947             if (needInitalizeLayouter)
948             {
949                 ItemsLayouter.Initialize(this);
950                 needInitalizeLayouter = false;
951             }
952
953             base.OnScrolling(source, args);
954         }
955
956         /// <summary>
957         /// Dispose ItemsView and all children on it.
958         /// </summary>
959         /// <param name="type">Dispose type.</param>
960         protected override void Dispose(DisposeTypes type)
961         {
962             if (disposed)
963             {
964                 return;
965             }
966
967             if (type == DisposeTypes.Explicit)
968             {
969                 disposed = true;
970
971                 // From now on, no need to use this properties,
972                 // so remove reference, to push it into garbage collector.
973
974                 if (InternalItemSource != null)
975                 {
976                     InternalItemSource.Dispose();
977                     InternalItemSource = null;
978                 }
979                 if (Header != null)
980                 {
981                     Utility.Dispose(Header);
982                     Header = null;
983                 }
984                 if (Footer != null)
985                 {
986                     Utility.Dispose(Footer);
987                     Footer = null;
988                 }
989                 groupHeaderTemplate = null;
990                 groupFooterTemplate = null;
991
992                 if (selectedItem != null) 
993                 {
994                     selectedItem = null;
995                 }
996                 if (selectedItems != null)
997                 {
998                     selectedItems.Clear();
999                     selectedItems = null;
1000                 }
1001             }
1002
1003             base.Dispose(type);
1004         }
1005
1006         private static void SelectionPropertyChanged(CollectionView colView, SelectionChangedEventArgs args)
1007         {
1008             var command = colView.SelectionChangedCommand;
1009
1010             if (command != null)
1011             {
1012                 var commandParameter = colView.SelectionChangedCommandParameter;
1013
1014                 if (command.CanExecute(commandParameter))
1015                 {
1016                     command.Execute(commandParameter);
1017                 }
1018             }
1019             colView.SelectionChanged?.Invoke(colView, args);
1020             colView.OnSelectionChanged(args);
1021         }
1022
1023         private static object CoerceSelectedItems(BindableObject bindable, object value)
1024         {
1025             if (value == null)
1026             {
1027                 return new SelectionList((CollectionView)bindable);
1028             }
1029
1030             if (value is SelectionList)
1031             {
1032                 return value;
1033             }
1034
1035             return new SelectionList((CollectionView)bindable, value as IList<object>);
1036         }
1037
1038         private static void SelectionModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
1039         {
1040             var colView = (CollectionView)bindable;
1041
1042             var oldMode = (ItemSelectionMode)oldValue;
1043             var newMode = (ItemSelectionMode)newValue;
1044
1045             IList<object> previousSelection = new List<object>();
1046             IList<object> newSelection = new List<object>();
1047
1048             switch (oldMode)
1049             {
1050                 case ItemSelectionMode.None:
1051                     break;
1052                 case ItemSelectionMode.SingleSelection:
1053                     if (colView.SelectedItem != null)
1054                     {
1055                         previousSelection.Add(colView.SelectedItem);
1056                     }
1057                     break;
1058                 case ItemSelectionMode.MultipleSelections:
1059                     previousSelection = colView.SelectedItems;
1060                     break;
1061             }
1062
1063             switch (newMode)
1064             {
1065                 case ItemSelectionMode.None:
1066                     break;
1067                 case ItemSelectionMode.SingleSelection:
1068                     if (colView.SelectedItem != null)
1069                     {
1070                         newSelection.Add(colView.SelectedItem);
1071                     }
1072                     break;
1073                 case ItemSelectionMode.MultipleSelections:
1074                     newSelection = colView.SelectedItems;
1075                     break;
1076             }
1077
1078             if (previousSelection.Count == newSelection.Count)
1079             {
1080                 if (previousSelection.Count == 0 || (previousSelection[0] == newSelection[0]))
1081                 {
1082                     // Both selections are empty or have the same single item; no reason to signal a change
1083                     return;
1084                 }
1085             }
1086
1087             var args = new SelectionChangedEventArgs(previousSelection, newSelection);
1088             SelectionPropertyChanged(colView, args);
1089         }
1090
1091         private void Init()
1092         {
1093             if (ItemsSource == null) return;
1094             if (ItemsLayouter == null) return;
1095             if (ItemTemplate == null) return;
1096
1097             if (disposed) return;
1098             if (needInitalizeLayouter)
1099             {
1100                 if (InternalItemSource == null) return;
1101
1102                 InternalItemSource.HasHeader = (header != null);
1103                 InternalItemSource.HasFooter = (footer != null);
1104             }
1105
1106             if (!wasRelayouted) return;
1107
1108             if (needInitalizeLayouter)
1109             {
1110                 ItemsLayouter.Initialize(this);
1111                 needInitalizeLayouter = false;
1112             }
1113             ItemsLayouter.RequestLayout(0.0f, true);
1114
1115             if (ScrollingDirection == Direction.Horizontal)
1116             {
1117                 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
1118             }
1119             else
1120             {
1121                 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
1122             }
1123         }
1124
1125         private bool PushRecycleGroupCache(RecyclerViewItem item)
1126         {
1127             if (item == null) throw new ArgumentNullException(nameof(item));
1128             if (RecycleCache.Count >= 20) return false;
1129             if (item.Template == null) return false;
1130             if (item.isGroupHeader)
1131             {
1132                 recycleGroupHeaderCache.Add(item);
1133             }
1134             else if (item.isGroupFooter)
1135             {
1136                 recycleGroupFooterCache.Add(item);
1137             }
1138             else return false;
1139             item.Hide();
1140             item.Index = -1;
1141             return true;
1142         }
1143
1144         private RecyclerViewItem PopRecycleGroupCache(DataTemplate Template, bool isHeader)
1145         {
1146             RecyclerViewItem viewItem = null;
1147
1148             var Cache = (isHeader ? recycleGroupHeaderCache : recycleGroupFooterCache);
1149             for (int i = 0; i < Cache.Count; i++)
1150             {
1151                 viewItem = Cache[i];
1152                 if (Template == viewItem.Template) break;
1153             }
1154
1155             if (viewItem != null)
1156             {
1157                 Cache.Remove(viewItem);
1158                 viewItem.Show();
1159             }
1160             return viewItem;
1161         }
1162         private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
1163         {
1164             switch (args.Action)
1165             {
1166                 case NotifyCollectionChangedAction.Add:
1167                     break;
1168                 case NotifyCollectionChangedAction.Remove:
1169                     // Clear removed items.
1170                     if (args.OldItems != null)
1171                     {
1172                         if (args.OldItems.Contains(selectedItem))
1173                         {
1174                             selectedItem = null;
1175                         }
1176                         
1177                         if (selectedItems != null)
1178                         {
1179                             foreach (object removed in args.OldItems)
1180                             {
1181                                 if (selectedItems.Contains(removed))
1182                                 {
1183                                     selectedItems.Remove(removed);
1184                                 }
1185                             }
1186                         }
1187                     }
1188                     break;
1189                 case NotifyCollectionChangedAction.Replace:
1190                     break;
1191                 case NotifyCollectionChangedAction.Move:
1192                     break;
1193                 case NotifyCollectionChangedAction.Reset:
1194                     break;
1195                 default:
1196                     throw new ArgumentOutOfRangeException(nameof(args));
1197             }
1198         }
1199
1200     }
1201 }