wpfwpf-controls

What internal mechanism triggers a ListBoxItem to be selected?


I'm trying to understand the details of WPF,but hit a stumbling block. I know that Selector is an abstract class that inherits from ItemsControl and adds the ability to select from its children. I know that ItemsControls have ItemTemplates that can set a child item's appearance and a panel that arranges those items. I know that there is often an ItemContainer that (at least in the case of a ListBoxItem) has an IsSelected property, which I believe can trigger the ListBox selection changed event (that might not be the right name).

What I absolutely can't figure out is, what changes the state of IsSelected internally? You click on an item, so there is probably a mouse down or preview mouse down or something...then a hit test?

I've been combing through ListBox, TabControl, and Selector source code at https://referencesource.microsoft.com/ trying to find the source of the click...is it on the panel? Is there some outside source? Do I have a fundamental misunderstanding of what's going on here?


Solution

  • ListBoxItem has overridden OnMouseLeftButtonDown and OnMouseRightButtonDown methods that call a private HandleMouseButtonDown method, which in turn calls an internal NotifyListItemClicked method of the ListBox class, which finally sets the item's IsSelected property.

    Copied from the reference source:

    internal void NotifyListItemClicked(ListBoxItem item, MouseButton mouseButton)
    {
        // When a ListBoxItem is left clicked, we should take capture
        // so we can auto scroll through the list.
        if (mouseButton == MouseButton.Left && Mouse.Captured != this)
        {
            Mouse.Capture(this, CaptureMode.SubTree);
            SetInitialMousePosition(); // Start tracking mouse movement
        }
    
        switch (SelectionMode)
        {
            case SelectionMode.Single:
                {
                    if (!item.IsSelected)
                    {
                        item.SetCurrentValueInternal(IsSelectedProperty, BooleanBoxes.TrueBox);
                    }
                    else if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
                    {
                        item.SetCurrentValueInternal(IsSelectedProperty, BooleanBoxes.FalseBox);
                    }
    
                    UpdateAnchorAndActionItem(ItemInfoFromContainer(item));
                }
                break;
    
            case SelectionMode.Multiple:
                MakeToggleSelection(item);
                break;
    
            case SelectionMode.Extended:
                // Extended selection works only with Left mouse button
                if (mouseButton == MouseButton.Left)
                {
                    if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift)) == (ModifierKeys.Control | ModifierKeys.Shift))
                    {
                        MakeAnchorSelection(item, false);
                    }
                    else if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
                    {
                        MakeToggleSelection(item);
                    }
                    else if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
                    {
                        MakeAnchorSelection(item, true);
                    }
                    else
                    {
                        MakeSingleSelection(item);
                    }
                }
                else if (mouseButton == MouseButton.Right) // Right mouse button
                {
                    // Shift or Control combination should not trigger any action
                    // If only Right mouse button is pressed we should move the anchor
                    // and select the item only if element under the mouse is not selected
                    if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift)) == 0)
                    {
                        if (item.IsSelected)
                            UpdateAnchorAndActionItem(ItemInfoFromContainer(item));
                        else
                            MakeSingleSelection(item);
                    }
                }
    
                break;
        }
    }