Back to list
christian289

implementing-wpf-dragdrop

by christian289

ClaudeCode와 함께하는 .NET 개발 튜토리얼

1🍴 0📅 Jan 25, 2026

SKILL.md


name: implementing-wpf-dragdrop description: Implements WPF drag and drop functionality using DragDrop.DoDragDrop, DataObject, and drag/drop events. Use when building file drop zones, list reordering, or inter-application data transfer.

WPF Drag and Drop Patterns

Implementing drag and drop functionality for data transfer within and between applications.

Advanced Patterns: See ADVANCED.md for visual feedback, list reordering, and custom data formats.

1. Drag and Drop Overview

Drag Source                           Drop Target
    │                                      │
    ├─ MouseDown                           │
    ├─ MouseMove (drag threshold)          │
    ├─ DragDrop.DoDragDrop()───────────────┤
    │                                      ├─ DragEnter
    │                                      ├─ DragOver
    │                                      ├─ DragLeave
    │                                      └─ Drop
    └─ GiveFeedback (cursor change)

2. Basic Drag Source

2.1 Simple Text Drag

namespace MyApp.Views;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

public partial class DragSourceView : UserControl
{
    private Point _startPoint;

    private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        _startPoint = e.GetPosition(null);
    }

    private void TextBlock_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed)
            return;

        var currentPoint = e.GetPosition(null);
        var diff = _startPoint - currentPoint;

        // Check if drag threshold is exceeded
        if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
            Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
        {
            if (sender is TextBlock textBlock)
            {
                // Create data object and start drag
                var data = new DataObject(DataFormats.Text, textBlock.Text);
                DragDrop.DoDragDrop(textBlock, data, DragDropEffects.Copy);
            }
        }
    }
}

2.2 Multiple Data Formats

private void StartDragWithMultipleFormats(FrameworkElement source, object item)
{
    var data = new DataObject();

    // Add multiple formats for compatibility
    if (item is MyDataItem dataItem)
    {
        data.SetData(typeof(MyDataItem), dataItem);
        data.SetData(DataFormats.Text, dataItem.ToString());
        data.SetData(DataFormats.Serializable, dataItem);
    }

    DragDrop.DoDragDrop(source, data, DragDropEffects.Copy | DragDropEffects.Move);
}

3. Drop Target

3.1 Basic Drop Target

<Border x:Name="DropZone"
        AllowDrop="True"
        DragEnter="DropZone_DragEnter"
        DragOver="DropZone_DragOver"
        DragLeave="DropZone_DragLeave"
        Drop="DropZone_Drop"
        Background="LightGray"
        BorderBrush="Gray"
        BorderThickness="2">
    <TextBlock Text="Drop files here"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"/>
</Border>
private void DropZone_DragEnter(object sender, DragEventArgs e)
{
    // Check if data format is acceptable
    if (!e.Data.GetDataPresent(DataFormats.FileDrop) &&
        !e.Data.GetDataPresent(DataFormats.Text))
    {
        e.Effects = DragDropEffects.None;
    }
    else
    {
        e.Effects = DragDropEffects.Copy;
        HighlightDropZone(true);
    }
    e.Handled = true;
}

private void DropZone_DragOver(object sender, DragEventArgs e)
{
    // Continuously update effects based on position or modifier keys
    if (e.Data.GetDataPresent(DataFormats.FileDrop))
    {
        e.Effects = (e.KeyStates & DragDropKeyStates.ControlKey) != 0
            ? DragDropEffects.Copy
            : DragDropEffects.Move;
    }
    e.Handled = true;
}

private void DropZone_DragLeave(object sender, DragEventArgs e)
{
    HighlightDropZone(false);
}

private void DropZone_Drop(object sender, DragEventArgs e)
{
    HighlightDropZone(false);

    if (e.Data.GetDataPresent(DataFormats.FileDrop))
    {
        var files = (string[])e.Data.GetData(DataFormats.FileDrop);
        ProcessDroppedFiles(files);
    }
    else if (e.Data.GetDataPresent(DataFormats.Text))
    {
        var text = (string)e.Data.GetData(DataFormats.Text);
        ProcessDroppedText(text);
    }

    e.Handled = true;
}

4. File Drop Zone Control

namespace MyApp.Controls;

using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;

public sealed partial class FileDropZone : UserControl
{
    public static readonly DependencyProperty AcceptedExtensionsProperty =
        DependencyProperty.Register(
            nameof(AcceptedExtensions),
            typeof(string[]),
            typeof(FileDropZone),
            new PropertyMetadata(new[] { "*" }));

    public string[] AcceptedExtensions
    {
        get => (string[])GetValue(AcceptedExtensionsProperty);
        set => SetValue(AcceptedExtensionsProperty, value);
    }

    public event EventHandler<IReadOnlyList<string>>? FilesDropped;

    public FileDropZone()
    {
        InitializeComponent();
        AllowDrop = true;
    }

    protected override void OnDragEnter(DragEventArgs e)
    {
        ValidateAndSetEffects(e);
    }

    protected override void OnDragOver(DragEventArgs e)
    {
        ValidateAndSetEffects(e);
    }

    protected override void OnDrop(DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(DataFormats.FileDrop))
            return;

        var files = (string[])e.Data.GetData(DataFormats.FileDrop);
        var validFiles = FilterValidFiles(files);

        if (validFiles.Count > 0)
        {
            FilesDropped?.Invoke(this, validFiles);
        }
    }

    private void ValidateAndSetEffects(DragEventArgs e)
    {
        e.Effects = DragDropEffects.None;

        if (!e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            e.Handled = true;
            return;
        }

        var files = (string[])e.Data.GetData(DataFormats.FileDrop);

        if (FilterValidFiles(files).Count > 0)
        {
            e.Effects = DragDropEffects.Copy;
        }

        e.Handled = true;
    }

    private List<string> FilterValidFiles(string[] files)
    {
        var result = new List<string>();

        foreach (var file in files)
        {
            if (!File.Exists(file))
                continue;

            var extension = Path.GetExtension(file).ToLowerInvariant();

            if (AcceptedExtensions.Contains("*") ||
                AcceptedExtensions.Contains(extension))
            {
                result.Add(file);
            }
        }

        return result;
    }
}

Usage

<local:FileDropZone AcceptedExtensions=".jpg,.png,.gif"
                    FilesDropped="OnImagesDropped"
                    Width="300" Height="200">
    <TextBlock Text="Drop images here (.jpg, .png, .gif)"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"/>
</local:FileDropZone>

5. DragDropEffects

EffectDescriptionCursor
NoneDrop not allowedNo-drop cursor
CopyCopy dataCopy cursor (+)
MoveMove dataMove cursor
LinkCreate linkLink cursor
ScrollScrolling occurringScroll cursor
AllCopy, Move, Scroll combined-
private void DropZone_DragOver(object sender, DragEventArgs e)
{
    // Set effect based on modifier keys
    if ((e.KeyStates & DragDropKeyStates.ControlKey) != 0)
    {
        e.Effects = DragDropEffects.Copy;
    }
    else if ((e.KeyStates & DragDropKeyStates.ShiftKey) != 0)
    {
        e.Effects = DragDropEffects.Move;
    }
    else if ((e.KeyStates & DragDropKeyStates.AltKey) != 0)
    {
        e.Effects = DragDropEffects.Link;
    }
    else
    {
        e.Effects = DragDropEffects.Move; // Default
    }
}

6. References

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon