Adding the logic of "An Extended WPF TabControl" to the next release?

Topics: Prism v4 - WPF 4
May 29, 2013 at 2:09 AM
Edited May 29, 2013 at 3:17 AM
Hi,

I download this source code "An Extended WPF TabControl" from the Code Project website and I was hoping to use it for the feature of having a single scrowing row. After studying the logic I found it wouldn't work with Prism Region Logic. So, there's need to be some coding changes to get it to work.

I believe this would be an added enhancement when adding Views to a TabControl. I do have the Prism 4.1 Source but, I'm not knowledgeable of the code and don't know where to start to implement this behavior.

Could this be added to one of your projects for future releases and/or could you direct me in the right direction to implement this myself? And, after successfully getting it work I could or some else could share it with the Prism Development Team?

The source is located in an assembly called Wpf.TabControl; you add it to your project and pretty much do the same thing as the AnimatedTabControl except the extended feature. The logic here is where TabItems are being added, but is done manually with its own featured add button:
        /// <summary>
        ///     Add a new Header
        /// </summary>
        public void AddTabItem()
        {
            if (IsFixedSize)
                throw new InvalidOperationException("ItemsSource is Fixed Size");

            int i = this.SelectedIndex;

            // give an opertunity to cancel the adding of the tabitem
            CancelEventArgs c = new CancelEventArgs();
            if (TabItemAdding != null)
                TabItemAdding(this, c);

            if (c.Cancel)
                return;

            TabItem tabItem;

            // Using ItemsSource property
            if (ItemsSource != null)
            {
                IList list = (IList)ItemsSource;
                NewTabItemEventArgs n = new NewTabItemEventArgs();
                if (NewTabItem == null)
                    throw new InvalidOperationException("You must implement the NewTabItem event to supply the item to be added to the tab control.");

                NewTabItem(this, n);
                if (n.Content == null)
                    return;

                if (i == -1 || i == list.Count - 1 || AddNewTabToEnd)
                    list.Add(n.Content);
                else
                    list.Insert(++i, n.Content);

                tabItem = (TabItem)this.ItemContainerGenerator.ContainerFromItem(n.Content);
            }
            else
            {
                // Using Items Property
                tabItem = new TabItem { Header = "New Tab" };

                if (i == -1 || i == this.Items.Count - 1 || AddNewTabToEnd)
                    this.Items.Add(tabItem);
                else
                    this.Items.Insert(++i, tabItem);
            }

            if (TabItemAdded != null)
                TabItemAdded(this, new TabItemEventArgs(tabItem));
        }
Here's is the logic for creating the Button for the AddTabItem:
...
..
.
       [TemplatePart(Name = "PART_NewTabButton", Type = typeof(ButtonBase))]
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

...
..
.
        private void SetAddNewButtonVisibility()
        {
            if (this.Template == null)
                return;

            ButtonBase button = this.Template.FindName("PART_NewTabButton", this) as ButtonBase;
            if (button == null) return;

            if (IsFixedSize)
                button.Visibility = Visibility.Collapsed;
            else
                button.Visibility = AllowAddNew
                                        ? Visibility.Visible
                                        : Visibility.Collapsed;
        }

....
...
.
            // set up the event handler for the 'New Tab' Button Click event
            _addNewButton = this.Template.FindName("PART_NewTabButton", this) as ButtonBase;
            if (_addNewButton != null)
                _addNewButton.Click += ((sender, routedEventArgs) => AddTabItem());
        }
I need an event that would trigger this logic to add the TabItem to its internal list in order to create the Scroll button. I will be removing the logic to create the tabs manually. So I need to understand how Prism creates tabs when adding a View to a Region.

Getting this to work with Prism would be a awesome feature to the TabControl and for the community.
May 29, 2013 at 7:24 PM
Hi,

Based on my understanding, the default implementation of TabControl in WPF inherits from Selector type. Therefore, Prism provides a specific region adapter for selectors, called SelectorRegionAdapter.

  • The SelectorRegionAdapter is in charge of creating the region, wiring up to the selector (in this case, your TabControl), and attach the SelectorItemsSourceSyncBehavior behavior.
  • The SelectorItemsSourceSyncBehavior is in charge of subscribing to the region events in order to react upon them and synchronize the Selector items with the region Views. The following code describes how this behavior add items to your Selector when Views are added to the region.
private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        int startIndex = e.NewStartingIndex;
        foreach (object newItem in e.NewItems)
        {
            this.hostControl.Items.Insert(startIndex++, newItem);
        }
    }
...
}
As you can see, when a View is added to the region, and that region is a Selector, a new item is added to the selector corresponding to that View.

You can find those classes in the Prism Library, under the namespaces Microsoft.Practices.Prism.Regions and Microsoft.Practices.Prism.Regions.Behaviors.

Hope this helps,

Federico Martinez
http://blogs.southworks.net/fmartinez
May 30, 2013 at 7:37 AM
Thanks for the reply!

I will dive into it and see if I can get this to work.