[NUI] Add TabView and related classes
authorJaeun Choi <jaeun12.choi@samsung.com>
Tue, 6 Oct 2020 02:33:12 +0000 (11:33 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Tue, 15 Dec 2020 06:33:59 +0000 (15:33 +0900)
TabView and its related classes, TabBar, TabContent, TabButton and
TabButtonGroup, are added.

TabView consists of TabBar and TabContent.

TabBar consists of a list of TabButton.
TabContent consists of a list of View.

TabView.AddTab() adds TabButton and View to TabBar and TabContent in
TabView.
TabView.RemoveTab() removes TabButton and View from TabBar and
TabContent in TabView.

src/Tizen.NUI.Components/Controls/TabBar.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Controls/TabButton.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Controls/TabButtonGroup.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Controls/TabContent.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Controls/TabView.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TabViewSample.cs [new file with mode: 0644]

diff --git a/src/Tizen.NUI.Components/Controls/TabBar.cs b/src/Tizen.NUI.Components/Controls/TabBar.cs
new file mode 100755 (executable)
index 0000000..284fcae
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TabButtonSelectedEventArgs is a class to record tab button selected event
+    /// arguments which will be sent to a user.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabButtonSelectedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Creates a new instance of TabButtonSelectedEventArgs.
+        /// </summary>
+        /// <param name="index">The selected index.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabButtonSelectedEventArgs(int index)
+        {
+            Index = index;
+        }
+
+        /// <summary>
+        /// The index of the selected tab button.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int Index { get; }
+    }
+
+    /// <summary>
+    /// TabBar is a class which contains a set of TabButtons and has one of them selected.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabBar : Control
+    {
+        private IList<TabButton> tabButtons;
+
+        private TabButtonGroup tabButtonGroup;
+
+        //TODO: This tab button height should be implemented in TabBar style.
+        private float tabButtonHeight = 72.0f;
+
+        /// <summary>
+        /// Creates a new instance of TabBar.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabBar()
+        {
+            Layout = new LinearLayout() { LinearOrientation = LinearLayout.Orientation.Horizontal };
+
+            WidthSpecification = LayoutParamPolicies.MatchParent;
+
+            tabButtons = new List<TabButton>();
+            tabButtonGroup = new TabButtonGroup();
+            SelectedIndex = -1;
+        }
+
+        /// <summary>
+        /// An event for the tab button selected signal which can be used to
+        /// subscribe or unsubscribe the event handler provided by a user.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<TabButtonSelectedEventArgs> TabButtonSelected;
+
+        /// <summary>
+        /// The index of the selected tab button.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected int SelectedIndex { get; set; }
+
+        /// <summary>
+        /// Gets the count of tab buttons.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int TabButtonCount => tabButtons.Count;
+
+        /// <summary>
+        /// Adds a tab button to TabBar.
+        /// </summary>
+        /// <param name="tabButton">A tab button to be added to TabBar.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument tabButton is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal void AddTabButton(TabButton tabButton)
+        {
+            if (tabButton == null)
+            {
+                throw new ArgumentNullException(nameof(tabButton), "tabButton should not be null.");
+            }
+
+            tabButtons.Add(tabButton);
+            Add(tabButton);
+            tabButtonGroup.Add(tabButton);
+
+            tabButton.Clicked += (object sender, ClickedEventArgs e) =>
+            {
+                int index = tabButtons.IndexOf(tabButton);
+
+                if (SelectedIndex == index)
+                {
+                    return;
+                }
+
+                SelectedIndex = index;
+
+                if (TabButtonSelected != null)
+                {
+                    TabButtonSelectedEventArgs args = new TabButtonSelectedEventArgs(SelectedIndex);
+                    TabButtonSelected(this, args);
+                }
+            };
+
+            if (SelectedIndex == -1)
+            {
+                tabButton.IsSelected = true;
+                tabButton.SetTabButtonState(ControlState.Pressed);
+                SelectedIndex = 0;
+
+                if (TabButtonSelected != null)
+                {
+                    TabButtonSelectedEventArgs args = new TabButtonSelectedEventArgs(SelectedIndex);
+                    TabButtonSelected(this, args);
+                }
+            }
+
+            //TODO: To support non-unified tab button size.
+            CalculateUnifiedTabButtonSize();
+        }
+
+        /// <summary>
+        /// Removes a tab button from TabBar.
+        /// </summary>
+        /// <param name="tabButton">A tab button to be removed from TabBar.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument tabButton is null.</exception>
+        /// <exception cref="ArgumentException">Thrown when the argument tabButton does not exist in TabBar.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal void RemoveTabButton(TabButton tabButton)
+        {
+            if (tabButton == null)
+            {
+                throw new ArgumentNullException(nameof(tabButton), "tabButton should not be null.");
+            }
+
+            if (tabButtons.Contains(tabButton) == false)
+            {
+                throw new ArgumentException("tabButton does not exist in TabBar.", nameof(tabButton));
+            }
+
+            int index = tabButtons.IndexOf(tabButton);
+            TabButton selectedTabButton = tabButtons[SelectedIndex];
+
+            tabButtons.Remove(tabButton);
+            Remove(tabButton);
+            tabButtonGroup.Remove(tabButton);
+
+            if ((index < SelectedIndex) || (tabButtons.Count == SelectedIndex))
+            {
+                SelectedIndex -= 1;
+
+                if (TabButtonSelected != null)
+                {
+                    TabButtonSelectedEventArgs args = new TabButtonSelectedEventArgs(SelectedIndex);
+                    TabButtonSelected(this, args);
+                }
+            }
+
+            if ((SelectedIndex != -1) && (selectedTabButton != tabButtons[SelectedIndex]))
+            {
+                tabButtons[SelectedIndex].IsSelected = true;
+                tabButtons[SelectedIndex].SetTabButtonState(ControlState.Pressed);
+            }
+
+            //TODO: To support non-unified tab button size.
+            CalculateUnifiedTabButtonSize();
+        }
+
+        /// <inheritdoc/>
+        public override void OnRelayout(Vector2 size, RelayoutContainer container)
+        {
+            base.OnRelayout(size, container);
+
+            //TODO: To support non-unified tab button size.
+            CalculateUnifiedTabButtonSize();
+        }
+
+        private void CalculateUnifiedTabButtonSize()
+        {
+            if (tabButtons.Count <= 0)
+            {
+                return;
+            }
+
+            var tabButtonWidth = Size.Width / tabButtons.Count;
+
+            foreach (TabButton tabButton in tabButtons)
+            {
+                if ((tabButton.Size.Width != tabButtonWidth) || (tabButton.Size.Height != tabButtonHeight))
+                {
+                    tabButton.Size = new Size(tabButtonWidth, tabButtonHeight);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the tab button at the specified index of TabBar.
+        /// </summary>
+        /// <param name="index">The index of TabBar where the specified tab button exists.</param>
+        /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of tab buttons.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabButton GetTabButton(int index)
+        {
+            if ((index < 0) || (index >= tabButtons.Count))
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), "index should not be greater than or equal to 0, and less than the number of tab buttons.");
+            }
+
+            return tabButtons[index];
+        }
+
+        /// <summary>
+        /// Dispose TabBar and all children on it.
+        /// </summary>
+        /// <param name="type">Dispose type.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                if (tabButtons != null)
+                {
+                    foreach (TabButton tabButton in tabButtons)
+                    {
+                        Utility.Dispose(tabButton);
+                    }
+
+                    tabButtons = null;
+                }
+
+                tabButtonGroup = null;
+            }
+
+            base.Dispose(type);
+        }
+    }
+}
diff --git a/src/Tizen.NUI.Components/Controls/TabButton.cs b/src/Tizen.NUI.Components/Controls/TabButton.cs
new file mode 100755 (executable)
index 0000000..77f0476
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TabButton is a class which is used for selecting one content in a TabView.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabButton : SelectButton
+    {
+        /// <summary>
+        /// Creates a new instance of TabButton.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabButton()
+        {
+            GridLayout.SetHorizontalStretch(this, GridLayout.StretchFlags.ExpandAndFill);
+        }
+
+        /// <summary>
+        /// Sets the control state of a TabButton.
+        /// TabButton needs to support this API since 'View.ControlState' is protected
+        /// and cannot be reached by TabButtonGroup.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal void SetTabButtonState(ControlState controlState)
+        {
+            base.ControlState = controlState;
+        }
+
+        /// <summary>
+        /// Sets Button.IsSelected to true after selecting TabButton since it is
+        /// set to false when re-selected according to Button's logic.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void OnSelectedChanged()
+        {
+            ControlState = ControlState.Pressed;
+            if (!IsSelected)
+            {
+                IsSelected = true;
+            }
+        }
+    }
+}
diff --git a/src/Tizen.NUI.Components/Controls/TabButtonGroup.cs b/src/Tizen.NUI.Components/Controls/TabButtonGroup.cs
new file mode 100755 (executable)
index 0000000..f724c34
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TabButtonGroup class is used to group together a set of TabButton controls.
+    /// It enables a user to select exclusively single TabButton of a group.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabButtonGroup : SelectGroup
+    {
+        /// <summary>
+        /// Adds a tab button to the end of TabButtonGroup.
+        /// </summary>
+        /// <param name="tabButton">A tab button to be added to the group.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument tabButton is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Add(TabButton tabButton)
+        {
+            if (tabButton == null)
+            {
+                throw new ArgumentNullException(nameof(tabButton), "tabButton should not be null.");
+            }
+
+            base.AddSelection(tabButton);
+        }
+
+        /// <summary>
+        /// Removes a tab button from TabButtonGroup.
+        /// </summary>
+        /// <param name="tabButton">A tab button to be removed from the group.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument tabButton is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Remove(TabButton tabButton)
+        {
+            if (tabButton == null)
+            {
+                throw new ArgumentNullException(nameof(tabButton), "tabButton should not be null.");
+            }
+
+            base.RemoveSelection(tabButton);
+        }
+
+        /// <summary>
+        /// Sets the state of the rest of tab buttons in TabButtonGroup as normal
+        /// when a selection is made by a user.
+        /// </summary>
+        /// <param name="selection">The handler of the SelectButton selected by a user.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument selection is null.</exception>
+        /// <exception cref="ArgumentException">Thrown when the argument selection does not exist in TabButtonGroup.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void SelectionHandler(SelectButton selection)
+        {
+            TabButton selectedTab = selection as TabButton;
+
+            if (selectedTab == null)
+            {
+                throw new ArgumentNullException(nameof(selection), "selection should not be null.");
+            }
+
+            if (ItemGroup.Contains(selectedTab) == false)
+            {
+                throw new ArgumentException("selection does not exist in TabButtonGroup.", nameof(selection));
+            }
+
+            foreach (TabButton tabButton in ItemGroup)
+            {
+                if ((tabButton != null) && (tabButton != selectedTab) && (selectedTab.IsEnabled == true))
+                {
+                    tabButton.IsSelected = false;
+                    tabButton.SetTabButtonState(ControlState.Normal);
+                }
+            }
+        }
+    }
+}
diff --git a/src/Tizen.NUI.Components/Controls/TabContent.cs b/src/Tizen.NUI.Components/Controls/TabContent.cs
new file mode 100755 (executable)
index 0000000..d2e1838
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using System.Collections.Generic;
+using Tizen.NUI.BaseComponents;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TabContent is a class which contains a set of Views and has one of them selected.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabContent : Control
+    {
+        private IList<View> views;
+
+        /// <summary>
+        /// Creates a new instance of TabContent.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabContent()
+        {
+            SelectedIndex = -1;
+            views = new List<View>();
+        }
+
+        /// <summary>
+        /// The index of the selected view.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected int SelectedIndex { get; set; }
+
+        /// <summary>
+        /// Gets the count of views.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int ViewCount => views.Count;
+
+        /// <summary>
+        /// Adds a view to TabContent.
+        /// </summary>
+        /// <param name="view">A view to be added to TabContent.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument view is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal void AddView(View view)
+        {
+            if (view == null)
+            {
+                throw new ArgumentNullException(nameof(view), "view should not be null.");
+            }
+
+            views.Add(view);
+
+            if (SelectedIndex == -1)
+            {
+                Select(0);
+            }
+        }
+
+        /// <summary>
+        /// Removes a view from TabContent.
+        /// </summary>
+        /// <param name="view">A view to be removed from TabContent.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the argument view is null.</exception>
+        /// <exception cref="ArgumentException">Thrown when the argument view does not exist in TabContent.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal void RemoveView(View view)
+        {
+            if (view == null)
+            {
+                throw new ArgumentNullException(nameof(view), "view should not be null.");
+            }
+
+            if (views.Contains(view) == false)
+            {
+                throw new ArgumentException("view does not exist in TabContent.", nameof(view));
+            }
+
+            int index = views.IndexOf(view);
+
+            views.Remove(view);
+
+            if (index == SelectedIndex)
+            {
+                if (views.Count == 0)
+                {
+                    Select(-1);
+                }
+                else if (SelectedIndex == views.Count)
+                {
+                    Select(SelectedIndex - 1);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Selects a view at the specified index of TabContent.
+        /// </summary>
+        /// <param name="index">The index of TabContent where a view will be selected.</param>
+        /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than -1, or greater than or equal to the number of views.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal void Select(int index)
+        {
+            if ((index < -1) || (index >= views.Count))
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), "index should not be greater than or equal to -1, and less than the number of views.");
+            }
+
+            if (SelectedIndex != -1)
+            {
+                Remove(views[SelectedIndex]);
+            }
+
+            if (index != -1)
+            {
+                Add(views[index]);
+            }
+
+            SelectedIndex = index;
+        }
+
+        /// <summary>
+        /// Gets the view at the specified index of TabContent.
+        /// </summary>
+        /// <param name="index">The index of TabContent where the specified view exists.</param>
+        /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of views.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public View GetView(int index)
+        {
+            if ((index < 0) || (index >= views.Count))
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), "index should not be greater than or equal to 0, and less than the number of views.");
+            }
+
+            return views[index];
+        }
+
+        /// <summary>
+        /// Dispose TabContent and all children on it.
+        /// </summary>
+        /// <param name="type">Dispose type.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                if (views != null)
+                {
+                    foreach (View view in views)
+                    {
+                        Utility.Dispose(view);
+                    }
+
+                    views = null;
+                }
+            }
+
+            base.Dispose(type);
+        }
+    }
+}
diff --git a/src/Tizen.NUI.Components/Controls/TabView.cs b/src/Tizen.NUI.Components/Controls/TabView.cs
new file mode 100755 (executable)
index 0000000..0aa9d83
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TabView is a class which contains a TabBar and TabContent and
+    /// selects a view from the TabContent according to the selected TabButton in the TabBar.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TabView : Control
+    {
+        private TabBar tabBar = null;
+
+        private TabContent content = null;
+
+        /// <summary>
+        /// Creates a new instance of TabView.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabView()
+        {
+            Layout = new LinearLayout() { LinearOrientation = LinearLayout.Orientation.Vertical };
+            WidthSpecification = LayoutParamPolicies.MatchParent;
+            HeightSpecification = LayoutParamPolicies.MatchParent;
+
+            InitTabBar();
+            InitContent();
+        }
+
+        private void InitTabBar()
+        {
+            if (tabBar != null)
+            {
+                return;
+            }
+
+            tabBar = new TabBar();
+            tabBar.TabButtonSelected += tabButtonSelectedHandler;
+
+            Add(tabBar);
+        }
+
+        private void InitContent()
+        {
+            if (content != null)
+            {
+                return;
+            }
+
+            content = new TabContent();
+            content.WidthSpecification = LayoutParamPolicies.MatchParent;
+            content.HeightSpecification = LayoutParamPolicies.MatchParent;
+
+            Add(content);
+        }
+
+        private void tabButtonSelectedHandler(object sender, TabButtonSelectedEventArgs args)
+        {
+            if ((content != null) && (content.ViewCount > args.Index))
+            {
+                content.Select(args.Index);
+            }
+        }
+
+        /// <summary>
+        /// Gets TabBar of TabView.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabBar TabBar
+        {
+            get
+            {
+                return tabBar;
+            }
+        }
+
+        /// <summary>
+        /// Gets TabContent of TabView.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TabContent Content
+        {
+            get
+            {
+                return content;
+            }
+        }
+
+        /// <summary>
+        /// Adds a tab with tab button and content view.
+        /// </summary>
+        /// <param name="tabButton">A tab button to be added.</param>
+        /// <param name="view">A content view to be added.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void AddTab(TabButton tabButton, View view)
+        {
+            if (TabBar != null)
+            {
+                TabBar.AddTabButton(tabButton);
+            }
+
+            if (Content != null)
+            {
+                Content.AddView(view);
+            }
+        }
+
+        /// <summary>
+        /// Removes a tab at the specified index of TabView.
+        /// </summary>
+        /// <param name="index">The index of TabView where a tab will be removed.</param>
+        /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of tabs.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RemoveTab(int index)
+        {
+            var tabButton = TabBar.GetTabButton(index);
+            if (tabButton != null)
+            {
+                TabBar.RemoveTabButton(tabButton);
+            }
+
+            var view = Content.GetView(index);
+            if (view != null)
+            {
+                Content.RemoveView(view);
+            }
+        }
+
+        /// <summary>
+        /// Dispose TabView and all children on it.
+        /// </summary>
+        /// <param name="type">Dispose type.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                if (tabBar != null)
+                {
+                    tabBar.TabButtonSelected -= tabButtonSelectedHandler;
+                    Utility.Dispose(tabBar);
+                }
+
+                if (content != null)
+                {
+                    Utility.Dispose(content);
+                }
+            }
+
+            base.Dispose(type);
+        }
+    }
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TabViewSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TabViewSample.cs
new file mode 100644 (file)
index 0000000..772b857
--- /dev/null
@@ -0,0 +1,175 @@
+using System;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Samples
+{
+    public class TabViewSample : IExample
+    {
+        private TabView tabView;
+        private TabButton tabButton, tabButton2, tabButton3;
+        private View content, content2, content3;
+        private Button addBtn, removeBtn;
+        private int tabCount;
+
+        public void Activate()
+        {
+            var window = NUIApplication.GetDefaultWindow();
+
+            tabView = new TabView()
+            {
+                WidthResizePolicy = ResizePolicyType.FillToParent,
+                HeightResizePolicy = ResizePolicyType.FillToParent
+            };
+
+            tabCount = 0;
+
+            tabButton = new TabButton()
+            {
+                Text = "Tab#1"
+            };
+
+            content = new TextLabel()
+            {
+                Text = "Content#1",
+                BackgroundColor = Color.Red,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                HorizontalAlignment = HorizontalAlignment.Center,
+                VerticalAlignment = VerticalAlignment.Center
+            };
+
+            tabView.AddTab(tabButton, content);
+            tabCount++;
+
+            tabButton2 = new TabButton()
+            {
+                Text = "Tab#2"
+            };
+
+            content2 = new TextLabel()
+            {
+                Text = "Content#2",
+                BackgroundColor = Color.Green,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                HorizontalAlignment = HorizontalAlignment.Center,
+                VerticalAlignment = VerticalAlignment.Center
+            };
+
+            tabView.AddTab(tabButton2, content2);
+            tabCount++;
+
+            tabButton3 = new TabButton()
+            {
+                Text = "Tab#3"
+            };
+
+            content3 = new TextLabel()
+            {
+                Text = "Content#3",
+                BackgroundColor = Color.Blue,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                HeightSpecification = LayoutParamPolicies.MatchParent,
+                HorizontalAlignment = HorizontalAlignment.Center,
+                VerticalAlignment = VerticalAlignment.Center
+            };
+
+            tabView.AddTab(tabButton3, content3);
+            tabCount++;
+
+            window.Add(tabView);
+
+            addBtn = new Button()
+            {
+                Text = "+Tab",
+                Size = new Size(100, 72),
+                Position = new Position(80, 290)
+            };
+
+            addBtn.Clicked += (object sender, ClickedEventArgs args) =>
+            {
+                if (tabCount < 3)
+                {
+                    tabCount++;
+
+                    var newTabButton = new TabButton()
+                    {
+                        Text = "Tab#" + tabCount.ToString()
+                    };
+
+                    var newContent = new TextLabel()
+                    {
+                        Text = "Content#" + tabCount.ToString(),
+                        WidthSpecification = LayoutParamPolicies.MatchParent,
+                        HeightSpecification = LayoutParamPolicies.MatchParent,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        VerticalAlignment = VerticalAlignment.Center
+                    };
+
+                    switch (tabCount)
+                    {
+                        case 1:
+                            newContent.BackgroundColor = Color.Red;
+                            break;
+                        case 2:
+                            newContent.BackgroundColor = Color.Green;
+                            break;
+                        default:
+                            newContent.BackgroundColor = Color.Blue;
+                            break;
+                    }
+
+                    tabView.AddTab(newTabButton, newContent);
+                }
+            };
+
+            window.Add(addBtn);
+
+            removeBtn = new Button()
+            {
+                Text = "-Tab",
+                Size = new Size(100, 72),
+                Position = new Position(180, 290),
+            };
+
+            removeBtn.Clicked += (object sender, ClickedEventArgs args) =>
+            {
+                if (tabCount > 0)
+                {
+                    tabCount--;
+                    tabView.RemoveTab(tabCount);
+                }
+            };
+
+            window.Add(removeBtn);
+        }
+
+        public void Deactivate()
+        {
+            var window = NUIApplication.GetDefaultWindow();
+
+            if (tabView != null)
+            {
+                window.Remove(tabView);
+                tabView.Dispose();
+                tabView = null;
+            }
+
+            if (addBtn != null)
+            {
+                window.Remove(addBtn);
+                addBtn.Dispose();
+                addBtn = null;
+            }
+
+            if (removeBtn != null)
+            {
+                window.Remove(removeBtn);
+                removeBtn.Dispose();
+                removeBtn = null;
+            }
+        }
+    }
+}