[NUI] Fix ScrollableBase Size Issue
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / ScrollableBase.cs
index c2dbd3e..0fa7250 100755 (executable)
  *
  */
 using System;
+using Tizen.NUI;
 using Tizen.NUI.BaseComponents;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
+using Tizen.NUI.Accessibility;
 
 namespace Tizen.NUI.Components
 {
     /// <summary>
     /// ScrollEventArgs is a class to record scroll event arguments which will sent to user.
     /// </summary>
-    /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 8 </since_tizen>
     public class ScrollEventArgs : EventArgs
     {
-        Position position;
+        private Position position;
 
         /// <summary>
         /// Default constructor.
         /// </summary>
         /// <param name="position">Current scroll position</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        /// <since_tizen> 8 </since_tizen>
         public ScrollEventArgs(Position position)
         {
             this.position = position;
         }
 
         /// <summary>
-        /// [Draft] Current scroll position.
+        /// Current position of ContentContainer.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Position Position
         {
             get
@@ -56,25 +56,68 @@ namespace Tizen.NUI.Components
     }
 
     /// <summary>
-    /// [Draft] This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
+    /// ScrollOutofBoundEventArgs is to record scroll out-of-bound event arguments which will be sent to user.
     /// </summary>
-    /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
     [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ScrollOutOfBoundEventArgs : EventArgs
+    {
+        /// <summary>
+        /// The bound to be scrolled out of.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public enum Bound
+        {
+            /// <summary>
+            /// Top bound.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Top,
+
+            /// <summary>
+            /// Bottom bound.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Bottom
+        }
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="bound">Current scrollable bound</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ScrollOutOfBoundEventArgs(Bound bound)
+        {
+            ScrollableBound = bound;
+        }
+
+        /// <summary>
+        /// Current position of ContentContainer.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Bound ScrollableBound
+        {
+            get;
+        }
+    }
+
+    /// <summary>
+    /// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
     public class ScrollableBase : Control
     {
         static bool LayoutDebugScrollableBase = false; // Debug flag
         private Direction mScrollingDirection = Direction.Vertical;
         private bool mScrollEnabled = true;
+        private int mScrollDuration = 125;
         private int mPageWidth = 0;
+        private float mPageFlickThreshold = 0.4f;
+        private float mScrollingEventThreshold = 0.00001f;
 
         private class ScrollableBaseCustomLayout : LayoutGroup
         {
             protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
             {
-                Extents padding = Padding;
-                float totalHeight = padding.Top + padding.Bottom;
-                float totalWidth = padding.Start + padding.End;
-
                 MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
                 MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
 
@@ -85,38 +128,30 @@ namespace Tizen.NUI.Components
                     scrollingDirection = scrollableBase.ScrollingDirection;
                 }
 
+                float totalWidth = 0.0f;
+                float totalHeight = 0.0f;
+
                 // measure child, should be a single scrolling child
                 foreach (LayoutItem childLayout in LayoutChildren)
                 {
-                    if (childLayout != null)
+                    if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
                     {
                         // Get size of child
                         // Use an Unspecified MeasureSpecification mode so scrolling child is not restricted to it's parents size in Height (for vertical scrolling)
                         // or Width for horizontal scrolling
-                        MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
-
                         if (scrollingDirection == Direction.Vertical)
                         {
-                            MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0));  // Height unrestricted by parent
+                            MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+                            MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0)); // Height unrestricted by parent
                         }
                         else
                         {
-                            MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));  // Width unrestricted by parent
+                            MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(widthMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+                            MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); // Width unrestricted by parent
                         }
 
-                        float childWidth = childLayout.MeasuredWidth.Size.AsDecimal();
-                        float childHeight = childLayout.MeasuredHeight.Size.AsDecimal();
-
-                        // Determine the width and height needed by the children using their given position and size.
-                        // Children could overlap so find the left most and right most child.
-                        Position2D childPosition = childLayout.Owner.Position2D;
-                        float childLeft = childPosition.X;
-                        float childTop = childPosition.Y;
-
-                        // Store current width and height needed to contain all children.
-                        Extents childMargin = childLayout.Margin;
-                        totalWidth = childWidth + childMargin.Start + childMargin.End;
-                        totalHeight = childHeight + childMargin.Top + childMargin.Bottom;
+                        totalWidth = childLayout.MeasuredWidth.Size.AsDecimal();
+                        totalHeight = childLayout.MeasuredHeight.Size.AsDecimal();
 
                         if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
                         {
@@ -129,9 +164,8 @@ namespace Tizen.NUI.Components
                     }
                 }
 
