28 Nov 2017 - Matthias Voigt
Drag&Drop ist eine grundlegende Funktionalität in vielen Desktop-Anwendungen. In WPF (Windows Presentation Foundation) ist die Implementierung von Drag&Drop relativ einfach und flexibel. Hier zeige ich, wie man Drag&Drop in einer WPF-Anwendung implementiert.
Nehmen wir an, wir haben ein Projekt mit mehreren ViewModels, die jeweils unterschiedliche Navigationselemente darstellen. Hier ist ein Beispielcode, der zeigt, wie man diese Elemente in eine allgemeine NavigationItemBase umwandeln kann:
public NavigationItemBase ToNavigationItem()
{
switch (this)
{
case NavigationPageViewModel pageItem:
return new NavigationPage { Title = Title, Name = pageItem.Name };
case NavigationListViewModel listItem:
var item = new NavigationList { Title = Title };
foreach (var subItem in listItem.SubItems)
item.SubItems.Add(subItem.ToNavigationItem());
return item;
case NavigationUriViewModel uriItem:
return new NavigationUri { Title = Title, Uri = uriItem.Uri };
default:
throw new ArgumentOutOfRangeException();
}
}
Um Drag&Drop in WPF zu verwenden, müssen wir die entsprechenden Ereignisse auf den Steuerelementen festlegen, die als Drag-Quelle und Drop-Ziel dienen.
<ListBox x:Name="SourceListBox"
PreviewMouseLeftButtonDown="SourceListBox_PreviewMouseLeftButtonDown"
PreviewMouseMove="SourceListBox_PreviewMouseMove" />
<ListBox x:Name="TargetListBox"
AllowDrop="True"
Drop="TargetListBox_Drop"
DragOver="TargetListBox_DragOver" />
Im Code-Behind der entsprechenden XAML-Datei definieren wir die Ereignishandler:
private Point _startPoint;
private void SourceListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
private void SourceListBox_PreviewMouseMove(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))
{
// Get the dragged ListBoxItem
ListBox listBox = sender as ListBox;
ListBoxItem listBoxItem = FindAncestor<ListBoxItem>((DependencyObject)e.OriginalSource);
if (listBoxItem == null)
return;
// Find the data behind the ListBoxItem
object data = listBox.ItemContainerGenerator.ItemFromContainer(listBoxItem);
// Initialize the drag & drop operation
DragDrop.DoDragDrop(listBoxItem, data, DragDropEffects.Move);
}
}
private void TargetListBox_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(NavigationItemBase)))
{
NavigationItemBase data = e.Data.GetData(typeof(NavigationItemBase)) as NavigationItemBase;
ListBox listBox = sender as ListBox;
listBox.Items.Add(data);
}
}
private void TargetListBox_DragOver(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(NavigationItemBase)) || sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
else
{
e.Effects = DragDropEffects.Move;
}
e.Handled = true;
}
private static T FindAncestor<T>(DependencyObject current) where T : DependencyObject
{
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
Mit diesen Schritten haben wir eine einfache Drag&Drop-Funktionalität in einer WPF-Anwendung implementiert. Die obigen Beispiele zeigen, wie man Ereignishandler für die Drag-Quelle und das Drop-Ziel einrichtet und wie man die Daten zwischen den ListBoxen verschiebt.
Drag&Drop in WPF ist mächtig und flexibel. Mit ein wenig Aufwand kann man diese Funktionalität in seine Anwendungen integrieren und so die Benutzerfreundlichkeit erheblich verbessern. Probier es aus und integriere Drag&Drop in deine eigenen WPF-Projekte!