Search code examples
c#wpfwpf-controls

Disable Drop inside DataGrid cell during edit mode


My application is a wine bottle/wine rack tracking tool that allows the user to quickly look up the location of a specific bottle of wine inside a collection of wine racks. I have drag and drop from the list of wine bottles (a DataGrid) to a wine rack (user control) by sending the bottle's ID (int) to the destination control. The problem is when the user clicks on a cell in the DataGrid to edit it's contents the drag drop executes and inserts the bottle ID into the text where the user clicks.

Here is my UI:

Application overview

This is what happens when I single click on a cell to edit:

Bug

If the user clicks multiple times on the cell I get this exception:

Exception Unhandled

Below is the XAML for the DataGrid:

<DataGrid Name="DGBottleList"
          Grid.Column="0"
          Grid.Row="0"
          HorizontalScrollBarVisibility="Auto"
          VerticalScrollBarVisibility="Auto"
          ItemsSource="{Binding Source=mainWindowBottles}" 
          IsSynchronizedWithCurrentItem="True" 
          SelectionChanged="DGBottleList_OnSelectionChanged"
          CurrentCellChanged="DGBottleList_CellChanged" 
          AutoGenerateColumns="False"
          Padding="0,0,0,0"
          Margin="0,0,5,0">


    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <EventSetter Event="MouseDoubleClick" Handler="DGBottleListRow_DoubleClick"/>
            <EventSetter Event="MouseMove"        Handler="DGBottleListRow_MouseMove"/>
        </Style>
    </DataGrid.Resources>

    <DataGrid.Columns>
        <DataGridTextColumn Header="Name"     
                            Binding="{Binding Name}"     
                            Width="SizeToCells" 
                            MinWidth="45"/>
        <DataGridTextColumn Header="Vineyard" 
                            Binding="{Binding Vineyard}" 
                            Width="SizeToCells" 
                            MinWidth="55"/>
        <DataGridTextColumn Header="Vintage"  
                            Binding="{Binding Vintage}"  
                            Width="SizeToCells" 
                            MinWidth="50"/>
        <DataGridTextColumn Header="Varietal" 
                            Binding="{Binding Varietal}" 
                            Width="SizeToCells" 
                            MinWidth="50"/>
        <DataGridTextColumn Header="Type"     
                            Binding="{Binding TypeText}" 
                            Width="SizeToCells" 
                            MinWidth="35" 
                            IsReadOnly="True"/>
        <DataGridTextColumn Header="Region"   
                            Binding="{Binding Region}"   
                            Width="SizeToCells" 
                            MinWidth="50"/>
        <DataGridTextColumn Header="Rack"     
                            Binding="{Binding RackName}" 
                            Width="SizeToCells" 
                            MinWidth="35" 
                            IsReadOnly="True"/>
    </DataGrid.Columns>

    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Copy Bottle"      Click="DGBottleList_CopyBottle"/>
            <MenuItem Header="Copy Many..."     Click="DGBottleList_CopyMany"/>
            <Separator/>
            <MenuItem Header="Add or Edit Note" Click="DGBottleList_AddNote"/>
            <MenuItem Header="Remove from rack" Click="DGBottleList_RemoveFromRack"/>
            <Separator/>
            <MenuItem Header="Delete"           Click="DGBottleList_Delete"/>
        </ContextMenu>
    </DataGrid.ContextMenu>
</DataGrid>

And here is my MouseMove:

protected void DGBottleListRow_MouseMove(object sender, MouseEventArgs e) 
{
    if (sender is not DataGridRow row)
        return;

    if (row.Item is not WineBottle movedBottle)
        return;

    if (e.LeftButton == MouseButtonState.Pressed)
        DragDrop.DoDragDrop(row, movedBottle.ID.ToString(), DragDropEffects.Move);
}

My understanding is the default AllowDrop should be False on the DataGrid and it's children. I have tried manually setting the AllowDrop to false on the DataGrid, DataGridRow, DataGridCell and the TextBlock child. I have tried assigning a handler method to all the before mentioned UI Elements that simply displays a MessageBox when a Drop happens to narrow down where the problem is coming from. I have not been able to get the MessageBox to fire yet. I am at a loss and any help would be appreciated. Thanks!


Solution

  • DataGridRow has an IsEditing dependency property. You could check if row is not being edited before DragDrop.DoDragDrop

    if (e.LeftButton == MouseButtonState.Pressed && !row.IsEditing)
            DragDrop.DoDragDrop(row, movedBottle.ID.ToString(), DragDropEffects.Move);