-
-                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
-                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
                 totalWidth = widthSizeAndState.Size.AsDecimal();
                 totalHeight = heightSizeAndState.Size.AsDecimal();
 
@@ -142,28 +176,27 @@ namespace Tizen.NUI.Components
                 widthSizeAndState.State = childWidthState;
                 heightSizeAndState.State = childHeightState;
 
-                SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, childWidthState),
-                                       ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childHeightState));
+                SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, childWidthState),
+                    ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, childHeightState));
 
                 // Size of ScrollableBase is changed. Change Page width too.
                 scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
+                scrollableBase.OnScrollingChildRelayout(null, null);
             }
 
             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
             {
                 foreach (LayoutItem childLayout in LayoutChildren)
                 {
-                    if (childLayout != null)
+                    if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
                     {
                         LayoutLength childWidth = childLayout.MeasuredWidth.Size;
                         LayoutLength childHeight = childLayout.MeasuredHeight.Size;
 
                         Position2D childPosition = childLayout.Owner.Position2D;
-                        Extents padding = Padding;
-                        Extents childMargin = childLayout.Margin;
 
-                        LayoutLength childLeft = new LayoutLength(childPosition.X + childMargin.Start + padding.Start);
-                        LayoutLength childTop = new LayoutLength(childPosition.Y + childMargin.Top + padding.Top);
+                        LayoutLength childLeft = new LayoutLength(childPosition.X);
+                        LayoutLength childTop = new LayoutLength(childPosition.Y);
 
                         childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
                     }
@@ -174,30 +207,27 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// The direction axis to scroll.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public enum Direction
         {
             /// <summary>
             /// Horizontal axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Horizontal,
 
             /// <summary>
             /// Vertical axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Vertical
         }
 
         /// <summary>
-        /// [Draft] Scrolling direction mode.
+        /// Scrolling direction mode.
         /// Default is Vertical scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Direction ScrollingDirection
         {
             get
@@ -209,24 +239,20 @@ namespace Tizen.NUI.Components
                 if (value != mScrollingDirection)
                 {
                     mScrollingDirection = value;
-                    mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ?
-                        PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
+                    mPanGestureDetector.ClearAngles();
                     mPanGestureDetector.AddDirection(value == Direction.Horizontal ?
                         PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
 
-                    ContentContainer.WidthSpecification = mScrollingDirection == Direction.Vertical ?
-                        LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent;
-                    ContentContainer.HeightSpecification = mScrollingDirection == Direction.Vertical ?
-                        LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent;
+                    ContentContainer.WidthSpecification = LayoutParamPolicies.WrapContent;
+                    ContentContainer.HeightSpecification = LayoutParamPolicies.WrapContent;
                 }
             }
         }
 
         /// <summary>
-        /// [Draft] Enable or disable scrolling.
+        /// Enable or disable scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool ScrollEnabled
         {
             get
@@ -251,78 +277,82 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// [Draft] Pages mode, enables moving to the next or return to current page depending on pan displacement.
+        /// Pages mode, enables moving to the next or return to current page depending on pan displacement.
         /// Default is false.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool SnapToPage { set; get; } = false;
 
         /// <summary>
-        /// [Draft] Get current page.
-        /// Working propery with SnapToPage property.
+        /// Get current page.
+        /// Working property with SnapToPage property.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public int CurrentPage { get; private set; } = 0;
 
         /// <summary>
-        /// [Draft] Duration of scroll animation.
+        /// Duration of scroll animation.
+        /// Default value is 125ms.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
+        public int ScrollDuration
+        {
+            set
+            {
+                mScrollDuration = value >= 0 ? value : mScrollDuration;
+            }
+            get
+            {
+                return mScrollDuration;
+            }
+        }
 
-        public int ScrollDuration { set; get; } = 125;
         /// <summary>
-        /// [Draft] Scroll Available area.
+        /// Scroll Available area.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Vector2 ScrollAvailableArea { set; get; }
 
         /// <summary>
         /// An event emitted when user starts dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollDragStarted;
 
         /// <summary>
         /// An event emitted when user stops dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollDragEnded;
 
-
         /// <summary>
         /// An event emitted when the scrolling slide animation starts, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollAnimationStarted;
 
         /// <summary>
         /// An event emitted when the scrolling slide animation ends, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollAnimationEnded;
 
-
         /// <summary>
         /// An event emitted when scrolling, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> Scrolling;
 
-
         /// <summary>
-        /// Scrollbar for ScrollableBase.<br />
+        /// An event emitted when scrolling out of bound, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
         [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<ScrollOutOfBoundEventArgs> ScrollOutOfBound;
+
+        /// <summary>
+        /// Scrollbar for ScrollableBase.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
         public ScrollbarBase Scrollbar
         {
             get
@@ -335,30 +365,32 @@ namespace Tizen.NUI.Components
                 {
                     scrollBar.Unparent();
                 }
-
                 scrollBar = value;
-                scrollBar.Name = "ScrollBar";
-                base.Add(scrollBar);
 
-                if (hideScrollbar)
-                {
-                    scrollBar.Hide();
-                }
-                else
+                if (scrollBar != null)
                 {
-                    scrollBar.Show();
-                }
+                    scrollBar.Name = "ScrollBar";
+                    base.Add(scrollBar);
 
-                SetScrollbar();
+                    if (hideScrollbar)
+                    {
+                        scrollBar.Hide();
+                    }
+                    else
+                    {
+                        scrollBar.Show();
+                    }
+
+                    SetScrollbar();
+                }
             }
         }
 
         /// <summary>
-        /// [Draft] Always hide Scrollbar.
+        /// Always hide Scrollbar.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public bool HideScrollBar
+        /// <since_tizen> 8 </since_tizen>
+        public bool HideScrollbar
         {
             get
             {
@@ -385,13 +417,13 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// Container which has content of ScrollableBase.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public View ContentContainer { get; private set; }
 
         /// <summary>
         /// Set the layout on this View. Replaces any existing Layout.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public new LayoutItem Layout
         {
             get
@@ -403,7 +435,7 @@ namespace Tizen.NUI.Components
                 ContentContainer.Layout = value;
                 if (ContentContainer.Layout != null)
                 {
-                    ContentContainer.Layout.SetPositionByLayout = false;
+                    ContentContainer.Layout.SetPositionByLayout = true;
                 }
             }
         }
@@ -411,7 +443,7 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// List of children of Container.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public new List<View> Children
         {
             get
@@ -422,9 +454,10 @@ namespace Tizen.NUI.Components
 
         /// <summary>
         /// Deceleration rate of scrolling by finger.
-        /// Rate should be 0 < rate < 1.
+        /// Rate should be bigger than 0 and smaller than 1.
+        /// Default value is 0.998f;
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public float DecelerationRate
         {
             get
@@ -433,7 +466,7 @@ namespace Tizen.NUI.Components
             }
             set
             {
-                decelerationRate = value;
+                decelerationRate = (value < 1 && value > 0) ? value : decelerationRate;
                 logValueOfDeceleration = (float)Math.Log(value);
             }
         }
@@ -444,6 +477,67 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         public float DecelerationThreshold { get; set; } = 0.1f;
 
+        /// <summary>
+        /// Scrolling event will be thrown when this amount of scroll positino is changed.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float ScrollingEventThreshold
+        {
+            get
+            {
+                return mScrollingEventThreshold;
+            }
+            set
+            {
+                if (mScrollingEventThreshold != value && value > 0)
+                {
+                    ContentContainer.RemovePropertyNotification(propertyNotification);
+                    propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(value));
+                    propertyNotification.Notified += OnPropertyChanged;
+                    mScrollingEventThreshold = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Page will be changed when velocity of panning is over threshold.
+        /// The unit of threshold is pixel per milisec.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public float PageFlickThreshold
+        {
+            get
+            {
+                return mPageFlickThreshold;
+            }
+            set
+            {
+                mPageFlickThreshold = value >= 0f ? value : mPageFlickThreshold;
+            }
+        }
+
+        /// <summary>
+        /// Padding for the ScrollableBase
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Extents Padding
+        {
+            get
+            {
+                return ContentContainer.Padding;
+            }
+            set
+            {
+                ContentContainer.Padding = value;
+            }
+        }
+
+        /// <summary>
+        /// Alphafunction for scroll animation.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AlphaFunction ScrollAlphaFunction { get; set; } = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+
         private bool hideScrollbar = true;
         private float maxScrollDistance;
         private float childTargetPosition = 0.0f;
@@ -454,16 +548,21 @@ namespace Tizen.NUI.Components
         private float ratioOfScreenWidthToCompleteScroll = 0.5f;
         private float totalDisplacementForPan = 0.0f;
         private Size previousContainerSize = new Size();
-
-        // If false then can only flick pages when the current animation/scroll as ended.
-        private bool flickWhenAnimating = false;
+        private Size previousSize = new Size();
         private PropertyNotification propertyNotification;
-
         private float noticeAnimationEndBeforePosition = 0.0f;
         private bool readyToNotice = false;
-        // Let's consider more whether this needs to be set as protected.
-        public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
 
+        /// <summary>
+        /// Notice before animation is finished.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        // Let's consider more whether this needs to be set as protected.
+        public float NoticeAnimationEndBeforePosition
+        {
+            get => noticeAnimationEndBeforePosition;
+            set => noticeAnimationEndBeforePosition = value;
+        }
 
         // Let's consider more whether this needs to be set as protected.
         private float finalTargetPosition;
@@ -479,12 +578,18 @@ namespace Tizen.NUI.Components
         private float logValueOfDeceleration = 0.0f;
         private float decelerationRate = 0.0f;
 
+        private View verticalTopShadowView;
+        private View verticalBottomShadowView;
+        private const int verticalShadowScaleHeightLimit = 64 * 3;
+        private const int verticalShadowAnimationDuration = 300;
+        private Animation verticalShadowAnimation;
+        private bool isVerticalShadowShown = false;
+        private float startShowShadowDisplacement;
 
         /// <summary>
-        /// [Draft] Constructor
+        /// Default Constructor
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public ScrollableBase() : base()
         {
             DecelerationRate = 0.998f;
@@ -500,12 +605,16 @@ namespace Tizen.NUI.Components
             //Default Scrolling child
             ContentContainer = new View()
             {
-                WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
-                HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
-                Layout = new AbsoluteLayout() { SetPositionByLayout = false },
+                Name = "ContentContainer",
+                WidthSpecification = LayoutParamPolicies.WrapContent,
+                HeightSpecification = LayoutParamPolicies.WrapContent,
+                Layout = new LinearLayout()
+                {
+                    SetPositionByLayout = true
+                },
             };
             ContentContainer.Relayout += OnScrollingChildRelayout;
-            propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(1.0f));
+            propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
             propertyNotification.Notified += OnPropertyChanged;
             base.Add(ContentContainer);
 
@@ -517,6 +626,28 @@ namespace Tizen.NUI.Components
             };
             mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
             Scrollbar = new Scrollbar();
+
+            //Show vertical shadow on the top (or bottom) of the scrollable when panning down (or up).
+            verticalTopShadowView = new View
+            {
+                BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_top.png",
+                Opacity = 1.0f,
+                SizeHeight = 0.0f,
+                PositionUsesPivotPoint = true,
+                ParentOrigin = NUI.ParentOrigin.TopCenter,
+                PivotPoint = NUI.PivotPoint.TopCenter,
+            };
+            verticalBottomShadowView = new View
+            {
+                BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_bottom.png",
+                Opacity = 1.0f,
+                SizeHeight = 0.0f,
+                PositionUsesPivotPoint = true,
+                ParentOrigin = NUI.ParentOrigin.BottomCenter,
+                PivotPoint = NUI.PivotPoint.BottomCenter,
+            };
+
+            AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "ScrollableBase");
         }
 
         private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
@@ -540,8 +671,7 @@ namespace Tizen.NUI.Components
         /// Called after a child has been added to the owning view.
         /// </summary>
         /// <param name="view">The child which has been added.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public override void Add(View view)
         {
             ContentContainer.Add(view);
@@ -551,8 +681,7 @@ namespace Tizen.NUI.Components
         /// Called after a child has been removed from the owning view.
         /// </summary>
         /// <param name="view">The child which has been removed.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public override void Remove(View view)
         {
             if (SnapToPage && CurrentPage == Children.IndexOf(view) && CurrentPage == Children.Count - 1)
@@ -569,7 +698,8 @@ namespace Tizen.NUI.Components
         private void OnScrollingChildRelayout(object source, EventArgs args)
         {
             // Size is changed. Calculate maxScrollDistance.
-            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height;
+            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height ||
+                previousSize.Width != Size.Width || previousSize.Height != Size.Height;
 
             if (isSizeChanged)
             {
@@ -578,13 +708,14 @@ namespace Tizen.NUI.Components
             }
 
             previousContainerSize = ContentContainer.Size;
+            previousSize = Size;
         }
 
         /// <summary>
-        /// The composition of a Scrollbar can vary depending on how you use ScrollableBase. 
+        /// The composition of a Scrollbar can vary depending on how you use ScrollableBase.
         /// Set the composition that will go into the ScrollableBase according to your ScrollableBase.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        /// <since_tizen> 8 </since_tizen>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected virtual void SetScrollbar()
         {
@@ -602,8 +733,7 @@ namespace Tizen.NUI.Components
         /// Scrolls to the item at the specified index.
         /// </summary>
         /// <param name="index">Index of item.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public void ScrollToIndex(int index)
         {
             if (ContentContainer.ChildCount - 1 < index || index < 0)
@@ -656,7 +786,7 @@ namespace Tizen.NUI.Components
             float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
             float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
 
-            scrollBar.Update(contentLength, Math.Abs(currentPosition));
+            scrollBar?.Update(contentLength, Math.Abs(currentPosition));
             CheckPreReachedTargetPosition();
         }
 
@@ -698,19 +828,6 @@ namespace Tizen.NUI.Components
             }
         }
 
-        // static constructor registers the control type
-        static ScrollableBase()
-        {
-            // ViewRegistry registers control type with DALi type registry
-            // also uses introspection to find any properties that need to be registered with type registry
-            CustomViewRegistry.Instance.Register(CreateInstance, typeof(ScrollableBase));
-        }
-
-        internal static CustomView CreateInstance()
-        {
-            return new ScrollableBase();
-        }
-
         private void AnimateChildTo(int duration, float axisPosition)
         {
             Debug.WriteLineIf(LayoutDebugScrollableBase, "AnimationTo Animation Duration:" + duration + " Destination:" + axisPosition);
@@ -726,7 +843,7 @@ namespace Tizen.NUI.Components
 
             scrollAnimation.Duration = duration;
             scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
-            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition);
+            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition, ScrollAlphaFunction);
             scrolling = true;
             OnScrollAnimationStarted();
             scrollAnimation.Play();
@@ -737,7 +854,7 @@ namespace Tizen.NUI.Components
         /// </summary>
         /// <param name="position">Destination.</param>
         /// <param name="animate">Scroll with or without animation</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public void ScrollTo(float position, bool animate)
         {
             float currentPositionX = ContentContainer.CurrentPosition.X != 0 ? ContentContainer.CurrentPosition.X : ContentContainer.Position.X;
@@ -779,12 +896,11 @@ namespace Tizen.NUI.Components
             float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? ContentContainer.PositionX : ContentContainer.PositionY;
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy childCurrentPosition:" + childCurrentPosition +
-                                                   " displacement:" + displacement,
-                                                   " maxScrollDistance:" + maxScrollDistance);
+                " displacement:" + displacement,
+                " maxScrollDistance:" + maxScrollDistance);
 
             childTargetPosition = childCurrentPosition + displacement; // child current position + gesture displacement
 
-
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
 
             if (animate)
@@ -808,7 +924,6 @@ namespace Tizen.NUI.Components
                 {
                     ContentContainer.PositionY = finalTargetPosition;
                 }
-
             }
         }
 
