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 [Bindable(true), CustomCategory("Content")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public DataTemplateSelector ItemTemplateSelector { get { return (DataTemplateSelector) GetValue(ItemTemplateSelectorProperty); } set { SetValue(ItemTemplateSelectorProperty, value); } } ///is set. /// /// 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 [Bindable(true), Category("Content")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public StyleSelector ItemContainerStyleSelector { get { return (StyleSelector) GetValue(ItemContainerStyleSelectorProperty); } set { SetValue(ItemContainerStyleSelectorProperty, value); } } ///is set. /// /// 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 ObservableCollectionGroupStyle { 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Validator.cs
- ZoneLinkButton.cs
- ListViewGroupConverter.cs
- SafePointer.cs
- JapaneseLunisolarCalendar.cs
- QueryGeneratorBase.cs
- XmlExtensionFunction.cs
- DiagnosticTrace.cs
- SchemaLookupTable.cs
- FunctionParameter.cs
- XmlSchemaInfo.cs
- UserPreferenceChangingEventArgs.cs
- IndividualDeviceConfig.cs
- Path.cs
- PackageFilter.cs
- Type.cs
- SqlServices.cs
- _KerberosClient.cs
- PageAsyncTaskManager.cs
- FixedStringLookup.cs
- WindowsListViewItem.cs
- MenuEventArgs.cs
- XmlElementList.cs
- StringPropertyBuilder.cs
- ArgIterator.cs
- ImageSourceConverter.cs
- FunctionGenerator.cs
- ColorContextHelper.cs
- DataError.cs
- BitmapEffectGeneralTransform.cs
- RawTextInputReport.cs
- HtmlInputPassword.cs
- HtmlWindowCollection.cs
- WebPartConnectVerb.cs
- KeyInfo.cs
- PathStreamGeometryContext.cs
- NetSectionGroup.cs
- EtwTrace.cs
- LightweightCodeGenerator.cs
- XmlSchemaObjectTable.cs
- XhtmlBasicLinkAdapter.cs
- ErrorStyle.cs
- ToolStripStatusLabel.cs
- Color.cs
- EditorResources.cs
- PolicyManager.cs
- SafeHandles.cs
- ReceiveParametersContent.cs
- AssemblyAssociatedContentFileAttribute.cs
- BitmapSizeOptions.cs
- LocalServiceSecuritySettingsElement.cs
- EpmContentSerializerBase.cs
- TreeViewHitTestInfo.cs
- EmptyTextWriter.cs
- XsltFunctions.cs
- SecondaryIndex.cs
- EditorBrowsableAttribute.cs
- SizeAnimation.cs
- SurrogateSelector.cs
- FileVersion.cs
- JsonEnumDataContract.cs
- ListViewTableRow.cs
- AttributeQuery.cs
- WorkflowDesigner.cs
- Header.cs
- XMLSyntaxException.cs
- SQLDateTime.cs
- TextSerializer.cs
- HttpProfileGroupBase.cs
- ProfilePropertyNameValidator.cs
- SubstitutionList.cs
- TextRangeBase.cs
- PageSettings.cs
- IntAverageAggregationOperator.cs
- HtmlControlAdapter.cs
- CultureTable.cs
- TextLineResult.cs
- ComponentCodeDomSerializer.cs
- SQLByteStorage.cs
- UniqueConstraint.cs
- TypeLoadException.cs
- WindowsEditBox.cs
- ServerProtocol.cs
- documentsequencetextcontainer.cs
- RTLAwareMessageBox.cs
- Mutex.cs
- MessageDecoder.cs
- UnionCodeGroup.cs
- FragmentQueryKB.cs
- Configuration.cs
- ConfigurationStrings.cs
- DirectionalLight.cs
- SBCSCodePageEncoding.cs
- Expressions.cs
- XmlSchemaExternal.cs
- LingerOption.cs
- AnonymousIdentificationSection.cs
- ToolboxItemLoader.cs
- UpdateExpressionVisitor.cs
- Msec.cs