Drag&Drop mit WPF

Drag&Drop mit WPF

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.

Implementierung von Drag&Drop

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();
    }
}

Drag&Drop in WPF

Schritt 1: Festlegen der Drag&Drop-Ereignisse

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" />

Schritt 2: Ereignishandler im Code-Behind

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.

Fazit

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!