@@ -827,6 +942,7 @@ namespace Tizen.NUI.Components
 
             if (type == DisposeTypes.Explicit)
             {
+                StopVerticalShadowAnimation();
                 StopScroll();
 
                 if (mPanGestureDetector != null)
@@ -835,6 +951,8 @@ namespace Tizen.NUI.Components
                     mPanGestureDetector.Dispose();
                     mPanGestureDetector = null;
                 }
+
+                propertyNotification.Dispose();
             }
             base.Dispose(type);
         }
@@ -858,16 +976,16 @@ namespace Tizen.NUI.Components
             }
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy maxScrollDistance:" + (scrollingChildLength - scrollerLength) +
-                                                   " parent length:" + scrollerLength +
-                                                   " scrolling child length:" + scrollingChildLength);
+                " parent length:" + scrollerLength +
+                " scrolling child length:" + scrollingChildLength);
 
             return Math.Max(scrollingChildLength - scrollerLength, 0);
         }
 
-        private void PageSnap()
+        private void PageSnap(float velocity)
         {
             Debug.WriteLineIf(LayoutDebugScrollableBase, "PageSnap with pan candidate totalDisplacement:" + totalDisplacementForPan +
-                                                                " currentPage[" + CurrentPage + "]");
+                " currentPage[" + CurrentPage + "]");
 
             //Increment current page if total displacement enough to warrant a page change.
             if (Math.Abs(totalDisplacementForPan) > (mPageWidth * ratioOfScreenWidthToCompleteScroll))
