Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Item selection in ItemsControl is not handled on PreviewMouseLeftButtonUp when multiple selection is enabled #336

Closed
jfevia opened this issue Nov 6, 2019 · 2 comments · Fixed by #383
Labels
Milestone

Comments

@jfevia
Copy link

jfevia commented Nov 6, 2019

Steps to reproduce

  1. Place a ListBox inside a TabControl and enabled drag & drop for both controls.
  2. Enable multiple selection for the ListBox.
  3. Select multiple items in the ListBox (e.g: press and hold CTRL key and click on the items in ListBox).
  4. Release CTRL key and click on one of the already selected items in the ListBox.

Example

<Window
    x:Class="gong_wpf_dragdrop_multiselect.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dd="urn:gong-wpf-dragdrop">
    <TabControl dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True">
        <TabItem>
            <ListBox
                dd:DragDrop.DragMouseAnchorPoint="0.5,0.5"
                dd:DragDrop.IsDragSource="True"
                dd:DragDrop.IsDropTarget="True"
                SelectionMode="Extended">
                <ListBoxItem Content="Item 1" />
                <ListBoxItem Content="Item 2" />
                <ListBoxItem Content="Item 3" />
                <ListBoxItem Content="Item 4" />
                <ListBoxItem Content="Item 5" />
            </ListBox>
        </TabItem>
    </TabControl>
</Window>

Expected behavior
"Reset" the selection to the already selected item in the ListBox only (e.g: de-select the other selected items).

Actual behavior
The selection does not change. The already selected items remain in that state until one item that is not selected is clicked or unless the user presses and holds the CTRL key and clicks on each item.

Findings
There exists a specific case in which the drag & drop is handled for ItemsControl elements. This occurs on PreviewMouseLeftButtonDown. However, PreviewMouseLeftButtonUp is not handled correctly.

I believe that the event PreviewMouseLeftButtonUp should be handled by the original element that set PreviewMouseLeftButtonDown's event to Handled = true.

if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
{
itemsControl.SetItemSelected(dragInfo.SourceItem, false);
}
else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
{
itemsControl.SetSelectedItem(dragInfo.SourceItem);
}

Environment
GongSolutions.WPF.DragDrop v2.x.x
Windows 10
Visual Studio 2019
.NET Framework 4.6

@Metritutus
Copy link
Contributor

Metritutus commented Jul 15, 2021

@punker76 Am I correct that all that needs to be done is to update line 458 in DragDrop.cs?

Specifically changing this:

if (sender is ItemsControl itemsControl && dragInfo != null && _clickSupressItem != null && _clickSupressItem == dragInfo.SourceItem)

To this:

if (dragInfo?.VisualSource is ItemsControl itemsControl && _clickSupressItem != null && _clickSupressItem == dragInfo.SourceItem)

I've tried to fix this issue because I've been working with drag-and-drop and multi-selection with WPF data grids, and found that:

  • It would not update the row selection when a row within an existing selection was clicked for a Datagrid in RowDetails.
  • It would try to set the SelectedItem of the parent DataGrid to the source item of the RowDetails DataGrid when clicking within an existing selection in the RowDetails DataGrid, which would result in System.Windows.Data Error: 23 if SelectedItem was bound.

In testing the above change, everything seems to work fine, however as I am not fully familiar with the code it's always possible that I've overlooked something important.

If you think this change is fine then I'll happily submit a pull-request with this change , assuming this project is not dead (which would be a shame), as I see that the last commit was in November 2020, and there are pending pull requests dating back years.

@Metritutus
Copy link
Contributor

As a followup to this, I have some further feedback here that may be of use to others working with nested ItemsControls and drag-drop.

I have recently been working with a DataGrid which has a RowDetailsTemplate containing a DataGrid. I have custom functionality in place for SelectionChanged events to handle various selection scenarios.

First, I noticed that because the GongSolutions.WPF.DragDrop code prevents default selection handling in DragSource_PreviewMouseLeftButtonDown (as mentioned in their source code comment), when programmatically setting the selection, other selection behaviour for ancestor ItemsControls won't run.

For example, in the following image there are two parent rows, each with a nested DataGrid, the first of which has one selected row, the second of which has two selected rows:
image

If I click on the row Two-Two, although Two-Three will be deselected, row One in the parent row will not be deselected.

To address this, I have modified the else if condition (

else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
) to be the following:

else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
{
    itemsControl.SetSelectedItem(dragInfo.SourceItem);

    if (sender != itemsControl && sender is ItemsControl ancestorItemsControl)
    {
        var ancestorItemContainer = ancestorItemsControl.ContainerFromElement(itemsControl);

        if (ancestorItemContainer != null)
        {
            var ancestorItem = ancestorItemsControl.ItemContainerGenerator.ItemFromContainer(ancestorItemContainer);

            if (ancestorItem != null)
            {
                ancestorItemsControl.SetSelectedItem(ancestorItem);
            }
        }
    }
}

This only goes up one level however as I'm only using the sender. To go up n-levels, it's possible that a solution using VisualTreeExtensions.GetVisualAncestor() may be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants