ItemsControl.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / ItemsControl.cs / 2 / ItemsControl.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Collections.ObjectModel;
using System.Collections.Specialized; 
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Threading;
using System.Windows.Controls.Primitives; 
using System.Windows.Data;
using System.Windows; 
using System.Windows.Media; 
using System.Windows.Markup;
using System.Windows.Input; 

using MS.Utility;
using MS.Internal;
using MS.Internal.Controls; 
using MS.Internal.Data;
using MS.Internal.KnownBoxes; 
using MS.Internal.PresentationFramework; 

namespace System.Windows.Controls 
{
    /// 
    ///     The base class for all controls that have multiple children.
    ///  
    /// 
    ///     ItemsControl adds Items, ItemTemplate, and Part features to a Control. 
    ///  
    //
    [DefaultEvent("OnItemsChanged"), DefaultProperty("Items")] 
    [ContentProperty("Items")]
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(FrameworkElement))]
    [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] // cannot be read & localized as string
    public class ItemsControl : Control, IAddChild, IGeneratorHost 
    {
        #region Constructors 
 
        /// 
        ///     Default ItemsControl constructor 
        /// 
        /// 
        ///     Automatic determination of current Dispatcher. Use alternative constructor
        ///     that accepts a Dispatcher for best performance. 
        /// 
        public ItemsControl() : base() 
        { 
        }
 
        static ItemsControl()
        {
            //
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsControl), new FrameworkPropertyMetadata(typeof(ItemsControl))); 
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ItemsControl));
            EventManager.RegisterClassHandler(typeof(ItemsControl), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnGotFocus)); 
        } 

        private void CreateItemCollectionAndGenerator() 
        {
            _items = new ItemCollection(this);

            // the generator must attach its collection change handler before 
            // the control itself, so that the generator is up-to-date by the
            // time the control tries to use it (bug 892806 et al.) 
            _itemContainerGenerator = new ItemContainerGenerator(this); 

            ((INotifyCollectionChanged)_items).CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemCollectionChanged); 

            if (IsInitPending)
            {
                _items.BeginInit(); 
            }
            else if (IsInitialized) 
            { 
                _items.BeginInit();
                _items.EndInit(); 
            }

            ((INotifyCollectionChanged)_groupStyle).CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupStyleChanged);
        } 

        #endregion 
 
        #region Properties
 
        /// 
        ///     Items is the collection of data that is used to generate the content
        ///     of this control.
        ///  
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Bindable(true), CustomCategory("Content")] 
        public ItemCollection Items 
        {
            get 
            {
                if (_items == null)
                {
                    CreateItemCollectionAndGenerator(); 
                }
 
                return _items; 
            }
        } 

        /// 
        /// This method is used by TypeDescriptor to determine if this property should
        /// be serialized. 
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)] 
        public bool ShouldSerializeItems() 
        {
            return HasItems; 
        }

        /// 
        ///     The DependencyProperty for the ItemsSource property. 
        ///     Flags:              None
        ///     Default Value:      null 
        ///  
        [CommonDependencyProperty]
        public static readonly DependencyProperty ItemsSourceProperty 
            = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ItemsControl),
                                          new FrameworkPropertyMetadata((IEnumerable)null,
                                                                        new PropertyChangedCallback(OnItemsSourceChanged)));
 
        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ItemsControl ic = (ItemsControl) d; 
            IEnumerable oldValue = (IEnumerable)e.OldValue;
            IEnumerable newValue = (IEnumerable)e.NewValue; 

            // distinguish between an explicit null value and one arising from
            // a Binding.  The former means to return to normal mode,
            // the latter means to use ItemsSource mode, but with a 
            // null collection.
            if (e.NewValue == null && !BindingOperations.IsDataBound(d, ItemsSourceProperty)) 
            { 
                ic.Items.ClearItemsSource();
            } 
            else
            {
                ic.Items.SetItemsSource(newValue);
            } 
            ic.OnItemsSourceChanged(oldValue, newValue);
        } 
 
        /// 
        /// Called when the value of ItemsSource changes. 
        /// 
        protected virtual void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
        {
        } 

        ///  
        ///     ItemsSource specifies a collection used to generate the content of 
        /// this control.  This provides a simple way to use exactly one collection
        /// as the source of content for this control. 
        /// 
        /// 
        ///     Any existing contents of the Items collection is replaced when this
        /// property is set. The Items collection will be made ReadOnly and FixedSize. 
        ///     When ItemsSource is in use, setting this property to null will remove
        /// the collection and restore use to Items (which will be an empty ItemCollection). 
        ///     When ItemsSource is not in use, the value of this property is null, and 
        /// setting it to null has no effect.
        ///  
        [Bindable(true), CustomCategory("Content")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnumerable ItemsSource
        { 
            get { return Items.ItemsSource; }
            set 
            { 
                if (value == null)
                { 
                    ClearValue(ItemsSourceProperty);
                }
                else
                { 
                    SetValue(ItemsSourceProperty, value);
                } 
            } 
        }
 
        /// 
        /// The ItemContainerGenerator associated with this control
        /// 
        [Bindable(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] 
        public ItemContainerGenerator ItemContainerGenerator
        { 
            get 
            {
                if (_itemContainerGenerator == null) 
                {
                    CreateItemCollectionAndGenerator();
                }
 
                return _itemContainerGenerator;
            } 
        } 

        ///  
        ///     Returns enumerator to logical children
        /// 
        protected internal override IEnumerator LogicalChildren
        { 
            get
            { 
                if (!HasItems) 
                {
                    return EmptyEnumerator.Instance; 
                }

                // Items in direct-mode of ItemCollection are the only model children.
                // note: the enumerator walks the ItemCollection.InnerList as-is, 
                // no flattening of any content on model children level!
                return this.Items.LogicalChildren; 
            } 
        }
 
        private void OnItemCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            SetValue(HasItemsPropertyKey, (_items != null) && !_items.IsEmpty);
 
            // If the focused item is removed, drop our reference to it.
            if ((e.Action == NotifyCollectionChangedAction.Remove && _focusedItem != null && _focusedItem.Equals(e.OldItems[0])) 
                || (e.Action == NotifyCollectionChangedAction.Reset)) 
            {
                _focusedItem = null; 
            }

            OnItemsChanged(e);
        } 

        ///  
        ///     This method is invoked when the Items property changes. 
        /// 
        protected virtual void OnItemsChanged(NotifyCollectionChangedEventArgs e) 
        {
        }

        ///  
        ///     The key needed set a read-only property.
        ///  
        internal static readonly DependencyPropertyKey HasItemsPropertyKey = 
                DependencyProperty.RegisterReadOnly(
                        "HasItems", 
                        typeof(bool),
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
 
        /// 
        ///     The DependencyProperty for the HasItems property. 
        ///     Flags:              None 
        ///     Other:              Read-Only
        ///     Default Value:      false 
        /// 
        public static readonly DependencyProperty HasItemsProperty =
                HasItemsPropertyKey.DependencyProperty;
 
        /// 
        ///     True if Items.Count > 0, false otherwise. 
        ///  
        [Bindable(false), Browsable(false)]
        public bool HasItems 
        {
            get { return (bool) GetValue(HasItemsProperty); }
        }
 
        /// 
        ///     The DependencyProperty for the DisplayMemberPath property. 
        ///     Flags:              none 
        ///     Default Value:      string.Empty
        ///  
        public static readonly DependencyProperty DisplayMemberPathProperty =
                DependencyProperty.Register(
                        "DisplayMemberPath",
                        typeof(string), 
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata( 
                                string.Empty, 
                                new PropertyChangedCallback(OnDisplayMemberPathChanged)));
 
        /// 
        ///     DisplayMemberPath is a simple way to define a default template
        ///     that describes how to convert Items into UI elements by using
        ///     the specified path. 
        /// 
        [Bindable(true), CustomCategory("Content")] 
        public string DisplayMemberPath 
        {
            get { return (string) GetValue(DisplayMemberPathProperty); } 
            set { SetValue(DisplayMemberPathProperty, value); }
        }

        ///  
        ///     Called when DisplayMemberPathProperty is invalidated on "d."
        ///  
        private static void OnDisplayMemberPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            ItemsControl ctrl = (ItemsControl) d; 

            string oldDisplayMemberPath = (string) e.OldValue;
            string newDisplayMemberPath = (string) e.NewValue;
 
            if (!String.IsNullOrEmpty(newDisplayMemberPath))
            { 
                // we will set the local value of ItemTemplateSelector. 
                // First check that this doesn't conflict with user's own setting.
                if (String.IsNullOrEmpty(oldDisplayMemberPath)) 
                {
                    // no previous DisplayMemberPath implies ITS was set by the user
                    if (!Helper.HasDefaultValue(ItemTemplateSelectorProperty, ctrl, ctrl, null))
                    { 
                        // if ITS was actually set, setting DisplayMemberPath is an
                        // error unless ITS came from a style and DMP is local 
                        if (ctrl.ReadLocalValue(ItemTemplateSelectorProperty) != DependencyProperty.UnsetValue || 
                            ctrl.ReadLocalValue(DisplayMemberPathProperty) == DependencyProperty.UnsetValue)
                        { 
                            throw new InvalidOperationException(SR.Get(SRID.DisplayMemberPathAndItemTemplateSelectorDefined));
                        }
                    }
                } 

                // now set the ItemTemplateSelector to use the new DisplayMemberPath 
                ctrl.ItemTemplateSelector = new DisplayMemberTemplateSelector(newDisplayMemberPath); 
            }
            else 
            {
                // clear ItemTemplateSelector if it came from the previous DisplayMemberPath
                if (!String.IsNullOrEmpty(oldDisplayMemberPath))
                { 
                    ctrl.ClearValue(ItemTemplateSelectorProperty);
                } 
            } 

            // this call is redundant - changes to ItemTemplateSelector (above) 
            // have already called it in all the cases that need to
            // ctrl.CheckTemplateSource();

            ctrl.OnDisplayMemberPathChanged(oldDisplayMemberPath, newDisplayMemberPath); 
        }
 
        ///  
        ///     This method is invoked when the DisplayMemberPath property changes.
        ///  
        /// The old value of the DisplayMemberPath property.
        /// The new value of the DisplayMemberPath property.
        protected virtual void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath)
        { 
        }
 
        ///  
        ///     The DependencyProperty for the ItemTemplate property.
        ///     Flags:              none 
        ///     Default Value:      null
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty ItemTemplateProperty = 
                DependencyProperty.Register(
                        "ItemTemplate", 
                        typeof(DataTemplate), 
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata( 
                                (DataTemplate) null,
                                new PropertyChangedCallback(OnItemTemplateChanged)));

        ///  
        ///     ItemTemplate is the template used to display each item.
        ///  
        [Bindable(true), CustomCategory("Content")] 
        public DataTemplate ItemTemplate
        { 
            get { return (DataTemplate) GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }
 
        /// 
        ///     Called when ItemTemplateProperty is invalidated on "d." 
        ///  
        /// The object on which the property was invalidated.
        /// EventArgs that contains the old and new values for this property 
        private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsControl) d).OnItemTemplateChanged((DataTemplate) e.OldValue, (DataTemplate) e.NewValue);
        } 

        ///  
        ///     This method is invoked when the ItemTemplate property changes. 
        /// 
        /// The old value of the ItemTemplate property. 
        /// The new value of the ItemTemplate property.
        protected virtual void OnItemTemplateChanged(DataTemplate oldItemTemplate, DataTemplate newItemTemplate)
        {
            CheckTemplateSource(); 

            if (_itemContainerGenerator != null) 
            { 
                _itemContainerGenerator.Refresh();
            } 
        }


        ///  
        ///     The DependencyProperty for the ItemTemplateSelector property.
        ///     Flags:              none 
        ///     Default Value:      null 
        /// 
        [CommonDependencyProperty] 
        public static readonly DependencyProperty ItemTemplateSelectorProperty =
                DependencyProperty.Register(
                        "ItemTemplateSelector",
                        typeof(DataTemplateSelector), 
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata( 
                                (DataTemplateSelector) null, 
                                new PropertyChangedCallback(OnItemTemplateSelectorChanged)));
 
        /// 
        ///     ItemTemplateSelector allows the application writer to provide custom logic
        ///     for choosing the template used to display each item.
        ///  
        /// 
        ///     This property is ignored if  is set. 
        ///  
        [Bindable(true), CustomCategory("Content")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public DataTemplateSelector ItemTemplateSelector
        {
            get { return (DataTemplateSelector) GetValue(ItemTemplateSelectorProperty); }
            set { SetValue(ItemTemplateSelectorProperty, value); } 
        }
 
        ///  
        ///     Called when ItemTemplateSelectorProperty is invalidated on "d."
        ///  
        /// The object on which the property was invalidated.
        /// EventArgs that contains the old and new values for this property
        private static void OnItemTemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((ItemsControl)d).OnItemTemplateSelectorChanged((DataTemplateSelector) e.OldValue, (DataTemplateSelector) e.NewValue);
        } 
 
        /// 
        ///     This method is invoked when the ItemTemplateSelector property changes. 
        /// 
        /// The old value of the ItemTemplateSelector property.
        /// The new value of the ItemTemplateSelector property.
        protected virtual void OnItemTemplateSelectorChanged(DataTemplateSelector oldItemTemplateSelector, DataTemplateSelector newItemTemplateSelector) 
        {
            CheckTemplateSource(); 
 
            if ((_itemContainerGenerator != null) && (ItemTemplate == null))
            { 
                _itemContainerGenerator.Refresh();
            }
        }
 

        ///  
        /// Throw if more than one of DisplayMemberPath, xxxTemplate and xxxTemplateSelector 
        /// properties are set on the given element.
        ///  
        private void CheckTemplateSource()
        {
            if (string.IsNullOrEmpty(DisplayMemberPath))
            { 
                Helper.CheckTemplateAndTemplateSelector("Item", ItemTemplateProperty, ItemTemplateSelectorProperty, this);
            } 
            else 
            {
                if (!(this.ItemTemplateSelector is DisplayMemberTemplateSelector)) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.ItemTemplateSelectorBreaksDisplayMemberPath));
                }
                if (Helper.IsTemplateDefined(ItemTemplateProperty, this)) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.DisplayMemberPathAndItemTemplateDefined)); 
                } 
            }
        } 

        /// 
        ///     The DependencyProperty for the ItemContainerStyle property.
        ///     Flags:              none 
        ///     Default Value:      null
        ///  
        [CommonDependencyProperty] 
        public static readonly DependencyProperty ItemContainerStyleProperty =
                DependencyProperty.Register( 
                        "ItemContainerStyle",
                        typeof(Style),
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata( 
                                (Style) null,
                                new PropertyChangedCallback(OnItemContainerStyleChanged))); 
 
        /// 
        ///     ItemContainerStyle is the style that is applied to the container element generated 
        ///     for each item.
        /// 
        [Bindable(true), Category("Content")]
        public Style ItemContainerStyle 
        {
            get { return (Style) GetValue(ItemContainerStyleProperty); } 
            set { SetValue(ItemContainerStyleProperty, value); } 
        }
 
        /// 
        ///     Called when ItemContainerStyleProperty is invalidated on "d."
        /// 
        /// The object on which the property was invalidated. 
        /// EventArgs that contains the old and new values for this property
        private static void OnItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        { 
            ((ItemsControl) d).OnItemContainerStyleChanged((Style) e.OldValue, (Style) e.NewValue);
        } 

        /// 
        ///     This method is invoked when the ItemContainerStyle property changes.
        ///  
        /// The old value of the ItemContainerStyle property.
        /// The new value of the ItemContainerStyle property. 
        protected virtual void OnItemContainerStyleChanged(Style oldItemContainerStyle, Style newItemContainerStyle) 
        {
            Helper.CheckStyleAndStyleSelector("ItemContainer", ItemContainerStyleProperty, ItemContainerStyleSelectorProperty, this); 

            if (_itemContainerGenerator != null)
            {
                _itemContainerGenerator.Refresh(); 
            }
        } 
 

        ///  
        ///     The DependencyProperty for the ItemContainerStyleSelector property.
        ///     Flags:              none
        ///     Default Value:      null
        ///  
        [CommonDependencyProperty]
        public static readonly DependencyProperty ItemContainerStyleSelectorProperty = 
                DependencyProperty.Register( 
                        "ItemContainerStyleSelector",
                        typeof(StyleSelector), 
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata(
                                (StyleSelector) null,
                                new PropertyChangedCallback(OnItemContainerStyleSelectorChanged))); 

        ///  
        ///     ItemContainerStyleSelector allows the application writer to provide custom logic 
        ///     to choose the style to apply to each generated container element.
        ///  
        /// 
        ///     This property is ignored if  is set.
        /// 
        [Bindable(true), Category("Content")] 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public StyleSelector ItemContainerStyleSelector 
        { 
            get { return (StyleSelector) GetValue(ItemContainerStyleSelectorProperty); }
            set { SetValue(ItemContainerStyleSelectorProperty, value); } 
        }

        /// 
        ///     Called when ItemContainerStyleSelectorProperty is invalidated on "d." 
        /// 
        /// The object on which the property was invalidated. 
        /// EventArgs that contains the old and new values for this property 
        private static void OnItemContainerStyleSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((ItemsControl) d).OnItemContainerStyleSelectorChanged((StyleSelector) e.OldValue, (StyleSelector) e.NewValue);
        }

        ///  
        ///     This method is invoked when the ItemContainerStyleSelector property changes.
        ///  
        /// The old value of the ItemContainerStyleSelector property. 
        /// The new value of the ItemContainerStyleSelector property.
        protected virtual void OnItemContainerStyleSelectorChanged(StyleSelector oldItemContainerStyleSelector, StyleSelector newItemContainerStyleSelector) 
        {
            Helper.CheckStyleAndStyleSelector("ItemContainer", ItemContainerStyleProperty, ItemContainerStyleSelectorProperty, this);

            if ((_itemContainerGenerator != null) && (ItemContainerStyle == null)) 
            {
                _itemContainerGenerator.Refresh(); 
            } 
        }
 
        /// 
        ///     Returns the ItemsControl for which element is an ItemsHost.
        ///     More precisely, if element is marked by setting IsItemsHost="true"
        ///     in the style for an ItemsControl, or if element is a panel created 
        ///     by the ItemsPresenter for an ItemsControl, return that ItemsControl.
        ///     Otherwise, return null. 
        ///  
        public static ItemsControl GetItemsOwner(DependencyObject element)
        { 
            ItemsControl container = null;
            Panel panel = element as Panel;

            if (panel != null && panel.IsItemsHost) 
            {
                // see if element was generated for an ItemsPresenter 
                ItemsPresenter ip = ItemsPresenter.FromPanel(panel); 

                if (ip != null) 
                {
                    // if so use the element whose style begat the ItemsPresenter
                    container = ip.Owner;
                } 
                else
                { 
                    // otherwise use element's templated parent 
                    container = panel.TemplatedParent as ItemsControl;
                } 
            }

            return container;
        } 

 
        ///  
        ///     The DependencyProperty for the ItemsPanel property.
        ///     Flags:              none 
        ///     Default Value:      null
        /// 
        [CommonDependencyProperty]
        public static readonly DependencyProperty ItemsPanelProperty 
            = DependencyProperty.Register("ItemsPanel", typeof(ItemsPanelTemplate), typeof(ItemsControl),
                                          new FrameworkPropertyMetadata(GetDefaultItemsPanelTemplate(), 
                                                                        new PropertyChangedCallback(OnItemsPanelChanged))); 

        private static ItemsPanelTemplate GetDefaultItemsPanelTemplate() 
        {
            ItemsPanelTemplate template = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(StackPanel)));
            template.Seal();
            return template; 
        }
 
        ///  
        ///     ItemsPanel is the panel that controls the layout of items.
        ///     (More precisely, the panel that controls layout is created 
        ///     from the template given by ItemsPanel.)
        /// 
        [Bindable(false)]
        public ItemsPanelTemplate ItemsPanel 
        {
            get { return (ItemsPanelTemplate) GetValue(ItemsPanelProperty); } 
            set { SetValue(ItemsPanelProperty, value); } 
        }
 
        /// 
        ///     Called when ItemsPanelProperty is invalidated on "d."
        /// 
        /// The object on which the property was invalidated. 
        /// EventArgs that contains the old and new values for this property
        private static void OnItemsPanelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        { 
            ((ItemsControl) d).OnItemsPanelChanged((ItemsPanelTemplate) e.OldValue, (ItemsPanelTemplate) e.NewValue);
        } 

        /// 
        ///     This method is invoked when the ItemsPanel property changes.
        ///  
        /// The old value of the ItemsPanel property.
        /// The new value of the ItemsPanel property. 
        protected virtual void OnItemsPanelChanged(ItemsPanelTemplate oldItemsPanel, ItemsPanelTemplate newItemsPanel) 
        {
            ItemContainerGenerator.OnPanelChanged(); 
        }


        private static readonly DependencyPropertyKey IsGroupingPropertyKey = 
            DependencyProperty.RegisterReadOnly("IsGrouping", typeof(bool), typeof(ItemsControl), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
 
        ///  
        ///     The DependencyProperty for the IsGrouping property.
        ///  
        public static readonly DependencyProperty IsGroupingProperty = IsGroupingPropertyKey.DependencyProperty;

        /// 
        ///     Returns whether the control is using grouping. 
        /// 
        [Bindable(false)] 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public bool IsGrouping
        { 
            get
            {
                return (bool)GetValue(IsGroupingProperty);
            } 
        }
 
        ///  
        /// The collection of GroupStyle objects that describes the display of
        /// each level of grouping.  The entry at index 0 describes the top level 
        /// groups, the entry at index 1 describes the next level, and so forth.
        /// If there are more levels of grouping than entries in the collection,
        /// the last entry is used for the extra levels.
        ///  
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public ObservableCollection GroupStyle 
        { 
            get { return _groupStyle; }
        } 

        /// 
        /// This method is used by TypeDescriptor to determine if this property should
        /// be serialized. 
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)] 
        public bool ShouldSerializeGroupStyle() 
        {
            return (GroupStyle.Count > 0); 
        }

        private void OnGroupStyleChanged(object sender, NotifyCollectionChangedEventArgs e)
        { 
            if (_itemContainerGenerator != null)
            { 
                _itemContainerGenerator.Refresh(); 
            }
        } 


        /// 
        ///     The DependencyProperty for the GroupStyleSelector property. 
        ///     Flags:              none
        ///     Default Value:      null 
        ///  
        public static readonly DependencyProperty GroupStyleSelectorProperty
            = DependencyProperty.Register("GroupStyleSelector", typeof(GroupStyleSelector), typeof(ItemsControl), 
                                          new FrameworkPropertyMetadata((GroupStyleSelector)null,
                                                                        new PropertyChangedCallback(OnGroupStyleSelectorChanged)));

        ///  
        ///     GroupStyleSelector allows the app writer to provide custom selection logic
        ///     for a GroupStyle to apply to each group collection. 
        ///  
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Bindable(true), CustomCategory("Content")] 
        public GroupStyleSelector GroupStyleSelector
        {
            get { return (GroupStyleSelector) GetValue(GroupStyleSelectorProperty); }
            set { SetValue(GroupStyleSelectorProperty, value); } 
        }
 
        ///  
        ///     Called when GroupStyleSelectorProperty is invalidated on "d."
        ///  
        /// The object on which the property was invalidated.
        /// EventArgs that contains the old and new values for this property
        private static void OnGroupStyleSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            ((ItemsControl) d).OnGroupStyleSelectorChanged((GroupStyleSelector) e.OldValue, (GroupStyleSelector) e.NewValue);
        } 
 
        /// 
        ///     This method is invoked when the GroupStyleSelector property changes. 
        /// 
        /// The old value of the GroupStyleSelector property.
        /// The new value of the GroupStyleSelector property.
        protected virtual void OnGroupStyleSelectorChanged(GroupStyleSelector oldGroupStyleSelector, GroupStyleSelector newGroupStyleSelector) 
        {
            if (_itemContainerGenerator != null) 
            { 
                _itemContainerGenerator.Refresh();
            } 
        }

        /// 
        ///     The DependencyProperty for the IsTextSearchEnabled property. 
        ///     Default Value:      false
        ///  
        public static readonly DependencyProperty IsTextSearchEnabledProperty = 
                DependencyProperty.Register(
                        "IsTextSearchEnabled", 
                        typeof(bool),
                        typeof(ItemsControl),
                        new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
 
        /// 
        ///     Whether TextSearch is enabled or not on this ItemsControl 
        ///  
        public bool IsTextSearchEnabled
        { 
            get { return (bool) GetValue(IsTextSearchEnabledProperty); }
            set { SetValue(IsTextSearchEnabledProperty, BooleanBoxes.Box(value)); }
        }
 
        #endregion
 
        #region Mapping methods 

        /// 
        /// Return the ItemsControl that owns the given container element
        ///
        public static ItemsControl ItemsControlFromItemContainer(DependencyObject container)
        { 
            UIElement ui = container as UIElement;
            if (ui == null) 
                return null; 

            // ui appeared in items collection 
            ItemsControl ic = LogicalTreeHelper.GetParent(ui) as ItemsControl;
            if (ic != null)
            {
                // this is the right ItemsControl as long as the item 
                // is (or is eligible to be) its own container
                IGeneratorHost host = ic as IGeneratorHost; 
                if (host.IsItemItsOwnContainer(ui)) 
                    return ic;
                else 
                    return null;
            }

            ui = VisualTreeHelper.GetParent(ui) as UIElement; 

            return ItemsControl.GetItemsOwner(ui); 
        } 

        /// 
        /// Return the container that owns the given element.  If itemsControl
        /// is not null, return a container that belongs to the given ItemsControl.
        /// If itemsControl is null, return the closest container belonging to
        /// any ItemsControl.  Return null if no such container exists. 
        ///
        public static DependencyObject ContainerFromElement(ItemsControl itemsControl, DependencyObject element) 
        { 
            if (element == null)
                throw new ArgumentNullException("element"); 

            // if the element is itself the desired container, return it
            if (IsContainerForItemsControl(element, itemsControl))
            { 
                return element;
            } 
 
            // start the tree walk at the element's parent
            FrameworkObject fo = new FrameworkObject(element); 
            fo.Reset(fo.GetPreferVisualParent(true).DO);

            // walk up, stopping when we reach the desired container
            while (fo.DO != null) 
            {
                if (IsContainerForItemsControl(fo.DO, itemsControl)) 
                { 
                    break;
                } 

                fo.Reset(fo.PreferVisualParent.DO);
            }
 
            return fo.DO;
        } 
 
        ///
        /// Return the container belonging to the current ItemsControl that owns 
        /// the given container element.  Return null if no such container exists.
        ///
        public DependencyObject ContainerFromElement(DependencyObject element)
        { 
            return ContainerFromElement(this, element);
        } 
 
        // helper method used by ContainerFromElement
        private static bool IsContainerForItemsControl(DependencyObject element, ItemsControl itemsControl) 
        {
            // is the element a container?
            if (element.ContainsValue(ItemContainerGenerator.ItemForItemContainerProperty))
            { 
                // does the element belong to the itemsControl?
                if (itemsControl == null || itemsControl == ItemsControlFromItemContainer(element)) 
                { 
                    return true;
                } 
            }

            return false;
        } 

        #endregion Mapping methods 
 
        #region IAddChild
 
        ///
        /// Called to Add the object as a Child.
        ///
        /// 
        /// Object to add as a child
        /// 
        void IAddChild.AddChild(Object value) 
        {
            AddChild(value); 
        }

        /// 
        ///  Add an object child to this control 
        /// 
        protected virtual void AddChild(object value) 
        { 
            Items.Add(value);
        } 

        ///
        /// Called when text appears under the tag in markup
        /// 
        ///
        /// Text to Add to the Object 
        /// 
        void IAddChild.AddText(string text)
        { 
            AddText(text);
        }

        ///  
        ///  Add a text string to this control
        ///  
        protected virtual void AddText(string text) 
        {
            Items.Add(text); 
        }

        #endregion
 
        #region IGeneratorHost
 
        //----------------------------------------------------- 
        //
        //  Interface - IGeneratorHost 
        //
        //-----------------------------------------------------

        ///  
        /// The view of the data
        ///  
        ItemCollection IGeneratorHost.View 
        {
            get { return Items; } 
        }

        /// 
        /// Return true if the item is (or is eligible to be) its own ItemContainer 
        /// 
        bool IGeneratorHost.IsItemItsOwnContainer(object item) 
        { 
            return IsItemItsOwnContainerOverride(item);
        } 

        /// 
        /// Return the element used to display the given item
        ///  
        DependencyObject IGeneratorHost.GetContainerForItem(object item)
        { 
            DependencyObject container; 

            // use the item directly, if possible (bug 870672) 
            if (IsItemItsOwnContainerOverride(item))
                container = item as DependencyObject;
            else
                container = GetContainerForItemOverride(); 

            // the container might have a parent from a previous 
            // generation (bug 873118).  If so, clean it up before using it again. 
            //
            // Note: This assumes the container is about to be added to a new parent, 
            // according to the ItemsControl/Generator/Container pattern.
            // If someone calls the generator and doesn't add the container to
            // a visual parent, unexpected things might happen.
            Visual visual = container as Visual; 
            if (visual != null)
            { 
                Visual parent = VisualTreeHelper.GetParent(visual) as Visual; 
                if (parent != null)
                { 
                    Invariant.Assert(parent is FrameworkElement, SR.Get(SRID.ItemsControl_ParentNotFrameworkElement));
                    Panel p = parent as Panel;
                    if(p != null && (visual is UIElement))
                    { 
                        ToolBarPanel tbp = p as ToolBarPanel;
                        if(tbp != null) 
                            tbp.UIElementCollection.RemoveNoVerify((UIElement)visual); 
                        else
                            p.Children.RemoveNoVerify((UIElement)visual); 
                    }
                    else
                        ((FrameworkElement)parent).TemplateChild = null;
                } 
            }
 
            return container; 
        }
 
        /// 
        /// Prepare the element to act as the ItemContainer for the corresponding item.
        /// 
        void IGeneratorHost.PrepareItemContainer(DependencyObject container, object item) 
        {
            // GroupItems are special - their information comes from a different place 
            GroupItem groupItem = container as GroupItem; 
            if (groupItem != null)
            { 
                groupItem.PrepareItemContainer(item);
                return;
            }
 
            if (ShouldApplyItemContainerStyle(container, item))
            { 
                // apply the ItemContainer style (if any) 
                ApplyItemContainerStyle(container, item);
            } 

            // forward ItemTemplate, et al.
            PrepareContainerForItemOverride(container, item);
 
            if (container == item && TraceData.IsEnabled)
            { 
                // issue a message if there's an ItemTemplate(Selector) for "direct" items 
                // The ItemTemplate isn't used, which may confuse the user (bug 991101).
                if (ItemTemplate != null || ItemTemplateSelector != null) 
                {
                    TraceData.Trace(TraceEventType.Error, TraceData.ItemTemplateForDirectItem, AvTrace.TypeName(item));
                }
            } 
        }
 
        ///  
        /// Undo any initialization done on the element during GetContainerForItem and PrepareItemContainer
        ///  
        void IGeneratorHost.ClearContainerForItem(DependencyObject container, object item)
        {
            // This method no longer does most of the work it used to (bug 1445288).
            // It is called when a container is removed from the tree;  such a 
            // container will be GC'd soon, so there's no point in changing
            // its properties. 
            // 
            // We still call the override method, to give subclasses a chance
            // to clean up anything they may have done during Prepare (bug 1561206). 

            GroupItem groupItem = container as GroupItem;
            if (groupItem == null)
            { 
                ClearContainerForItemOverride(container, item);
            } 
            else 
            {
                // GroupItems are special - their information comes from a different place 
            }
        }

        ///  
        /// Determine if the given element was generated for this host as an ItemContainer.
        ///  
        bool IGeneratorHost.IsHostForItemContainer(DependencyObject container) 
        {
            // If ItemsControlFromItemContainer can determine who owns the element, 
            // use its decision.
            ItemsControl ic = ItemsControlFromItemContainer(container);
            if (ic != null)
                return (ic == this); 

            // If the element is in my items view, and if it can be its own ItemContainer, 
            // it's mine.  Contains may be expensive, so we avoid calling it in cases 
            // where we already know the answer - namely when the element has a
            // logical parent (ItemsControlFromItemContainer handles this case).  This 
            // leaves only those cases where the element belongs to my items
            // without having a logical parent (e.g. via ItemsSource) and without
            // having been generated yet. HasItem indicates if anything has been generated.
            DependencyObject parent = LogicalTreeHelper.GetParent(container); 
            if (parent == null)
            { 
                return IsItemItsOwnContainerOverride(container) && 
                    HasItems && Items.Contains(container);
            } 

            // Otherwise it's not mine
            return false;
        } 

        ///  
        /// Return the GroupStyle (if any) to use for the given group at the given level. 
        /// 
        GroupStyle IGeneratorHost.GetGroupStyle(CollectionViewGroup group, int level) 
        {
            GroupStyle result = null;

            // a. Use global selector 
            if (GroupStyleSelector != null)
            { 
                result = GroupStyleSelector(group, level); 
            }
 
            // b. lookup in GroupStyle list
            if (result == null)
            {
                // use last entry for all higher levels 
                if (level >= GroupStyle.Count)
                { 
                    level = GroupStyle.Count - 1; 
                }
 
                if (level >= 0)
                {
                    result = GroupStyle[level];
                } 
            }
 
            return result; 
        }
 
        /// 
        /// Communicates to the host that the generator is using grouping.
        /// 
        void IGeneratorHost.SetIsGrouping(bool isGrouping) 
        {
            SetValue(IsGroupingPropertyKey, BooleanBoxes.Box(isGrouping)); 
        } 

        #endregion IGeneratorHost 

        #region ISupportInitialize
        /// 
        ///     Initialization of this element is about to begin 
        /// 
        public override void BeginInit() 
        { 
            base.BeginInit();
 
            if (_items != null)
            {
                _items.BeginInit();
            } 
        }
 
        ///  
        ///     Initialization of this element has completed
        ///  
        public override void EndInit()
        {
            if (IsInitPending)
            { 
                if (_items != null)
                { 
                    _items.EndInit(); 
                }
 
                base.EndInit();
            }
        }
 
        private bool IsInitPending
        { 
            get 
            {
                return ReadInternalFlag(InternalFlags.InitPending); 
            }
        }

        #endregion 

        #region Protected Methods 
 
        /// 
        /// Return true if the item is (or should be) its own item container 
        /// 
        protected virtual bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is UIElement); 
        }
 
        ///  Create or identify the element used to display the given item.  
        protected virtual DependencyObject GetContainerForItemOverride()
        { 
            return new ContentPresenter();
        }

        ///  
        /// Prepare the element to display the item.  This may involve
        /// applying styles, setting bindings, etc. 
        ///  
        protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item)
        { 
            // Each type of "ItemContainer" element may require its own initialization.
            // We use explicit polymorphism via internal methods for this.
            //
            // Another way would be to define an interface IGeneratedItemContainer with 
            // corresponding virtual "core" methods.  Base classes (ContentControl,
            // ItemsControl, ContentPresenter) would implement the interface 
            // and forward the work to subclasses via the "core" methods. 
            //
            // While this is better from an OO point of view, and extends to 
            // 3rd-party elements used as containers, it exposes more public API.
            // Management considers this undesirable, hence the following rather
            // inelegant code.
 
            HeaderedContentControl hcc;
            ContentControl cc; 
            ContentPresenter cp; 
            ItemsControl ic;
            HeaderedItemsControl hic; 

            if ((hcc = element as HeaderedContentControl) != null)
            {
                hcc.PrepareHeaderedContentControl(item, ItemTemplate, ItemTemplateSelector); 
            }
            else if ((cc = element as ContentControl) != null) 
            { 
                cc.PrepareContentControl(item, ItemTemplate, ItemTemplateSelector);
            } 
            else if ((cp = element as ContentPresenter) != null)
            {
                cp.PrepareContentPresenter(item, ItemTemplate, ItemTemplateSelector);
            } 
            else if ((hic = element as HeaderedItemsControl) != null)
            { 
                hic.PrepareHeaderedItemsControl(item, ItemTemplate, ItemTemplateSelector, 
                    ItemContainerStyle, ItemContainerStyleSelector);
            } 
            else if ((ic = element as ItemsControl) != null)
            {
                if (ic != this)
                { 
                    ic.PrepareItemsControl(item, ItemTemplate, ItemTemplateSelector,
                        ItemContainerStyle, ItemContainerStyleSelector); 
                } 
            }
        } 

        /// 
        /// Undo the effects of PrepareContainerForItemOverride.
        ///  
        protected virtual void ClearContainerForItemOverride(DependencyObject element, object item)
        { 
            // This method no longer does the work it used to (bug 1445288). 
            // It is called when a container is removed from the tree;  such a
            // container will be GC'd soon, so there's no point in changing 
            // its properties.
        }

        ///  
        ///     Called when a TextInput event is received.
        ///  
        ///  
        protected override void OnTextInput(TextCompositionEventArgs e)
        { 
            base.OnTextInput(e);

            // Only handle text from ourselves or an item container
            if (!String.IsNullOrEmpty(e.Text) && IsTextSearchEnabled && 
                (e.OriginalSource == this || ItemsControlFromItemContainer(e.OriginalSource as DependencyObject) == this))
            { 
                TextSearch instance = TextSearch.EnsureInstance(this); 

                if (instance != null) 
                {
                    instance.DoSearch(e.Text);
                    // Note: we always want to handle the event to denote that we
                    // actually did something.  We wouldn't want an AccessKey 
                    // to get invoked just because there wasn't a match here.
                    e.Handled = true; 
                } 
            }
        } 

        /// 
        ///     Called when a KeyDown event is received.
        ///  
        /// 
        protected override void OnKeyDown(KeyEventArgs e) 
        { 
            base.OnKeyDown(e);
            if (IsTextSearchEnabled) 
            {
                // If the pressed the backspace key, delete the last character
                // in the TextSearch current prefix.
                if (e.Key == Key.Back) 
                {
                    TextSearch instance = TextSearch.EnsureInstance(this); 
 
                    if (instance != null)
                    { 
                        instance.DeleteLastCharacter();
                    }
                }
            } 
        }
 
        internal override void OnTemplateChangedInternal(FrameworkTemplate oldTemplate, FrameworkTemplate newTemplate) 
        {
            // Forget about the old ItemsHost we had when the style changes 
            _itemsHost = null;
            _scrollHost = null;
            SetBoolField(BoolField.ScrollHostValid, false);
 
            base.OnTemplateChangedInternal(oldTemplate, newTemplate);
        } 
 
        /// 
        /// Determine whether the ItemContainerStyle/StyleSelector should apply to the container 
        /// 
        /// true if the ItemContainerStyle should apply to the item
        protected virtual bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
        { 
            return true;
        } 
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Internal methods
        // 
        //-----------------------------------------------------
 
        ///  
        /// Prepare to display the item.
        ///  
        internal void PrepareItemsControl(object item,
                                    DataTemplate itemTemplate, DataTemplateSelector itemTemplateSelector,
                                    Style itemContainerStyle, StyleSelector itemContainerStyleSelector)
        { 
            if (item != this)
            { 
                // copy templates and styles from parent ItemsControl 
                if (itemTemplate != null)
                    SetValue(ItemTemplateProperty, itemTemplate); 
                if (itemTemplateSelector != null)
                    SetValue(ItemTemplateSelectorProperty, itemTemplateSelector);
                if (itemContainerStyle != null &&
                    Helper.HasDefaultValue(ItemContainerStyleProperty, this, this, null)) 
                {
                    SetValue(ItemContainerStyleProperty, itemContainerStyle); 
                } 
                if (itemContainerStyleSelector != null &&
                    Helper.HasDefaultValue(ItemContainerStyleSelectorProperty, this, this, null)) 
                {
                    SetValue(ItemContainerStyleSelectorProperty, itemContainerStyleSelector);
                }
            } 
        }
 
        internal Panel ItemsHost 
        {
            get 
            {
                return _itemsHost;
            }
            set { _itemsHost = value; } 
        }
 
        #region Keyboard Navigation 

        internal void NavigateByLine(FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs) 
        {
            NavigateByLine(FocusedItem, direction, itemNavigateArgs);
        }
 
        internal void NavigateByLine(object startingItem, FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs)
        { 
            if (ItemsHost == null) 
            {
                return; 
            }

            // If the focused item has been scrolled out of view and they want to
            // start navigating again, scroll it back into view. 
            if (startingItem != null && !IsOnCurrentPage(startingItem, direction))
            { 
                MakeVisible(Items.IndexOf(startingItem)); 
                // Wait for layout
                ItemsHost.UpdateLayout(); 
            }

            // When we get here if startingItem is non-null, it must be on the visible page.
            NavigateByLineInternal(startingItem, direction, itemNavigateArgs); 
        }
 
        private void NavigateByLineInternal(object startingItem, FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs) 
        {
            // If there is no starting item, just navigate to the first item. 
            if (startingItem == null)
            {
                NavigateToStart(itemNavigateArgs);
            } 
            else
            { 
                FrameworkElement startingElement = null; 
                FrameworkElement nextElement = null;
 
                startingElement = ItemContainerGenerator.ContainerFromItem(startingItem) as FrameworkElement;
                // If the container isn't there, it might have been degenerated or
                // it might have been scrolled out of view.  Either way, we
                // should start navigation from the ItemsHost b/c we know it 
                // is visible.
                // The generator could have given us an element which isn't 
                // actually visually connected.  In this case we should use 
                // the ItemsHost as well.
                if (startingElement == null || !ItemsHost.IsAncestorOf(startingElement)) 
                {
                    // Bug 991220 makes it so that we have to start from the ScrollHost.
                    // If we try to start from the ItemsHost it will always skip the first item.
                    startingElement = ScrollHost; 
                }
 
                nextElement = KeyboardNavigation.Current.PredictFocusedElement(startingElement, direction) as FrameworkElement; 

                // We can only navigate there if the target element is in the items host. 
                if ((nextElement != null) && (ItemsHost.IsAncestorOf(nextElement)))
                {
                    object nextItem = GetEncapsulatingItem(nextElement);
 
                    if (nextItem != DependencyProperty.UnsetValue)
                    { 
                        NavigateToItem(nextItem, itemNavigateArgs); 
                    }
                } 
            }
        }

        internal void NavigateByPage(FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs) 
        {
            NavigateByPage(FocusedItem, direction, itemNavigateArgs); 
        } 

        internal void NavigateByPage(object startingItem, FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs) 
        {
            if (ItemsHost == null)
            {
                return; 
            }
 
            // If the focused item has been scrolled out of view and they want to 
            // start navigating again, scroll it back into view.
            if (startingItem != null && !IsOnCurrentPage(startingItem, direction)) 
            {
                while (MakeVisible(Items.IndexOf(startingItem)))
                {
                    double oldHorizontalOffset = ScrollHost.HorizontalOffset; 
                    double oldVerticalOffset = ScrollHost.VerticalOffset;
 
                    ItemsHost.UpdateLayout(); 

                    // If offset does not change - exit the loop 
                    if (DoubleUtil.AreClose(oldHorizontalOffset, ScrollHost.HorizontalOffset) &&
                        DoubleUtil.AreClose(oldVerticalOffset, ScrollHost.VerticalOffset))
                        break;
                } 
            }
 
            NavigateByPageInternal(startingItem, direction, itemNavigateArgs); 
        }
 
        private void NavigateByPageInternal(object startingItem, FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs)
        {
            // Move to the last guy on the page if we're not already there.
            if (startingItem == null) 
            {
                NavigateToFirstItemOnCurrentPage(startingItem, direction, itemNavigateArgs); 
            } 
            else
            { 
                // See if the currently focused guy is the first or last one one the page
                int firstIndex;
                object first = GetFirstItemOnCurrentPage(startingItem, direction, out firstIndex);
 
                if (startingItem.Equals(first))
                { 
                    // Page in that direction 
                    bool navigateAfterMeasure = false;
 
                    if (ScrollHost != null)
                    {
                        switch (direction)
                        { 
                            case FocusNavigationDirection.Up:
                                if (IsLogicalHorizontal) 
                                { 
                                    ScrollHost.PageLeft();
                                } 
                                else
                                {
                                    ScrollHost.PageUp();
                                } 

                                navigateAfterMeasure = true; 
                                break; 

                            case FocusNavigationDirection.Down: 
                                if (IsLogicalHorizontal)
                                {
                                    ScrollHost.PageRight();
                                } 
                                else
                                { 
                                    ScrollHost.PageDown(); 
                                }
 
                                navigateAfterMeasure = true;
                                break;
                        }
                    } 

                    // After measure we should focus the first guy on the page 
                    if (navigateAfterMeasure) 
                    {
                        if (ItemsHost != null) 
                        {
                            ItemsHost.UpdateLayout();
                            NavigateToFirstItemOnCurrentPage(startingItem, direction, itemNavigateArgs);
                        } 
                    }
                } 
                else 
                {
                    // The currently focused guy is not the first on the page, so move there 
                    if (first != DependencyProperty.UnsetValue)
                    {
                        NavigateToItem(first, firstIndex, itemNavigateArgs);
                    } 
                }
            } 
        } 

        internal void NavigateToStart(ItemNavigateArgs itemNavigateArgs) 
        {
            if (HasItems)
            {
                int foundIndex; 
                object item = FindFocusable(0, 1, out foundIndex);
                NavigateToItem(item, foundIndex, itemNavigateArgs); 
            } 
        }
 
        internal void NavigateToEnd(ItemNavigateArgs itemNavigateArgs)
        {
            if (HasItems)
            { 
                int foundIndex;
                object item = FindFocusable(Items.Count - 1, -1, out foundIndex); 
                NavigateToItem(item, foundIndex, itemNavigateArgs); 
            }
        } 

        internal void NavigateToItem(object item, ItemNavigateArgs itemNavigateArgs)
        {
            NavigateToItem(item, -1, itemNavigateArgs, false /* alwaysAtTopOfViewport */); 
        }
 
        internal void NavigateToItem(object item, int itemIndex, ItemNavigateArgs itemNavigateArgs) 
        {
            NavigateToItem(item, itemIndex, itemNavigateArgs, false /* alwaysAtTopOfViewport */); 
        }

        internal void NavigateToItem(object item, ItemNavigateArgs itemNavigateArgs, bool alwaysAtTopOfViewport)
        { 
            NavigateToItem(item, -1, itemNavigateArgs, alwaysAtTopOfViewport);
        } 
 
        private void NavigateToItem(object item, int elementIndex, ItemNavigateArgs itemNavigateArgs, bool alwaysAtTopOfViewport)
        { 
            //

            // Perhaps the container isn't generated yet.  In this case we try to shift the view,
            // wait for measure, and then call it again. 
            if (item == DependencyProperty.UnsetValue)
            { 
                return; 
            }
 
            if (elementIndex == -1)
            {
                elementIndex = Items.IndexOf(item);
                if (elementIndex == -1) 
                    return;
            } 
 
            while (MakeVisible(elementIndex, alwaysAtTopOfViewport, false /* alignMinorAxisToo */))
            { 
                // The above operations to change VerticalOffset might have invalidated measure.
                // Try again after measure.
                Debug.Assert(ItemsHost != null);
 

                double oldHorizontalOffset = ScrollHost.HorizontalOffset; 
                double oldVerticalOffset = ScrollHost.VerticalOffset; 

                ItemsHost.UpdateLayout(); 

                // If offset does not change - exit the loop
                if (DoubleUtil.AreClose(oldHorizontalOffset, ScrollHost.HorizontalOffset) &&
                    DoubleUtil.AreClose(oldVerticalOffset, ScrollHost.VerticalOffset)) 
                    break;
            } 
 
            FocusItem(item, itemNavigateArgs);
        } 

        private object FindFocusable(int startIndex, int direction, out int foundIndex)
        {
            // HasItems may be wrong when underlying collection does not notify, but this function 
            // only cares about what's been generated and is consistent with ItemsControl state.
            if (HasItems) 
            { 
                int count = Items.Count;
                for (; startIndex >= 0 && startIndex < count; startIndex += direction) 
                {
                    FrameworkElement container = ItemContainerGenerator.ContainerFromIndex(startIndex) as FrameworkElement;

                    // If the UI is non-null it must meet some minimum requirements to consider it for 
                    // navigation (focusable, enabled).  If it has no UI we can make no judgements about it
                    // at this time, so it is navigable. 
                    if (container == null || Keyboard.IsFocusable(container)) 
                    {
                        foundIndex = startIndex; 
                        return Items[startIndex];
                    }
                }
            } 

            foundIndex = -1; 
            return null; 
        }
 
        private bool MakeVisible(int index)
        {
            return MakeVisible(index, false /* alwaysAtTopOfViewport */, false /* alignMinorAxisToo */);
        } 

        // Shifts the viewport to make the given index visible. 
        // Returns true if the viewport shifted. 
        private bool MakeVisible(int index, bool alwaysAtTopOfViewport, bool alignMinorAxisToo)
        { 
            if (index == -1) return false;

            if (ScrollHost != null)
            { 
                bool offsetChanged = false;
 
                double initialHorizontalOffset = ScrollHost.HorizontalOffset; 
                double initialVerticalOffset = ScrollHost.VerticalOffset;
 
                double newHorizontalOffset = initialHorizontalOffset;
                double newVerticalOffset = initialVerticalOffset;

                if (IsLogicalVertical) 
                {
                    if (alwaysAtTopOfViewport) 
                    { 
                        newVerticalOffset = index;
                    } 
                    else
                    {
                        // First check that the bottom is visible
                        if (DoubleUtil.GreaterThan(index + 1, initialVerticalOffset + ScrollHost.ViewportHeight)) 
                        {
                            newVerticalOffset = Math.Max(0.0, index + 1 - ScrollHost.ViewportHeight); 
                        } 

                        // Next make sure that the top is visible 
                        if (DoubleUtil.LessThan(index, initialVerticalOffset))
                        {
                            newVerticalOffset = index;
                        } 
                    }
 
                    if (alignMinorAxisToo) 
                    {
                        newHorizontalOffset = 0; 
                    }

                    if (!DoubleUtil.AreClose(initialHorizontalOffset, newHorizontalOffset))
                    { 
                        ScrollHost.ScrollToHorizontalOffset(newHorizontalOffset);
                        offsetChanged = true; 
                    } 

                    if (!DoubleUtil.AreClose(initialVerticalOffset, newVerticalOffset)) 
                    {
                        ScrollHost.ScrollToVerticalOffset(newVerticalOffset);
                        offsetChanged = true;
                    } 
                }
                else if (IsLogicalHorizontal) 
                { 
                    if (alwaysAtTopOfViewport)
                    { 
                        newHorizontalOffset = index;
                    }
                    else
                    { 
                        // First check that the bottom is visible
                        if (DoubleUtil.GreaterThan(index + 1, initialHorizontalOffset + ScrollHost.ViewportWidth)) 
                        { 
                            newHorizontalOffset = Math.Max(0.0, index + 1 - ScrollHost.ViewportWidth);
                        } 

                        // Next make sure that the top is visible
                        if (DoubleUtil.LessThan(index, initialHorizontalOffset))
                        { 
                            newHorizontalOffset = index;
                        } 
                    } 

                    if (alignMinorAxisToo) 
                    {
                        newVerticalOffset = 0;
                    }
 
                    if (!DoubleUtil.AreClose(initialHorizontalOffset, newHorizontalOffset))
                    { 
                        ScrollHost.ScrollToHorizontalOffset(newHorizontalOffset); 
                        offsetChanged = true;
                    } 

                    if (!DoubleUtil.AreClose(initialVerticalOffset, newVerticalOffset))
                    {
                        ScrollHost.ScrollToVerticalOffset(newVerticalOffset); 
                        offsetChanged = true;
                    } 
                } 
                else
                { 
                    FrameworkElement container = ItemContainerGenerator.ContainerFromIndex(index) as FrameworkElement;
                    if (container != null)
                    {
                        container.BringIntoView(); 
                        offsetChanged = !DoubleUtil.AreClose(initialHorizontalOffset, ScrollHost.HorizontalOffset) ||
                                        !DoubleUtil.AreClose(initialVerticalOffset, ScrollHost.VerticalOffset); 
                    } 
                }
 
                return offsetChanged;
            }

            return false; 
        }
 
        private void NavigateToFirstItemOnCurrentPage(object startingItem, FocusNavigationDirection direction, ItemNavigateArgs itemNavigateArgs) 
        {
            int foundIndex; 
            object firstItem = GetFirstItemOnCurrentPage(startingItem, direction, out foundIndex);

            if (firstItem != DependencyProperty.UnsetValue)
            { 
                FocusItem(firstItem, itemNavigateArgs);
            } 
        } 

        private object GetFirstItemOnCurrentPage(object startingItem, FocusNavigationDirection direction, out int foundIndex) 
        {
            Debug.Assert(direction == FocusNavigationDirection.Up || direction == FocusNavigationDirection.Down, "Can only get the first item on a page using North or South");
            foundIndex = -1;
 
            //
            if (IsLogicalVertical) 
            { 
                if (direction == FocusNavigationDirection.Up)
                { 
                    return FindFocusable((int)ScrollHost.VerticalOffset, 1, out foundIndex);
                }
                else // if (direction == FocusNavigationDirection.Down)
                { 
                    return FindFocusable((int)(ScrollHost.VerticalOffset + ScrollHost.ViewportHeight - 1), -1, out foundIndex);
                } 
            } 
            else if (IsLogicalHorizontal)
            { 
                if (direction == FocusNavigationDirection.Up)
                {
                    return FindFocusable((int)ScrollHost.HorizontalOffset, 1, out foundIndex);
                } 
                else // if (direction == FocusNavigationDirection.Down)
                { 
                    return FindFocusable((int)(ScrollHost.HorizontalOffset + ScrollHost.ViewportWidth - 1), -1, out foundIndex); 
                }
            } 

            // We assume we're physically scrolling in both directions now.
            FrameworkElement startElement = ItemContainerGenerator.ContainerFromItem(startingItem) as FrameworkElement;
            FrameworkElement currentElement = startElement; 
            FrameworkElement previousElement = null;
            if (startElement != null) 
            { 
                // If the focused guy isn't on the page, try to move until we are on the page.
                // 
                while (currentElement != null && !IsOnCurrentPage(currentElement, direction))
                {
                    previousElement = currentElement;
                    currentElement = KeyboardNavigation.Current.PredictFocusedElement(currentElement, direction) as FrameworkElement; 
                }
 
                while (currentElement != null && IsOnCurrentPage(currentElement, direction)) 
                {
                    previousElement = currentElement; 
                    currentElement = KeyboardNavigation.Current.PredictFocusedElement(currentElement, direction) as FrameworkElement;
                }

                return GetEncapsulatingItem(previousElement); 
            }
 
            return null; 
        }
 
        /// 
        /// Determines if the given item is on the current visible page.
        /// 
        private bool IsOnCurrentPage(object item, FocusNavigationDirection axis) 
        {
            FrameworkElement container = ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; 
 
            if (container == null)
            { 
                return false;
            }

            return IsOnCurrentPage(container, axis, false); 
        }
 
        private bool IsOnCurrentPage(FrameworkElement element, FocusNavigationDirection axis) 
        {
            return IsOnCurrentPage(element, axis, false); 
        }

        /// 
        /// Determines if the given element is on the current visible page. 
        /// The element must be completely on the page on the given axis, but need
        /// not be completely contained on the page in the perpendicular axis. 
        /// For example, if axis == North, then the element's Top and Bottom must 
        /// be completely contained on the page.
        ///  
        private bool IsOnCurrentPage(FrameworkElement element, FocusNavigationDirection axis, bool fullyVisible)
        {
            // NOTE: When ScrollHost is non-null, we use ScrollHost instead of
            //       ItemsHost because ItemsHost in the physically scrolling 
            //       case will just have its layout offset shifted, and all
            //       items will always be within the bounding box of the ItemsHost, 
            //       and we want to know if you can actually see the element. 
            FrameworkElement viewPort = ScrollHost;
            if (viewPort == null) 
            {
                viewPort = ItemsHost;
            }
 
            // If there's no ScrollHost or ItemsHost, the element is not on the page
            if (viewPort == null) 
            { 
                return false;
            } 

            if (element == null || !viewPort.IsAncestorOf(element))
            {
                return false; 
            }
 
            Rect viewPortBounds = new Rect(new Point(), viewPort.RenderSize); 
            Rect elementBounds = new Rect(new Point(), element.RenderSize);
            elementBounds = element.TransformToAncestor(viewPort).TransformBounds(elementBounds); 

            // Return true if the element is completely contained within the page along the given axis.

            if (fullyVisible) 
            {
                return viewPortBounds.Contains(elementBounds); 
            } 
            else
            { 
                if (axis == FocusNavigationDirection.Up || axis == FocusNavigationDirection.Down)
                {
                    // Check that the element's Top/Bottom are inside the viewport's top and bottom
                    if (DoubleUtil.LessThanOrClose(viewPortBounds.Top, elementBounds.Top) 
                        && DoubleUtil.LessThanOrClose(elementBounds.Bottom, viewPortBounds.Bottom))
                    { 
                        return true; 
                    }
                } 
                else if (axis == FocusNavigationDirection.Right || axis == FocusNavigationDirection.Left)
                {
                    if (DoubleUtil.LessThanOrClose(viewPortBounds.Left, elementBounds.Left)
                        && DoubleUtil.LessThanOrClose(elementBounds.Right, viewPortBounds.Right)) 
                    {
                        return true; 
                    } 
                }
            } 


            return false;
        } 

        private static void OnGotFocus(object sender, KeyboardFocusChangedEventArgs e) 
        { 
            ItemsControl itemsControl = (ItemsControl)sender;
            UIElement itemContainer = e.OriginalSource as UIElement; 
            if ((itemContainer != null) && (itemContainer != itemsControl))
            {
                object item = itemsControl.ItemContainerGenerator.ItemFromContainer(itemContainer);
                if (item != DependencyProperty.UnsetValue) 
                    itemsControl._focusedItem = item;
            } 
        } 

 
        /// 
        /// The item corresponding to the UI container which has focus.
        /// Virtualizing panels remove visual children you can't see.
        /// When you scroll the focused element out of view we throw 
        /// focus back on to the items control and remember the item which
        /// was focused.  When it scrolls back into view (and focus is 
        /// still on the ItemsControl) we'll focus it. 
        /// 
        internal object FocusedItem 
        {
            get { return _focusedItem; }
        }
 
        private object _focusedItem;
 
        internal class ItemNavigateArgs 
        {
            public ItemNavigateArgs(InputDevice deviceUsed, ModifierKeys modifierKeys) 
            {
                _deviceUsed = deviceUsed;
                _modifierKeys = modifierKeys;
            } 

            public InputDevice DeviceUsed { get { return _deviceUsed; } } 
 
            private InputDevice _deviceUsed;
            private ModifierKeys _modifierKeys; 

            public static ItemNavigateArgs Empty
            {
                get 
                {
                    if (_empty == null) 
                    { 
                        _empty = new ItemNavigateArgs(null, ModifierKeys.None);;
                    } 
                    return _empty;
                }
            }
            private static ItemNavigateArgs _empty; 
        }
 
        // 
        internal virtual void FocusItem(object item, ItemNavigateArgs itemNavigateArgs)
        { 
            if (item != null)
            {
                UIElement container = ItemContainerGenerator.ContainerFromItem(item) as UIElement;
                if (container != null) 
                {
                    Keyboard.Focus(container); 
                } 
            }
            if (itemNavigateArgs.DeviceUsed is KeyboardDevice) 
            {
                KeyboardNavigation.ShowFocusVisual();
            }
        } 

        // 
 

 

        internal bool IsLogicalVertical
        {
            get 
            {
                return (ItemsHost != null && ItemsHost.HasLogicalOrientation && ItemsHost.LogicalOrientation == Orientation.Vertical && 
                        ScrollHost != null && ScrollHost.CanContentScroll); 
            }
        } 

        internal bool IsLogicalHorizontal
        {
            get 
            {
                return (ItemsHost != null && ItemsHost.HasLogicalOrientation && ItemsHost.LogicalOrientation == Orientation.Horizontal && 
                        ScrollHost != null && ScrollHost.CanContentScroll); 
            }
        } 

        internal ScrollViewer ScrollHost
        {
            get 
            {
                if (!GetBoolField(BoolField.ScrollHostValid)) 
                { 
                    if (_itemsHost == null)
                    { 
                        return null;
                    }
                    else
                    { 
                        // We have an itemshost, so walk up the tree looking for the ScrollViewer
                        for (DependencyObject current = _itemsHost; current != this && current != null; current = VisualTreeHelper.GetParent(current)) 
                        { 
                            ScrollViewer scrollViewer = current as ScrollViewer;
                            if (scrollViewer != null) 
                            {
                                _scrollHost = scrollViewer;
                                break;
                            } 
                        }
 
                        SetBoolField(BoolField.ScrollHostValid, true); 
                    }
                } 

                return _scrollHost;
            }
        } 

        internal static TimeSpan AutoScrollTimeout 
        { 
            get
            { 
                // NOTE: NtUser does the following (file: windows/ntuser/kernel/sysmet.c)
                //     gpsi->dtLBSearch = dtTime * 4;            // dtLBSearch   =  4  * gdtDblClk
                //     gpsi->dtScroll = gpsi->dtLBSearch / 5;  // dtScroll     = 4/5 * gdtDblClk
 
                return TimeSpan.FromMilliseconds(MS.Win32.SafeNativeMethods.GetDoubleClickTime() * 0.8);
            } 
        } 

        internal void DoAutoScroll() 
        {
            DoAutoScroll(FocusedItem);
        }
 
        internal void DoAutoScroll(object startingItem)
        { 
            // Attempt to compute positions based on the ScrollHost. 
            // If that doesn't exist, use the ItemsHost.
            FrameworkElement relativeTo = ScrollHost != null ? (FrameworkElement)ScrollHost : ItemsHost; 
            if (relativeTo != null)
            {
                // Figure out where the mouse is w.r.t. the ItemsControl.
 
                Point mousePosition = Mouse.GetPosition(relativeTo);
 
                // Take the bounding box of the ListBox and scroll against that 
                Rect bounds = new Rect(new Point(), relativeTo.RenderSize);
                bool focusChanged = false; 

                if (mousePosition.Y < bounds.Top)
                {
                    NavigateByLine(startingItem, FocusNavigationDirection.Up, new ItemNavigateArgs(Mouse.PrimaryDevice, Keyboard.Modifiers)); 
                    focusChanged = startingItem != FocusedItem;
                } 
                else if (mousePosition.Y >= bounds.Bottom) 
                {
                    NavigateByLine(startingItem, FocusNavigationDirection.Down, new ItemNavigateArgs(Mouse.PrimaryDevice, Keyboard.Modifiers)); 
                    focusChanged = startingItem != FocusedItem;
                }

                // Try horizontal scroll if vertical scroll did not happen 
                if (!focusChanged)
                { 
                    if (mousePosition.X < bounds.Left) 
                    {
                        FocusNavigationDirection direction = FocusNavigationDirection.Left; 
                        if (IsRTL(relativeTo))
                        {
                            direction = FocusNavigationDirection.Right;
                        } 

                        NavigateByLine(startingItem, direction, new ItemNavigateArgs(Mouse.PrimaryDevice, Keyboard.Modifiers)); 
                    } 
                    else if (mousePosition.X >= bounds.Right)
                    { 
                        FocusNavigationDirection direction = FocusNavigationDirection.Right;
                        if (IsRTL(relativeTo))
                        {
                            direction = FocusNavigationDirection.Left; 
                        }
 
                        NavigateByLine(startingItem, direction, new ItemNavigateArgs(Mouse.PrimaryDevice, Keyboard.Modifiers)); 
                    }
                } 
            }
        }

        private bool IsRTL(FrameworkElement element) 
        {
            FlowDirection flowDirection = element.FlowDirection; 
            return (flowDirection == FlowDirection.RightToLeft); 
        }
 
        private object GetEncapsulatingItem(FrameworkElement element)
        {
            object item = DependencyProperty.UnsetValue;
 
            while (item == DependencyProperty.UnsetValue && element != null)
            { 
                item = ItemContainerGenerator.ItemFromContainer(element); 
                element = VisualTreeHelper.GetParent(element) as FrameworkElement;
            } 

            return item;
        }
 
        #endregion Keyboard Navigation
 
        //------------------------------------------------------ 
        //
        //  Private Methods 
        //
        //------------------------------------------------------

        private void ApplyItemContainerStyle(DependencyObject container, object item) 
        {
            FrameworkObject foContainer = new FrameworkObject(container); 
 
            // don't overwrite a locally-defined style (bug 1018408)
            if (!foContainer.IsStyleSetFromGenerator && 
                container.ReadLocalValue(FrameworkElement.StyleProperty) != DependencyProperty.UnsetValue)
            {
                return;
            } 

            // Control's ItemContainerStyle has first stab 
            Style style = ItemContainerStyle; 

            // no ItemContainerStyle set, try ItemContainerStyleSelector 
            if (style == null)
            {
                if (ItemContainerStyleSelector != null)
                { 
                    style = ItemContainerStyleSelector.SelectStyle(item, container);
                } 
            } 

            // apply the style, if found 
            if (style != null)
            {
                // verify style is appropriate before applying it
                if (!style.TargetType.IsInstanceOfType(container)) 
                    throw new InvalidOperationException(SR.Get(SRID.StyleForWrongType, style.TargetType.Name, container.GetType().Name));
 
                foContainer.Style = style; 
                foContainer.IsStyleSetFromGenerator = true;
            } 
            else if (foContainer.IsStyleSetFromGenerator)
            {
                // if Style was formerly set from ItemContainerStyle, clear it
                foContainer.IsStyleSetFromGenerator = false; 
                container.ClearValue(FrameworkElement.StyleProperty);
            } 
        } 

        private void RemoveItemContainerStyle(DependencyObject container) 
        {
            FrameworkObject foContainer = new FrameworkObject(container);

            if (foContainer.IsStyleSetFromGenerator) 
            {
                container.ClearValue(FrameworkElement.StyleProperty); 
            } 
        }
 
        private bool GetBoolField(BoolField field)
        {
            return (_boolFieldStore & field) != 0;
        } 

        private void SetBoolField(BoolField field,bool value) 
        { 
            if (value)
            { 
                 _boolFieldStore |= field;
            }
            else
            { 
                 _boolFieldStore &= (~field);
            } 
        } 

        internal object GetItemOrContainerFromContainer(DependencyObject container) 
        {
            object item = ItemContainerGenerator.ItemFromContainer(container);

            if (item == DependencyProperty.UnsetValue 
                && ItemsControlFromItemContainer(container) == this
                && ((IGeneratorHost)this).IsItemItsOwnContainer(container)) 
            { 
                item = container;
            } 

            return item;
        }
 
        #region Method Overrides
 
        ///  
        ///     Returns a string representation of this object.
        ///  
        /// 
        public override string ToString()
        {
            // HasItems may be wrong when underlying collection does not notify, 
            // but this function should try to return what's consistent with ItemsControl state.
            int itemsCount = HasItems ? Items.Count : 0; 
            return SR.Get(SRID.ToStringFormatString_ItemsControl, this.GetType(), itemsCount); 
        }
 
        #endregion

        #region Data
 
        [System.Flags]
        private enum BoolField : uint 
        { 
            ScrollHostValid                 = 0x00000001,
            DisplayMemberPathCacheValid     = 0x00000002, 
        }

        private ItemCollection _items;                      // Cache for Items property
        private ItemContainerGenerator _itemContainerGenerator; 
        private Panel _itemsHost;
        private ScrollViewer _scrollHost; 
        private BoolField _boolFieldStore; 
        private ObservableCollection _groupStyle = new ObservableCollection();
 
        #endregion

        #region DTypeThemeStyleKey
 
        // Returns the DependencyObjectType for the registered ThemeStyleKey's default
        // value. Controls will override this method to return approriate types. 
        internal override DependencyObjectType DTypeThemeStyleKey 
        {
            get { return _dType; } 
        }

        private static DependencyObjectType _dType;
 
        #endregion DTypeThemeStyleKey
    } 
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK