File: System\Windows\Controls\ListBoxItem.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using MS.Internal.KnownBoxes;
using System.ComponentModel;
using System.Windows.Threading;
 
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
 
// Disable CS3001: Warning as Error: not CLS-compliant
#pragma warning disable 3001
 
namespace System.Windows.Controls
{
    /// <summary>
    ///     Control that implements a selectable item inside a ListBox.
    /// </summary>
    [DefaultEvent("Selected")]
    public class ListBoxItem : ContentControl
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        ///     Default DependencyObject constructor
        /// </summary>
        public ListBoxItem() : base()
        {
        }
 
        static ListBoxItem()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ListBoxItem), new FrameworkPropertyMetadata(typeof(ListBoxItem)));
            _dType = DependencyObjectType.FromSystemTypeInternal(typeof(ListBoxItem));
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(ListBoxItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
            KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(ListBoxItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
 
            IsEnabledProperty.OverrideMetadata(typeof(ListBoxItem), new UIPropertyMetadata(new PropertyChangedCallback(OnVisualStatePropertyChanged)));
            IsMouseOverPropertyKey.OverrideMetadata(typeof(ListBoxItem), new UIPropertyMetadata(new PropertyChangedCallback(OnVisualStatePropertyChanged)));
            Selector.IsSelectionActivePropertyKey.OverrideMetadata(typeof(ListBoxItem), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnVisualStatePropertyChanged)));
            AutomationProperties.IsOffscreenBehaviorProperty.OverrideMetadata(typeof(ListBoxItem), new FrameworkPropertyMetadata(IsOffscreenBehavior.FromClip));
        }
 
        #endregion
 
        //-------------------------------------------------------------------
        //
        //  Public Properties
        //
        //-------------------------------------------------------------------
 
        #region Public Properties
 
        /// <summary>
        ///     Indicates whether this ListBoxItem is selected.
        /// </summary>
        public static readonly DependencyProperty IsSelectedProperty =
                Selector.IsSelectedProperty.AddOwner(typeof(ListBoxItem),
                        new FrameworkPropertyMetadata(BooleanBoxes.FalseBox,
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                new PropertyChangedCallback(OnIsSelectedChanged)));
 
        /// <summary>
        ///     Indicates whether this ListBoxItem is selected.
        /// </summary>
        [Bindable(true), Category("Appearance")]
        public bool IsSelected
        {
            get { return (bool) GetValue(IsSelectedProperty); }
            set { SetValue(IsSelectedProperty, BooleanBoxes.Box(value)); }
        }
 
        private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBoxItem listItem = d as ListBoxItem;
            bool isSelected = (bool) e.NewValue;
 
            Selector parentSelector = listItem.ParentSelector;
            if (parentSelector != null)
            {
                parentSelector.RaiseIsSelectedChangedAutomationEvent(listItem, isSelected);
            }
 
            if (isSelected)
            {
                listItem.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, listItem));
            }
            else
            {
                listItem.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, listItem));
            }
 
            listItem.UpdateVisualState();
        }
 
        /// <summary>
        ///     Event indicating that the IsSelected property is now true.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected virtual void OnSelected(RoutedEventArgs e)
        {
            HandleIsSelectedChanged(true, e);
        }
 
        /// <summary>
        ///     Event indicating that the IsSelected property is now false.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected virtual void OnUnselected(RoutedEventArgs e)
        {
            HandleIsSelectedChanged(false, e);
        }
 
        private void HandleIsSelectedChanged(bool newValue, RoutedEventArgs e)
        {
            RaiseEvent(e);
        }
 
        #endregion
 
        #region Events
 
        /// <summary>
        ///     Raised when the item's IsSelected property becomes true.
        /// </summary>
        public static readonly RoutedEvent SelectedEvent = Selector.SelectedEvent.AddOwner(typeof(ListBoxItem));
 
        /// <summary>
        ///     Raised when the item's IsSelected property becomes true.
        /// </summary>
        public event RoutedEventHandler Selected
        {
            add
            {
                AddHandler(SelectedEvent, value);
            }
            remove
            {
                RemoveHandler(SelectedEvent, value);
            }
        }
 
        /// <summary>
        ///     Raised when the item's IsSelected property becomes false.
        /// </summary>
        public static readonly RoutedEvent UnselectedEvent = Selector.UnselectedEvent.AddOwner(typeof(ListBoxItem));
 
        /// <summary>
        ///     Raised when the item's IsSelected property becomes false.
        /// </summary>
        public event RoutedEventHandler Unselected
        {
            add
            {
                AddHandler(UnselectedEvent, value);
            }
            remove
            {
                RemoveHandler(UnselectedEvent, value);
            }
        }
 
        #endregion
 
        //-------------------------------------------------------------------
        //
        //  Protected Methods
        //
        //-------------------------------------------------------------------
 
        #region Protected Methods
 
        internal override void ChangeVisualState(bool useTransitions)
        {
            // Change to the correct state in the Interaction group
            if (!IsEnabled)
            {
                // [copied from SL code]
                // If our child is a control then we depend on it displaying a proper "disabled" state.  If it is not a control
                // (ie TextBlock, Border, etc) then we will use our visuals to show a disabled state.
                VisualStateManager.GoToState(this, Content is Control ? VisualStates.StateNormal : VisualStates.StateDisabled, useTransitions);
            }
            else if (IsMouseOver)
            {
                VisualStateManager.GoToState(this, VisualStates.StateMouseOver, useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, VisualStates.StateNormal, useTransitions);
            }
 
            // Change to the correct state in the Selection group
            if (IsSelected)
            {
                if (Selector.GetIsSelectionActive(this))
                {
                    VisualStateManager.GoToState(this, VisualStates.StateSelected, useTransitions);
                }
                else
                {
                    VisualStates.GoToState(this, useTransitions, VisualStates.StateSelectedUnfocused, VisualStates.StateSelected);
                }
            }
            else
            {
                VisualStateManager.GoToState(this, VisualStates.StateUnselected, useTransitions);
            }
 
            if (IsKeyboardFocused)
            {
                VisualStateManager.GoToState(this, VisualStates.StateFocused, useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, VisualStates.StateUnfocused, useTransitions);
            }
 
            base.ChangeVisualState(useTransitions);
        }
 
        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new ListBoxItemWrapperAutomationPeer(this);
        }
 
 
        /// <summary>
        ///     This is the method that responds to the MouseButtonEvent event.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (!e.Handled)
            {
                // turn this into a Selector.ItemClicked thing?
                e.Handled = true;
                HandleMouseButtonDown(MouseButton.Left);
            }
            base.OnMouseLeftButtonDown(e);
        }
 
        /// <summary>
        ///     This is the method that responds to the MouseButtonEvent event.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
        {
            if (!e.Handled)
            {
                // turn this into a Selector.ItemClicked thing?
                e.Handled = true;
                HandleMouseButtonDown(MouseButton.Right);
            }
            base.OnMouseRightButtonDown(e);
        }
 
        private void HandleMouseButtonDown(MouseButton mouseButton)
        {
            if (Selector.UiGetIsSelectable(this) && Focus())
            {
                ListBox parent = ParentListBox;
                if (parent != null)
                {
                    parent.NotifyListItemClicked(this, mouseButton);
                }
            }
        }
 
        /// <summary>
        /// Called when IsMouseOver changes on this element.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            // abort any drag operation we have queued.
            if (parentNotifyDraggedOperation != null)
            {
                parentNotifyDraggedOperation.Abort();
                parentNotifyDraggedOperation = null;
            }
 
            if (IsMouseOver)
            {
                ListBox parent = ParentListBox;
 
                if (parent != null && Mouse.LeftButton == MouseButtonState.Pressed)
                {
                    parent.NotifyListItemMouseDragged(this);
                }
            }
            base.OnMouseEnter(e);
        }
 
        /// <summary>
        /// Called when IsMouseOver changes on this element.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            // abort any drag operation we have queued.
            if (parentNotifyDraggedOperation != null)
            {
                parentNotifyDraggedOperation.Abort();
                parentNotifyDraggedOperation = null;
            }
 
            base.OnMouseLeave(e);
        }
 
        /// <summary>
        /// Called when the visual parent of this element changes.
        /// </summary>
        /// <param name="oldParent"></param>
        protected internal override void OnVisualParentChanged(DependencyObject oldParent)
        {
            ItemsControl oldItemsControl = null;
 
            if (VisualTreeHelper.GetParent(this) == null)
            {
                if (IsKeyboardFocusWithin)
                {
                    // This ListBoxItem had focus but was removed from the tree.
                    // The normal behavior is for focus to become null, but we would rather that
                    // focus go to the parent ListBox.
 
                    // Use the oldParent to get a reference to the ListBox that this ListBoxItem used to be in.
                    // The oldParent's ItemsOwner should be the ListBox.
                    oldItemsControl = ItemsControl.GetItemsOwner(oldParent);
                }
            }
 
            base.OnVisualParentChanged(oldParent);
 
            // If earlier, we decided to set focus to the old parent ListBox, do it here
            // after calling base so that the state for IsKeyboardFocusWithin is updated correctly.
            if (oldItemsControl != null)
            {
                oldItemsControl.Focus();
            }
        }
 
 
        #endregion
 
        //-------------------------------------------------------------------
        //
        //  Implementation
        //
        //-------------------------------------------------------------------
 
        #region Implementation
 
        private ListBox ParentListBox
        {
            get
            {
                return ParentSelector as ListBox;
            }
        }
 
        internal Selector ParentSelector
        {
            get
            {
                return ItemsControl.ItemsControlFromItemContainer(this) as Selector;
            }
        }
 
        #endregion
 
#if OLD_AUTOMATION
// left here for reference only as it is still used by MonthCalendar
        /// <summary>
        /// DependencyProperty for SelectionContainer property.
        /// </summary>
        internal static readonly DependencyProperty SelectionContainerProperty
            = DependencyProperty.RegisterAttached("SelectionContainer", typeof(UIElement), typeof(ListBoxItem),
            new FrameworkPropertyMetadata((UIElement)null));
#endif
 
        #region Private Fields
 
        DispatcherOperation parentNotifyDraggedOperation = null;
 
        #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
    }
}