wpf drag and drop visual effects




WPF Datagrid drag and drop questions (4)

I think this i what you are looking for:

add this code to the DataGrid__PreviewMouseLeftButtonDown event handler:

private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    this.startingPosition = e.GetPosition(null);

    DependencyObject dep = (DependencyObject)e.OriginalSource;

    // iteratively traverse the visual tree until get a row or null
    while ((dep != null) && !(dep is DataGridRow))
    {
        dep = VisualTreeHelper.GetParent(dep);
    }

    //if this is a row (item)
    if (dep is DataGridRow)
    {
        //if the pointed item is already selected do not reselect it, so the previous multi-selection will remain
        if (songListDB.SelectedItems.Contains((dep as DataGridRow).Item))
        {
            // now the drag will drag all selected files
            e.Handled = true;
        }
    }
}

and now the draging won't change you selection.

Have a good luck!

I used that article to write my answer

I have a WPF Datagrid and I'm implementing drag and drop functionality.
The datagrid has a list of "files" and the user can drag them and copy the file to the desktop.
This is done like this:

string[] files = new String[myDataGrid.SelectedItems.Count];
int ix = 0;
foreach (object nextSel in myDataGrid.SelectedItems)
{
    files[ix] = ((Song)nextSel).FileLocation;
    ++ix;
}
string dataFormat = DataFormats.FileDrop;
DataObject dataObject = new DataObject(dataFormat, files);
DragDrop.DoDragDrop(this.myDataGrid, dataObject, DragDropEffects.Copy);  

I have two questions:
1. When I want to drag multiple items- this is a problem because after I select a couple and start clicking on one to start dragging- only that gets selected and the other items get deselected. I tried the solution that is given here but for some reason it doesn't work.
2. I want to remove the dragged item from the datagrid after it is copied. The problem is that I don't know how to check if the file was copied or whether the user just dragged it on the screen without copying it.

I hope you can help me solve these problems.
Thanks!


Improved on finding the row. Also selecting the clicked row when not dragging is added. This now behaves exactly as other Microsoft selectors (eg Outlook)

    public TreeDataGrid()
    {
        Loaded += TreeDataGrid_Loaded;
        LoadingRow += new EventHandler<DataGridRowEventArgs>(TreeDataGrid_LoadingRow);
    }

    #region MultiSelect Drag

    object toSelectItemOnMouseLeftButtonUp;

    void TreeDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
    {
        e.Row.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(Row_PreviewMouseLeftButtonDown);
        e.Row.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(Row_PreviewMouseLeftButtonUp); 
    }

    void Row_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridRow row = (DataGridRow)sender;
        toSelectItemOnMouseLeftButtonUp = null; // always clear selecteditem 
        if (SelectedItems.Contains(row.Item))  //if the pointed item is already selected do not reselect it, so the previous multi-selection will remain
        {
            e.Handled = true;  // this prevents the multiselection from disappearing, BUT datagridcell still gets the event and sets DataGrid's private member _selectionAnchor
            toSelectItemOnMouseLeftButtonUp = row.Item; // store our item to select on MouseLeftButtonUp
        }
    }

    void Row_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        DataGridRow row = (DataGridRow)sender; 
        if (row.Item == toSelectItemOnMouseLeftButtonUp)  // check if it's set and concerning the same row
        {
            if (SelectedItem == toSelectItemOnMouseLeftButtonUp) SelectedItem = null;  // if the item is already selected whe need to trigger a change 
            SelectedItem = toSelectItemOnMouseLeftButtonUp;  // this will clear the multi selection, and only select the item we pressed down on
            typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, new DataGridCellInfo(row.Item, ColumnFromDisplayIndex(0)));  // we need to set this anchor for when we select by pressing shift key
            toSelectItemOnMouseLeftButtonUp = null;  // handled
        }
    }

    #endregion

Use PreviewMouseLeftButtonDown to add the selected items for the drag operation.


WPF drag and drop from a ListBox that has SelectionMode=Extended

Here's what I've done. In your DragDrop code, subscribe to the PreviewMouseLeftButtonDown. If the item you are already clicking on is selected, then set e.Handled to true.

In my sample below, I identify a part of the list box item as a drag grip (with bumps) so that I can distinguish between the item and a drag surface. I just had to get the list box item data template and the drag and drop behavior to agree on a name of the drag grip element.

The PreviewMouseLeftButtonDown from my work in progress:

private void ItemsControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    dragStartPoint = e.GetPosition(null);

    ItemsControl itemsControl = this.AssociatedObject as ItemsControl;
    if (itemsControl != null)
    {
        this.sourceItemContainer = itemsControl.ContainerFromElement((Visual)e.OriginalSource) as FrameworkElement;
    }

    // If this is an multiple or extended selection list box, and on a drag grip, then ensure the item being hit is selected
    // This prevents the ItemsControl from using this MouseDown to change selection, except over a selected item's drag grip.            
    if ((this.IsMultipleSelectionListBox() == true) && (this.IsOriginalSourceDragGrip(e) != false) && (this.IsSourceListBoxItemSelected() == true))
    {
        e.Handled = true;
    }
}