PageRenderTime 169ms CodeModel.GetById 84ms app.highlight 9ms RepoModel.GetById 43ms app.codeStats 0ms

/cinch/V1 (VS2008 WPF Only)/cinch/MVVM.Demo/UserControls/TabControlEx.cs

#
C# | 238 lines | 155 code | 27 blank | 56 comment | 28 complexity | d3d889a58124e907d90d4bfc65002fca MD5 | raw file
  1using System;
  2using System.Collections.Specialized;
  3using System.Windows;
  4using System.Windows.Controls;
  5using System.Windows.Controls.Primitives;
  6
  7namespace MVVM.Demo
  8{
  9    /// <summary>
 10    /// The standard WPF TabControl is quite bad in the fact that it only
 11    /// even contains the current TabItem in the VisualTree, so if you
 12    /// have complex views it takes a while to re-create the view each tab
 13    /// selection change.Which makes the standard TabControl very sticky to
 14    /// work with. This class along with its associated ControlTemplate
 15    /// allow all TabItems to remain in the VisualTree without it being Sticky.
 16    /// It does this by keeping all TabItem content in the VisualTree but
 17    /// hides all inactive TabItem content, and only keeps the active TabItem
 18    /// content shown.
 19    /// </summary>
 20    [TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
 21    public class TabControlEx : TabControl
 22    {
 23        #region Data
 24        private Panel itemsHolder = null;
 25        #endregion
 26
 27        #region Ctor
 28        public TabControlEx()
 29            : base()
 30        {
 31            // this is necessary so that we get the initial databound selected item
 32            this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
 33            this.Loaded += TabControlEx_Loaded;
 34        }
 35        #endregion
 36
 37        #region Public/Protected Methods
 38        /// <summary>
 39        /// get the ItemsHolder and generate any children
 40        /// </summary>
 41        public override void OnApplyTemplate()
 42        {
 43            base.OnApplyTemplate();
 44            itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel;
 45            UpdateSelectedItem();
 46        }
 47
 48        /// <summary>
 49        /// when the items change we remove any generated panel children and add any new ones as necessary
 50        /// </summary>
 51        /// <param name="e"></param>
 52        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
 53        {
 54            base.OnItemsChanged(e);
 55
 56            if (itemsHolder == null)
 57            {
 58                return;
 59            }
 60
 61            switch (e.Action)
 62            {
 63                case NotifyCollectionChangedAction.Reset:
 64                    itemsHolder.Children.Clear();
 65                    break;
 66
 67                case NotifyCollectionChangedAction.Add:
 68                case NotifyCollectionChangedAction.Remove:
 69                    if (e.OldItems != null)
 70                    {
 71                        foreach (var item in e.OldItems)
 72                        {
 73                            ContentPresenter cp = FindChildContentPresenter(item);
 74                            if (cp != null)
 75                            {
 76                                itemsHolder.Children.Remove(cp);
 77                            }
 78                        }
 79                    }
 80
 81                    // don't do anything with new items because we don't want to
 82                    // create visuals that aren't being shown
 83
 84                    UpdateSelectedItem();
 85                    break;
 86
 87                case NotifyCollectionChangedAction.Replace:
 88                    throw new NotImplementedException("Replace not implemented yet");
 89            }
 90        }
 91
 92        /// <summary>
 93        /// update the visible child in the ItemsHolder
 94        /// </summary>
 95        /// <param name="e"></param>
 96        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
 97        {
 98            base.OnSelectionChanged(e);
 99            UpdateSelectedItem();
100        }
101
102        /// <summary>
103        /// copied from TabControl; wish it were protected in that class instead of private
104        /// </summary>
105        /// <returns></returns>
106        protected TabItem GetSelectedTabItem()
107        {
108            object selectedItem = base.SelectedItem;
109            if (selectedItem == null)
110            {
111                return null;
112            }
113            TabItem item = selectedItem as TabItem;
114            if (item == null)
115            {
116                item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;
117            }
118            return item;
119        }
120        #endregion
121
122        #region Private Methods
123
124        /// <summary>
125        /// in some scenarios we need to update when loaded in case the 
126        /// ApplyTemplate happens before the databind.
127        /// </summary>
128        /// <param name="sender"></param>
129        /// <param name="e"></param>
130        private void TabControlEx_Loaded(object sender, RoutedEventArgs e)
131        {
132            UpdateSelectedItem();
133        }
134
135        /// <summary>
136        /// if containers are done, generate the selected item
137        /// </summary>
138        /// <param name="sender"></param>
139        /// <param name="e"></param>
140        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
141        {
142            if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
143            {
144                this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
145                UpdateSelectedItem();
146            }
147        }
148
149        /// <summary>
150        /// generate a ContentPresenter for the selected item
151        /// </summary>
152        private void UpdateSelectedItem()
153        {
154            if (itemsHolder == null)
155            {
156                return;
157            }
158
159            // generate a ContentPresenter if necessary
160            TabItem item = GetSelectedTabItem();
161            if (item != null)
162            {
163                CreateChildContentPresenter(item);
164            }
165
166            // show the right child
167            foreach (ContentPresenter child in itemsHolder.Children)
168            {
169                child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed;
170            }
171        }
172
173        /// <summary>
174        /// create the child ContentPresenter for the given item (could be data or a TabItem)
175        /// </summary>
176        /// <param name="item"></param>
177        /// <returns></returns>
178        private ContentPresenter CreateChildContentPresenter(object item)
179        {
180            if (item == null)
181            {
182                return null;
183            }
184
185            ContentPresenter cp = FindChildContentPresenter(item);
186
187            if (cp != null)
188            {
189                return cp;
190            }
191
192            // the actual child to be added.  cp.Tag is a reference to the TabItem
193            cp = new ContentPresenter();
194            cp.Content = (item is TabItem) ? (item as TabItem).Content : item;
195            cp.ContentTemplate = this.SelectedContentTemplate;
196            cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
197            cp.ContentStringFormat = this.SelectedContentStringFormat;
198            cp.Visibility = Visibility.Collapsed;
199            cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
200            itemsHolder.Children.Add(cp);
201            return cp;
202        }
203
204        /// <summary>
205        /// Find the CP for the given object.  data could be a TabItem or a piece of data
206        /// </summary>
207        /// <param name="data"></param>
208        /// <returns></returns>
209        private ContentPresenter FindChildContentPresenter(object data)
210        {
211            if (data is TabItem)
212            {
213                data = (data as TabItem).Content;
214            }
215
216            if (data == null)
217            {
218                return null;
219            }
220
221            if (itemsHolder == null)
222            {
223                return null;
224            }
225
226            foreach (ContentPresenter cp in itemsHolder.Children)
227            {
228                if (cp.Content == data)
229                {
230                    return cp;
231                }
232            }
233
234            return null;
235        }
236        #endregion
237    }
238}