@@ -881,6 +999,17 @@ namespace Tizen.NUI.Components
                     CurrentPage = Math.Max(0, --CurrentPage);
                 }
             }
+            else if (Math.Abs(velocity) > PageFlickThreshold)
+            {
+                if (velocity < 0)
+                {
+                    CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), ++CurrentPage);
+                }
+                else
+                {
+                    CurrentPage = Math.Max(0, --CurrentPage);
+                }
+            }
 
             // Animate to new page or reposition to current page
             float destinationX = -(Children[CurrentPage].Position.X + Children[CurrentPage].CurrentSize.Width / 2 - CurrentSize.Width / 2); // set to middle of current page
@@ -888,12 +1017,142 @@ namespace Tizen.NUI.Components
             AnimateChildTo(ScrollDuration, destinationX);
         }
 
+        private void AttachShadowView()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            base.Add(verticalTopShadowView);
+            base.Add(verticalBottomShadowView);
+
+            verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalTopShadowView.Opacity = 1.0f;
+
+            verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalBottomShadowView.Opacity = 1.0f;
+
+            // at the beginning, height of vertical shadow is 0, so it is invisible.
+            isVerticalShadowShown = false;
+        }
+
+        private void DragVerticalShadow(float displacement)
+        {
+            if ((int)displacement > 0) // downwards
+            {
+                // check if reaching at the top.
+                if ((int)finalTargetPosition != 0)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    startShowShadowDisplacement = displacement;
+                    OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Top);
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = (int)displacement < (int)startShowShadowDisplacement ? 0 : displacement - startShowShadowDisplacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+                verticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                verticalTopShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else if ((int)displacement < 0) // upwards
+            {
+                // check if reaching at the bottom.
+                if (-(int)finalTargetPosition != (int)maxScrollDistance)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    startShowShadowDisplacement = displacement;
+                    OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Bottom);
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = (int)startShowShadowDisplacement < (int)displacement ? 0 : startShowShadowDisplacement - displacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+                verticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                verticalBottomShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else
+            {
+                // if total displacement is 0, shadow would become invisible.
+                isVerticalShadowShown = false;
+            }
+        }
+
+        private void PlayVerticalShadowAnimation()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            if (verticalShadowAnimation == null)
+            {
+                verticalShadowAnimation = new Animation(verticalShadowAnimationDuration);
+                verticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
+            }
+
+            View targetView = totalDisplacementForPan < 0 ? verticalBottomShadowView : verticalTopShadowView;
+            verticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
+            verticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
+            verticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
+            verticalShadowAnimation.Play();
+        }
+
+        private void StopVerticalShadowAnimation()
+        {
+            if (verticalShadowAnimation == null || verticalShadowAnimation.State != Animation.States.Playing)
+                return;
+
+            verticalShadowAnimation.Stop(Animation.EndActions.Cancel);
+            OnVerticalShadowAnimationFinished(null, null);
+            verticalShadowAnimation.Clear();
+        }
+
+        private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
+        {
+            base.Remove(verticalTopShadowView);
+            base.Remove(verticalBottomShadowView);
+
+            verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+
+            // after animation finished, height & opacity of vertical shadow both are 0, so it is invisible.
+            isVerticalShadowShown = false;
+        }
+
+        private void OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound bound)
+        {
+            ScrollOutOfBoundEventArgs args = new ScrollOutOfBoundEventArgs(bound);
+            ScrollOutOfBound?.Invoke(this, args);
+        }
+
         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
         {
-            if (e.PanGesture.State == Gesture.StateType.Started)
+            OnPanGesture(e.PanGesture);
+        }
+
+        private void OnPanGesture(PanGesture panGesture)
+        {
+            if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
+            {
+                return;
+            }
+
+            if (panGesture.State == Gesture.StateType.Started)
             {
                 readyToNotice = false;
                 base.Add(mInterruptTouchingChild);
+                AttachShadowView();
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Gesture Start");
                 if (scrolling && !SnapToPage)
                 {
@@ -902,22 +1161,28 @@ namespace Tizen.NUI.Components
                 totalDisplacementForPan = 0.0f;
                 OnScrollDragStarted();
             }
-            else if (e.PanGesture.State == Gesture.StateType.Continuing)
+            else if (panGesture.State == Gesture.StateType.Continuing)
             {
                 if (ScrollingDirection == Direction.Horizontal)
                 {
-                    ScrollBy(e.PanGesture.Displacement.X, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.X;
+                    ScrollBy(panGesture.Displacement.X, false);
+                    totalDisplacementForPan += panGesture.Displacement.X;
                 }
                 else
                 {
-                    ScrollBy(e.PanGesture.Displacement.Y, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.Y;
+                    // if vertical shadow is shown, does not scroll.
+                    if (!isVerticalShadowShown)
+                    {
+                        ScrollBy(panGesture.Displacement.Y, false);
+                    }
+                    totalDisplacementForPan += panGesture.Displacement.Y;
+                    DragVerticalShadow(totalDisplacementForPan);
                 }
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "OnPanGestureDetected Continue totalDisplacementForPan:" + totalDisplacementForPan);
             }
-            else if (e.PanGesture.State == Gesture.StateType.Finished)
+            else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
             {
+                PlayVerticalShadowAnimation();
                 OnScrollDragEnded();
                 StopScroll(); // Will replace previous animation so will stop existing one.
 
@@ -927,13 +1192,26 @@ namespace Tizen.NUI.Components
                     scrollAnimation.Finished += ScrollAnimationFinished;
                 }
 
+                float panVelocity = (ScrollingDirection == Direction.Horizontal) ? panGesture.Velocity.X : panGesture.Velocity.Y;
+
                 if (SnapToPage)
                 {
-                    PageSnap();
+                    PageSnap(panVelocity);
                 }
                 else
                 {
-                    Decelerating((ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y);
+                    if (panVelocity == 0)
+                    {
+                        float currentScrollPosition = (ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+                        scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+                        scrollAnimation.Duration = 0;
+                        scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", currentScrollPosition);
+                        scrollAnimation.Play();
+                    }
+                    else
+                    {
+                        Decelerating(panVelocity, scrollAnimation);
+                    }
                 }
 
                 totalDisplacementForPan = 0;
@@ -943,6 +1221,17 @@ namespace Tizen.NUI.Components
             }
         }
 
+        internal override bool OnAccessibilityPan(PanGesture gestures)
+        {
+            if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
+            {
+                return false;
+            }
+
+            OnPanGesture(gestures);
+            return true;
+        }
+
         private float CustomScrollAlphaFunction(float progress)
         {
             if (panAnimationDelta == 0)
@@ -961,7 +1250,13 @@ namespace Tizen.NUI.Components
             }
         }
 
-        private void Decelerating(float velocity)
+        /// <summary>
+        /// you can override it to custom your decelerating
+        /// </summary>
+        /// <param name="velocity">Velocity of current pan.</param>
+        /// <param name="animation">Scroll animation.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Decelerating(float velocity, Animation animation)
         {
             // Decelerating using deceleration equation ===========
             //
@@ -977,7 +1272,7 @@ namespace Tizen.NUI.Components
             // X(∞) = V0 * d / (1 - d); <-- Result using inifit T can be final position because T is tending to infinity.
             //
             // Because of final T is tending to inifity, we should use threshold value to finish.
-            // Final T = log(-threshold * log d / |V0| ) / log d; 
+            // Final T = log(-threshold * log d / |V0| ) / log d;
 
             velocityOfLastPan = Math.Abs(velocity);
 
@@ -1044,16 +1339,11 @@ namespace Tizen.NUI.Components
             finalTargetPosition = destination;
 
             customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
-            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
+            animation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
             GC.KeepAlive(customScrollAlphaFunction);
-            scrollAnimation.Duration = (int)panAnimationDuration;
-            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
-            scrollAnimation.Play();
-        }
-
-        protected void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
-        {
-
+            animation.Duration = (int)panAnimationDuration;
+            animation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
+            animation.Play();
         }
 
         private void ScrollAnimationFinished(object sender, EventArgs e)
@@ -1072,6 +1362,65 @@ namespace Tizen.NUI.Components
             return position;
         }
 
+        /// <summary>
+        /// Scroll position given to ScrollTo.
+        /// This is the position in the opposite direction to the position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.Position);
+            }
+        }
+
+        /// <summary>
+        /// Current scroll position in the middle of ScrollTo animation.
+        /// This is the position in the opposite direction to the current position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollCurrentPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.CurrentPosition);
+            }
+        }
+
+        /// <summary>
+        /// Remove all children in ContentContainer.
+        /// </summary>
+        /// <param name="dispose">If true, removed child is disposed.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RemoveAllChildren(bool dispose = false)
+        {
+            RecursiveRemoveChildren(ContentContainer, dispose);
+        }
+
+        private void RecursiveRemoveChildren(View parent, bool dispose)
+        {
+            if (parent == null)
+            {
+                return;
+            }
+            int maxChild = (int)parent.GetChildCount();
+            for (int i = maxChild - 1; i >= 0; --i)
+            {
+                View child = parent.GetChildAt((uint)i);
+                if (child == null)
+                {
+                    continue;
+                }
+                RecursiveRemoveChildren(child, dispose);
+                parent.Remove(child);
+                if (dispose)
+                {
+                    child.Dispose();
+                }
+            }
+        }
+
     }
 
 } // namespace