From wpf-dev-pack
Implements drag and drop in WPF apps using DragDrop.DoDragDrop, DataObject, and events for file drop zones, list reordering, inter-app data transfer.
npx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packThis skill uses the workspace's default tool permissions.
Implementing drag and drop functionality for data transfer within and between applications.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
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.
Drag Source Drop Target
│ │
├─ MouseDown │
├─ MouseMove (drag threshold) │
├─ DragDrop.DoDragDrop()───────────────┤
│ ├─ DragEnter
│ ├─ DragOver
│ ├─ DragLeave
│ └─ Drop
└─ GiveFeedback (cursor change)
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);
}
}
}
}
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);
}
<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;
}
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;
}
}
<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>
| Effect | Description | Cursor |
|---|---|---|
| None | Drop not allowed | No-drop cursor |
| Copy | Copy data | Copy cursor (+) |
| Move | Move data | Move cursor |
| Link | Create link | Link cursor |
| Scroll | Scrolling occurring | Scroll cursor |
| All | Copy, 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
}
}