4
votes

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!

2

2 Answers

5
votes

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

2
votes

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