I wanted to give you a link to a simple solution. This turned out not possible to do.
So I have adapted a solution from the following links:
*
Drag and Drop DataGrid Row in WPF[
^] = initial attempt - not a good solution for any Drag'n'Drop!
*
WPF Tutorial | Drag & Drop[
^] = a better way of implementing Drag'n'Drop but for the wrong control
*
WPF: Scroll content of control when drag & drop is in progress[
^] = a working solution with no example
1.
ScrollOnDragOverBehavior
from the link above required no modification, so grab the code from there.
2.
ObservableObject
class for wrapping the
INotifyPropertyChanged
public abstract class ObservableObject : INotifyPropertyChanged
{
public void Set<TValue>(ref TValue field, TValue newValue, [CallerMemberName] string propertyName = "")
{
if (!EqualityComparer<TValue>.Default.Equals(field, default) && field!.Equals(newValue))
return;
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler? PropertyChanged;
}
3.
Employee
Data Model:
public class EmployeeModel : ObservableObject
{
private int empNo;
private string empName;
private int salary;
public int EmpNo
{
get => empNo;
set => Set(ref empNo, value);
}
public string EmpName
{
get => empName;
set => Set(ref empName, value);
}
public int Salary
{
get => salary;
set => Set(ref salary, value);
}
}
4.
MainWindow
code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitData();
DataGrid.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
DataGrid.MouseMove += OnMouseMove;
DataGrid.DragEnter += OnDragEnter;
DataGrid.Drop += OnDrop;
}
private void InitData()
{
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
Employees.Add(new() { EmpNo = 101, EmpName = "Yudhistir", Salary = 56000 });
Employees.Add(new() { EmpNo = 102, EmpName = "Bhim", Salary = 36000 });
Employees.Add(new() { EmpNo = 103, EmpName = "Arjun", Salary = 45000 });
Employees.Add(new() { EmpNo = 104, EmpName = "Sahedev", Salary = 24000 });
Employees.Add(new() { EmpNo = 105, EmpName = "Nakul", Salary = 22000 });
}
public ObservableCollection<EmployeeModel> Employees { get; set; } = new();
private Point startPoint;
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
=> startPoint = e.GetPosition(null);
private void OnMouseMove(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
DataGridRow row = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
EmployeeModel employee = (EmployeeModel)DataGrid.ItemContainerGenerator.ItemFromContainer(row);
DataObject dragDataObject = new DataObject("DataRow", employee);
DragDrop.DoDragDrop(row, dragDataObject, DragDropEffects.Move);
}
}
private void OnDragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("DataRow") && sender != e.Source)
return;
e.Effects = DragDropEffects.None;
}
private void OnDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("DataRow"))
return;
EmployeeModel source = e.Data.GetData("DataRow") as EmployeeModel;
var row = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
var destination = (EmployeeModel)DataGrid.ItemContainerGenerator.ItemFromContainer(row);
Employees.RemoveAt(Employees.IndexOf(source));
Employees.Insert(Employees.IndexOf(destination) + 1, source);
}
private static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
do
{
if( current is T dependencyObject )
return dependencyObject;
current = VisualTreeHelper.GetParent(current);
}
while (current != null);
return null;
}
}
5.
MainWindow
XAML/UI
<Window x:Class="WpfScrollOnDragOverBehavior.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfScrollOnDragOverBehavior"
mc:Ignorable="d"
x:Name="Window"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox HorizontalAlignment="Stretch" Margin="10"
Text="EmployeeModel Information"
TextAlignment="Center" FontFamily="SimSun"
FontSize="28" />
<DataGrid x:Name="DataGrid"
Grid.Row="1"
ItemsSource="{Binding ElementName=Window, Path=Employees}"
AutoGenerateColumns="False"
HorizontalAlignment="Stretch" Margin="10"
ColumnWidth="*"
SelectionMode="Extended"
AllowDrop="True"
local:ScrollOnDragOverBehavior.IsScrollOnDragOverEnabled="True">
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding EmpNo}"
Header="ProductId"></DataGridTextColumn>
<DataGridTextColumn
Binding="{Binding EmpName}"
Header="ProductName"></DataGridTextColumn>
<DataGridTextColumn
Binding="{Binding Salary}"
Header="ProductPrice"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Now when you drag a
DataGridRow
to the edge of the
DataGrid
, the
DataGrid
will scroll. When you drop, The item that is being dragged will drop
after the target row.
BONUS
If you want to show the row being dragged next to the mouse cursor, have a look at Josh's excellent article
Drag and Drop Items in a WPF ListView[
^] - I will leave this one to you as it is outside the scope of this question.