| File: System\Windows\Controls\ListBoxItem.cs | Web Access |
| Project: src\wpf\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. 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 CS3001 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; 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; 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. 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. 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. 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 private 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 } }