Refactoring itemSelectionMode names and adding SingleAlways (#2985)
[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                 ViewStyle layouterStyle = ThemeManager.GetStyle(styleName);
289                 if (layouterStyle != null)
290                 {
291                     itemsLayouter.Padding = new Extents(layouterStyle.Padding);
292                 }
293                 Init();
294             }
295         }
296
297         /// <summary>
298         /// Scrolling direction to display items layout.
299         /// </summary>
300         [EditorBrowsable(EditorBrowsableState.Never)]
301         public new Direction ScrollingDirection
302         {
303             get
304             {
305                 return base.ScrollingDirection;
306             }
307             set
308             {
309                 if (base.ScrollingDirection != value)
310                 {
311                     base.ScrollingDirection = value;
312                     needInitalizeLayouter = true;
313                     Init();
314                 }
315             }
316         }
317
318         /// <summary>
319         /// Selected item in single selection.
320         /// </summary>
321         [EditorBrowsable(EditorBrowsableState.Never)]
322         public object SelectedItem
323         {
324             get => GetValue(SelectedItemProperty);
325             set => SetValue(SelectedItemProperty, value);
326         }
327
328         /// <summary>
329         /// Selected items list in multiple selection.
330         /// </summary>
331         [EditorBrowsable(EditorBrowsableState.Never)]
332         public IList<object> SelectedItems
333         {
334             get => (IList<object>)GetValue(SelectedItemsProperty);
335             // set => SetValue(SelectedItemsProperty, new SelectionList(this, value));
336         }
337
338         /// <summary>
339         /// Selection mode to handle items selection. See ItemSelectionMode for details.
340         /// </summary>
341         [EditorBrowsable(EditorBrowsableState.Never)]
342         public ItemSelectionMode SelectionMode
343         {
344             get => (ItemSelectionMode)GetValue(SelectionModeProperty);
345             set => SetValue(SelectionModeProperty, value);
346         }
347
348         /// <summary>
349         /// Command of selection changed.
350         /// </summary>
351         [EditorBrowsable(EditorBrowsableState.Never)]
352         public ICommand SelectionChangedCommand { set; get; }
353
354         /// <summary>
355         /// Command parameter of selection changed.
356         /// </summary>
357         [EditorBrowsable(EditorBrowsableState.Never)]
358         public object SelectionChangedCommandParameter { set; get; }
359
360         /// <summary>
361         /// Header item which placed in top-most position.
362         /// note : internal index and count will be increased.
363         /// </summary>
364         [EditorBrowsable(EditorBrowsableState.Never)]
365         public RecyclerViewItem Header
366         {
367             get => header;
368             set
369             {
370                 if (header != null)
371                 {
372                     //ContentContainer.Remove(header);
373                     Utility.Dispose(header);
374                 }
375                 if (value != null)
376                 {
377                     value.Index = 0;
378                     value.ParentItemsView = this;
379                     value.IsHeader = true;
380                     ContentContainer.Add(value);
381                 }
382                 header = value;
383                 needInitalizeLayouter = true;
384                 Init();
385             }
386         }
387
388         /// <summary>
389         /// Footer item which placed in bottom-most position.
390         /// note : internal count will be increased.
391         /// </summary>
392         [EditorBrowsable(EditorBrowsableState.Never)]
393         public RecyclerViewItem Footer
394         {
395             get => footer;
396             set
397             {
398                 if (footer != null)
399                 {
400                     //ContentContainer.Remove(footer);
401                     Utility.Dispose(footer);
402                 }
403                 if (value != null)
404                 {
405                     value.Index = InternalItemSource?.Count ?? 0;
406                     value.ParentItemsView = this;
407                     value.IsFooter = true;
408                     ContentContainer.Add(value);
409                 }
410                 footer = value;
411                 needInitalizeLayouter = true;
412                 Init();
413             }
414         }
415
416         /// <summary>
417         /// Boolean flag of group feature existence.
418         /// </summary>
419         [EditorBrowsable(EditorBrowsableState.Never)]
420         public bool IsGrouped
421         {
422             get => isGrouped;
423             set
424             {
425                 isGrouped = value;
426                 needInitalizeLayouter = true;
427                 //Need to re-intialize Internal Item Source.
428                 if (InternalItemSource != null)
429                 {
430                     InternalItemSource.Dispose();
431                     InternalItemSource = null;
432                 }
433                 if (ItemsSource != null)
434                     InternalItemSource = ItemsSourceFactory.Create(this);
435                 Init();
436             }
437         }
438
439         /// <summary>
440         ///  DataTemplate of group header. Group feature is not supported yet.
441         /// </summary>
442         [EditorBrowsable(EditorBrowsableState.Never)]
443         public DataTemplate GroupHeaderTemplate
444         {
445             get
446             {
447                 return groupHeaderTemplate;
448             }
449             set
450             {
451                 groupHeaderTemplate = value;
452                 needInitalizeLayouter = true;
453                 Init();
454             }
455         }
456
457         /// <summary>
458         /// DataTemplate of group footer. Group feature is not supported yet.
459         /// </summary>
460         [EditorBrowsable(EditorBrowsableState.Never)]
461         public DataTemplate GroupFooterTemplate
462         {
463             get
464             {
465                 return groupFooterTemplate;
466             }
467             set
468             {
469                 groupFooterTemplate = value;
470                 needInitalizeLayouter = true;
471                 Init();
472             }
473         }
474
475         /// <summary>
476         /// Internal encapsulated items data source.
477         /// </summary>
478         internal new IGroupableItemSource InternalItemSource
479         {
480             get
481             {
482                 return (base.InternalItemSource as IGroupableItemSource);
483             }
484             set
485             {
486                 base.InternalItemSource = value;
487             }
488         }
489
490         /// <summary>
491         /// Size strategy of measuring scroll content. see details in ItemSizingStrategy.
492         /// </summary>
493         [EditorBrowsable(EditorBrowsableState.Never)]
494         internal ItemSizingStrategy SizingStrategy { get; set; }
495
496         /// <inheritdoc/>
497         [EditorBrowsable(EditorBrowsableState.Never)]
498         public override void OnRelayout(Vector2 size, RelayoutContainer container)
499         {
500             base.OnRelayout(size, container);
501
502             wasRelayouted = true;
503             if (needInitalizeLayouter) Init();
504         }
505
506         /// <inheritdoc/>
507         [EditorBrowsable(EditorBrowsableState.Never)]
508         public override void NotifyDataSetChanged()
509         {
510             if (selectedItem != null)
511             {
512                 selectedItem = null;
513             }
514             if (selectedItems != null)
515             {
516                 selectedItems.Clear();
517             }
518
519             base.NotifyDataSetChanged();
520         }
521
522         /// <inheritdoc/>
523         [EditorBrowsable(EditorBrowsableState.Never)]
524         public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
525         {
526             View nextFocusedView = null;
527
528             if (focusedView == null)
529             {
530                 // If focusedView is null, find child which has previous data index
531                 if (ContentContainer.Children.Count > 0 && InternalItemSource.Count > 0)
532                 {
533                     for (int i = 0; i < ContentContainer.Children.Count; i++)
534                     {
535                         RecyclerViewItem item = Children[i] as RecyclerViewItem;
536                         if (item?.Index == prevFocusedDataIndex)
537                         {
538                             nextFocusedView = item;
539                             break;
540                         }
541                     }
542                 }
543             }
544             else
545             {
546                 // If this is not first focus, request next focus to Layouter
547                 nextFocusedView = ItemsLayouter.RequestNextFocusableView(currentFocusedView, direction, loopEnabled);
548             }
549
550             if (nextFocusedView != null)
551             {
552                 // Check next focused view is inside of visible area.
553                 // If it is not, move scroll position to make it visible.
554                 Position scrollPosition = ContentContainer.CurrentPosition;
555                 float targetPosition = -(ScrollingDirection == Direction.Horizontal ? scrollPosition.X : scrollPosition.Y);
556
557                 float left = nextFocusedView.Position.X;
558                 float right = nextFocusedView.Position.X + nextFocusedView.Size.Width;
559                 float top = nextFocusedView.Position.Y;
560                 float bottom = nextFocusedView.Position.Y + nextFocusedView.Size.Height;
561
562                 float visibleRectangleLeft = -scrollPosition.X;
563                 float visibleRectangleRight = -scrollPosition.X + Size.Width;
564                 float visibleRectangleTop = -scrollPosition.Y;
565                 float visibleRectangleBottom = -scrollPosition.Y + Size.Height;
566
567                 if (ScrollingDirection == Direction.Horizontal)
568                 {
569                     if ((direction == View.FocusDirection.Left || direction == View.FocusDirection.Up) && left < visibleRectangleLeft)
570                     {
571                         targetPosition = left;
572                     }
573                     else if ((direction == View.FocusDirection.Right || direction == View.FocusDirection.Down) && right > visibleRectangleRight)
574                     {
575                         targetPosition = right - Size.Width;
576                     }
577                 }
578                 else
579                 {
580                     if ((direction == View.FocusDirection.Up || direction == View.FocusDirection.Left) && top < visibleRectangleTop)
581                     {
582                         targetPosition = top;
583                     }
584                     else if ((direction == View.FocusDirection.Down || direction == View.FocusDirection.Right) && bottom > visibleRectangleBottom)
585                     {
586                         targetPosition = bottom - Size.Height;
587                     }
588                 }
589
590                 focusedView = nextFocusedView;
591                 prevFocusedDataIndex = (nextFocusedView as RecyclerViewItem)?.Index ?? -1;
592
593                 ScrollTo(targetPosition, true);
594             }
595             else
596             {
597                 // If nextView is null, it means that we should move focus to outside of Control.
598                 // Return FocusableView depending on direction.
599                 switch (direction)
600                 {
601                     case View.FocusDirection.Left:
602                         {
603                             nextFocusedView = LeftFocusableView;
604                             break;
605                         }
606                     case View.FocusDirection.Right:
607                         {
608                             nextFocusedView = RightFocusableView;
609                             break;
610                         }
611                     case View.FocusDirection.Up:
612                         {
613                             nextFocusedView = UpFocusableView;
614                             break;
615                         }
616                     case View.FocusDirection.Down:
617                         {
618                             nextFocusedView = DownFocusableView;
619                             break;
620                         }
621                 }
622
623                 if (nextFocusedView != null)
624                 {
625                     focusedView = null;
626                 }
627                 else
628                 {
629                     //If FocusableView doesn't exist, not move focus.
630                     nextFocusedView = focusedView;
631                 }
632             }
633
634             return nextFocusedView;
635         }
636
637         /// <summary>
638         /// Update selected items list in multiple selection.
639         /// </summary>
640         /// <param name="newSelection">updated selection list by user</param>
641         [EditorBrowsable(EditorBrowsableState.Never)]
642         public void UpdateSelectedItems(IList<object> newSelection)
643         {
644             var oldSelection = new List<object>(SelectedItems);
645
646             suppressSelectionChangeNotification = true;
647
648             SelectedItems.Clear();
649
650             if (newSelection?.Count > 0)
651             {
652                 for (int n = 0; n < newSelection.Count; n++)
653                 {
654                     SelectedItems.Add(newSelection[n]);
655                 }
656             }
657
658             suppressSelectionChangeNotification = false;
659
660             SelectedItemsPropertyChanged(oldSelection, newSelection);
661         }
662
663         /// <summary>
664         /// Scroll to specific position with or without animation.
665         /// </summary>
666         /// <param name="position">Destination.</param>
667         /// <param name="animate">Scroll with or without animation</param>
668         [EditorBrowsable(EditorBrowsableState.Never)]
669         public new void ScrollTo(float position, bool animate) => base.ScrollTo(position, animate);
670
671         /// <summary>
672         /// Scroll to specific item's aligned position with or without animation.
673         /// </summary>
674         /// <param name="index">Target item index of dataset.</param>
675         /// <param name="animate">Boolean flag of animation.</param>
676         /// <param name="align">Align state of item. see details in ItemScrollTo.</param>
677         [EditorBrowsable(EditorBrowsableState.Never)]
678         public virtual void ScrollTo(int index, bool animate = false, ItemScrollTo align = ItemScrollTo.Nearest)
679         {
680             if (ItemsLayouter == null) throw new Exception("Item Layouter must exist.");
681
682             float scrollPos, curPos, curSize, curItemSize;
683             (float x, float y) = ItemsLayouter.GetItemPosition(index);
684             (float width, float height) = ItemsLayouter.GetItemSize(index);
685             if (ScrollingDirection == Direction.Horizontal)
686             {
687                 scrollPos = x;
688                 curPos = ScrollPosition.X;
689                 curSize = Size.Width;
690                 curItemSize = width;
691             }
692             else
693             {
694                 scrollPos = y;
695                 curPos = ScrollPosition.Y;
696                 curSize = Size.Height;
697                 curItemSize = height;
698             }
699
700             //Console.WriteLine("[NUI] ScrollTo [{0}:{1}], curPos{2}, itemPos{3}, curSize{4}, itemSize{5}", InternalItemSource.GetPosition(item), align, curPos, scrollPos, curSize, curItemSize);
701             switch (align)
702             {
703                 case ItemScrollTo.Start:
704                     //nothing necessary.
705                     break;
706                 case ItemScrollTo.Center:
707                     scrollPos = scrollPos - (curSize / 2) + (curItemSize / 2);
708                     break;
709                 case ItemScrollTo.End:
710                     scrollPos = scrollPos - curSize + curItemSize;
711                     break;
712                 case ItemScrollTo.Nearest:
713                     if (scrollPos < curPos - curItemSize)
714                     {
715                         // item is placed before the current screen. scrollTo.Top
716                     }
717                     else if (scrollPos >= curPos + curSize + curItemSize)
718                     {
719                         // item is placed after the current screen. scrollTo.End
720                         scrollPos = scrollPos - curSize + curItemSize;
721                     }
722                     else
723                     {
724                         // item is in the scroller. ScrollTo() is ignored.
725                         return;
726                     }
727                     break;
728             }
729
730             //Console.WriteLine("[NUI] ScrollTo [{0}]-------------------", scrollPos);
731             base.ScrollTo(scrollPos, animate);
732         }
733
734         /// <summary>
735         /// Apply style to CollectionView
736         /// </summary>
737         /// <param name="viewStyle">The style to apply.</param>
738         [EditorBrowsable(EditorBrowsableState.Never)]
739         public override void ApplyStyle(ViewStyle viewStyle)
740         {
741             base.ApplyStyle(viewStyle);
742             if (viewStyle != null)
743             {
744                 //Extension = RecyclerViewItemStyle.CreateExtension();
745             }
746             if (itemsLayouter != null)
747             {
748                 string styleName = "Tizen.NUI.Compoenents." + (itemsLayouter is LinearLayouter? "LinearLayouter" : (itemsLayouter is GridLayouter ? "GridLayouter" : "ItemsLayouter"));
749                 ViewStyle layouterStyle = ThemeManager.GetStyle(styleName);
750                 if (layouterStyle != null)
751                     itemsLayouter.Padding = new Extents(layouterStyle.Padding);
752             }
753         }
754
755         // Realize and Decorate the item.
756         internal override RecyclerViewItem RealizeItem(int index)
757         {
758             RecyclerViewItem item;
759             if (index == 0 && Header != null)
760             {
761                 Header.Show();
762                 return Header;
763             }
764
765             if (index == InternalItemSource.Count - 1 && Footer != null)
766             {
767                 Footer.Show();
768                 return Footer;
769             }
770
771             if (isGrouped)
772             {
773                 var context = InternalItemSource.GetItem(index);
774                 if (InternalItemSource.IsGroupHeader(index))
775                 {
776                     DataTemplate templ = (groupHeaderTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupHeaderTemplate;
777
778                     RecyclerViewItem groupHeader = PopRecycleGroupCache(templ, true);
779                     if (groupHeader == null)
780                     {
781                         groupHeader = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupHeaderTemplate, context, this);
782
783                         groupHeader.Template = templ;
784                         groupHeader.isGroupHeader = true;
785                         groupHeader.isGroupFooter = false;
786                         ContentContainer.Add(groupHeader);
787                     }
788                     groupHeader.ParentItemsView = this;
789                     groupHeader.Index = index;
790                     groupHeader.ParentGroup = context;
791                     groupHeader.BindingContext = context;
792                     //group selection?
793                     item = groupHeader;
794                 }
795                 else if (InternalItemSource.IsGroupFooter(index))
796                 {
797                     DataTemplate templ = (groupFooterTemplate as DataTemplateSelector)?.SelectDataTemplate(context, this) ?? groupFooterTemplate;
798
799                     RecyclerViewItem groupFooter = PopRecycleGroupCache(templ, false);
800                     if (groupFooter == null)
801                     {
802                         groupFooter = (RecyclerViewItem)DataTemplateExtensions.CreateContent(groupFooterTemplate, context, this);
803
804                         groupFooter.Template = templ;
805                         groupFooter.isGroupHeader = false;
806                         groupFooter.isGroupFooter = true;
807                         ContentContainer.Add(groupFooter);
808                     }
809                     groupFooter.ParentItemsView = this;
810                     groupFooter.Index = index;
811                     groupFooter.ParentGroup = context;
812                     groupFooter.BindingContext = context;
813
814                     //group selection?
815                     item = groupFooter;
816                 }
817                 else
818                 {
819                     item = base.RealizeItem(index);
820                     item.ParentGroup = InternalItemSource.GetGroupParent(index);
821                 }
822             }
823             else
824             {
825                 item = base.RealizeItem(index);
826             }
827
828             switch (SelectionMode)
829             {
830                 case ItemSelectionMode.Single:
831                 case ItemSelectionMode.SingleAlways:
832                     if (item.BindingContext != null && item.BindingContext == SelectedItem)
833                     {
834                         item.IsSelected = true;
835                     }
836                     break;
837
838                 case ItemSelectionMode.Multiple:
839                     if ((item.BindingContext != null) && (SelectedItems?.Contains(item.BindingContext) ?? false))
840                     {
841                         item.IsSelected = true;
842                     }
843                     break;
844                 case ItemSelectionMode.None:
845                     item.IsSelectable = false;
846                     break;
847             }
848             return item;
849         }
850
851         // Unrealize and caching the item.
852         internal override void UnrealizeItem(RecyclerViewItem item, bool recycle = true)
853         {
854             if (item == Header)
855             {
856                 item.Hide();
857                 return;
858             }
859             if (item == Footer)
860             {
861                 item.Hide();
862                 return;
863             }
864             if (item.isGroupHeader || item.isGroupFooter)
865             {
866                 item.Index = -1;
867                 item.ParentItemsView = null;
868                 item.BindingContext = null; 
869                 item.IsPressed = false;
870                 item.IsSelected = false;
871                 item.IsEnabled = true;
872                 item.UpdateState();
873                 //item.Relayout -= OnItemRelayout;
874                 if (!recycle || !PushRecycleGroupCache(item))
875                     Utility.Dispose(item);
876                 return;
877             }
878
879             base.UnrealizeItem(item, recycle);
880         }
881
882         internal void SelectedItemsPropertyChanged(IList<object> oldSelection, IList<object> newSelection)
883         {
884             if (suppressSelectionChangeNotification)
885             {
886                 return;
887             }
888
889             foreach (RecyclerViewItem item in ContentContainer.Children.Where((item) => item is RecyclerViewItem))
890             {
891                 if (item.BindingContext == null) continue;
892                 if (newSelection.Contains(item.BindingContext))
893                 {
894                     if (!item.IsSelected) item.IsSelected = true;
895                 }
896                 else
897                 {
898                     if (item.IsSelected) item.IsSelected = false;
899                 }
900             }
901             SelectionPropertyChanged(this, new SelectionChangedEventArgs(oldSelection, newSelection));
902
903             OnPropertyChanged(SelectedItemsProperty.PropertyName);
904         }
905
906         /// <summary>
907         /// Internal selection callback.
908         /// </summary>
909         [EditorBrowsable(EditorBrowsableState.Never)]
910         protected virtual void OnSelectionChanged(SelectionChangedEventArgs args)
911         {
912             //Selection Callback
913         }
914
915         /// <summary>
916         /// Adjust scrolling position by own scrolling rules.
917         /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
918         /// </summary>
919         /// <param name="position">Scroll position which is calculated by ScrollableBase</param>
920         /// <returns>Adjusted scroll destination</returns>
921         [EditorBrowsable(EditorBrowsableState.Never)]
922         protected override float AdjustTargetPositionOfScrollAnimation(float position)
923         {
924             // Destination is depending on implementation of layout manager.
925             // Get destination from layout manager.
926             return ItemsLayouter.CalculateCandidateScrollPosition(position);
927         }
928
929         /// <summary>
930         /// OnScroll event callback.
931         /// </summary>
932         /// <param name="source">Scroll source object</param>
933         /// <param name="args">Scroll event argument</param>
934         [EditorBrowsable(EditorBrowsableState.Never)]
935         protected override void OnScrolling(object source, ScrollEventArgs args)
936         {
937             if (disposed) return;
938
939             if (needInitalizeLayouter)
940             {
941                 ItemsLayouter.Initialize(this);
942                 needInitalizeLayouter = false;
943             }
944
945             base.OnScrolling(source, args);
946         }
947
948         /// <summary>
949         /// Dispose ItemsView and all children on it.
950         /// </summary>
951         /// <param name="type">Dispose type.</param>
952         protected override void Dispose(DisposeTypes type)
953         {
954             if (disposed)
955             {
956                 return;
957             }
958
959             if (type == DisposeTypes.Explicit)
960             {
961                 disposed = true;
962
963                 // From now on, no need to use this properties,
964                 // so remove reference, to push it into garbage collector.
965
966                 if (InternalItemSource != null)
967                 {
968                     InternalItemSource.Dispose();
969                     InternalItemSource = null;
970                 }
971                 if (Header != null)
972                 {
973                     Utility.Dispose(Header);
974                     Header = null;
975                 }
976                 if (Footer != null)
977                 {
978                     Utility.Dispose(Footer);
979                     Footer = null;
980                 }
981                 groupHeaderTemplate = null;
982                 groupFooterTemplate = null;
983
984                 if (selectedItem != null) 
985                 {
986                     selectedItem = null;
987                 }
988                 if (selectedItems != null)
989                 {
990                     selectedItems.Clear();
991                     selectedItems = null;
992                 }
993             }
994
995             base.Dispose(type);
996         }
997
998         private static void SelectionPropertyChanged(CollectionView colView, SelectionChangedEventArgs args)
999         {
1000             var command = colView.SelectionChangedCommand;
1001
1002             if (command != null)
1003             {
1004                 var commandParameter = colView.SelectionChangedCommandParameter;
1005
1006                 if (command.CanExecute(commandParameter))
1007                 {
1008                     command.Execute(commandParameter);
1009                 }
1010             }
1011             colView.SelectionChanged?.Invoke(colView, args);
1012             colView.OnSelectionChanged(args);
1013         }
1014
1015         private static object CoerceSelectedItems(BindableObject bindable, object value)
1016         {
1017             if (value == null)
1018             {
1019                 return new SelectionList((CollectionView)bindable);
1020             }
1021
1022             if (value is SelectionList)
1023             {
1024                 return value;
1025             }
1026
1027             return new SelectionList((CollectionView)bindable, value as IList<object>);
1028         }
1029
1030         private static void SelectionModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
1031         {
1032             var colView = (CollectionView)bindable;
1033
1034             var oldMode = (ItemSelectionMode)oldValue;
1035             var newMode = (ItemSelectionMode)newValue;
1036
1037             IList<object> previousSelection = new List<object>();
1038             IList<object> newSelection = new List<object>();
1039
1040             switch (oldMode)
1041             {
1042                 case ItemSelectionMode.None:
1043                     break;
1044                 case ItemSelectionMode.Single:
1045                     if (colView.SelectedItem != null)
1046                     {
1047                         previousSelection.Add(colView.SelectedItem);
1048                     }
1049                     break;
1050                 case ItemSelectionMode.Multiple:
1051                     previousSelection = colView.SelectedItems;
1052                     break;
1053             }
1054
1055             switch (newMode)
1056             {
1057                 case ItemSelectionMode.None:
1058                     break;
1059                 case ItemSelectionMode.Single:
1060                     if (colView.SelectedItem != null)
1061                     {
1062                         newSelection.Add(colView.SelectedItem);
1063                     }
1064                     break;
1065                 case ItemSelectionMode.Multiple:
1066                     newSelection = colView.SelectedItems;
1067                     break;
1068             }
1069
1070             if (previousSelection.Count == newSelection.Count)
1071             {
1072                 if (previousSelection.Count == 0 || (previousSelection[0] == newSelection[0]))
1073                 {
1074                     // Both selections are empty or have the same single item; no reason to signal a change
1075                     return;
1076                 }
1077             }
1078
1079             var args = new SelectionChangedEventArgs(previousSelection, newSelection);
1080             SelectionPropertyChanged(colView, args);
1081         }
1082
1083         private void Init()
1084         {
1085             if (ItemsSource == null) return;
1086             if (ItemsLayouter == null) return;
1087             if (ItemTemplate == null) return;
1088
1089             if (disposed) return;
1090             if (needInitalizeLayouter)
1091             {
1092                 if (InternalItemSource == null) return;
1093
1094                 InternalItemSource.HasHeader = (header != null);
1095                 InternalItemSource.HasFooter = (footer != null);
1096             }
1097
1098             if (!wasRelayouted) return;
1099
1100             if (needInitalizeLayouter)
1101             {
1102                 ItemsLayouter.Initialize(this);
1103                 needInitalizeLayouter = false;
1104             }
1105             ItemsLayouter.RequestLayout(0.0f, true);
1106
1107             if (ScrollingDirection == Direction.Horizontal)
1108             {
1109                 ContentContainer.SizeWidth = ItemsLayouter.CalculateLayoutOrientationSize();
1110             }
1111             else
1112             {
1113                 ContentContainer.SizeHeight = ItemsLayouter.CalculateLayoutOrientationSize();
1114             }
1115         }
1116
1117         private bool PushRecycleGroupCache(RecyclerViewItem item)
1118         {
1119             if (item == null) throw new ArgumentNullException(nameof(item));
1120             if (RecycleCache.Count >= 20) return false;
1121             if (item.Template == null) return false;
1122             if (item.isGroupHeader)
1123             {
1124                 recycleGroupHeaderCache.Add(item);
1125             }
1126             else if (item.isGroupFooter)
1127             {
1128                 recycleGroupFooterCache.Add(item);
1129             }
1130             else return false;
1131             item.Hide();
1132             item.Index = -1;
1133             return true;
1134         }
1135
1136         private RecyclerViewItem PopRecycleGroupCache(DataTemplate Template, bool isHeader)
1137         {
1138             RecyclerViewItem viewItem = null;
1139
1140             var Cache = (isHeader ? recycleGroupHeaderCache : recycleGroupFooterCache);
1141             for (int i = 0; i < Cache.Count; i++)
1142             {
1143                 viewItem = Cache[i];
1144                 if (Template == viewItem.Template) break;
1145             }
1146
1147             if (viewItem != null)
1148             {
1149                 Cache.Remove(viewItem);
1150                 viewItem.Show();
1151             }
1152             return viewItem;
1153         }
1154         private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
1155         {
1156             switch (args.Action)
1157             {
1158                 case NotifyCollectionChangedAction.Add:
1159                     break;
1160                 case NotifyCollectionChangedAction.Remove:
1161                     // Clear removed items.
1162                     if (args.OldItems != null)
1163                     {
1164                         if (args.OldItems.Contains(selectedItem))
1165                         {
1166                             selectedItem = null;
1167                         }
1168                         
1169                         if (selectedItems != null)
1170                         {
1171                             foreach (object removed in args.OldItems)
1172                             {
1173                                 if (selectedItems.Contains(removed))
1174                                 {
1175                                     selectedItems.Remove(removed);
1176                                 }
1177                             }
1178                         }
1179                     }
1180                     break;
1181                 case NotifyCollectionChangedAction.Replace:
1182                     break;
1183                 case NotifyCollectionChangedAction.Move:
1184                     break;
1185                 case NotifyCollectionChangedAction.Reset:
1186                     break;
1187                 default:
1188                     throw new ArgumentOutOfRangeException(nameof(args));
1189             }
1190         }
1191
1192     }
1193 }