I was recently adding some drag/drop functionality to a usercontrol that I'm creating, and I decided that it made sense to factor the specifics of drag/drop out for subsequent testing, etc. The core logic is in DragOnMouseMove<TInput>()
.
(Formatting trimmed for brevity)
internal class DragState
{
private static readonly Size deadZoneSize = SystemInformation.DragSize;
private Rectangle dragDeadZone = Rectangle.Empty;
private Control owningControl = null;
public BlockTypeDragState(Control owner)
{
// null check boilerplate here
this.owningControl = owner;
return;
}
private bool IsActive
{
get { return this.dragDeadZone != Rectangle.Empty; }
}
public void DragOnMouseMove(
Point mouseAt,
DragDropEffects allowedEffects,
Func<object> generateDragData)
{
this.DragOnMouseMove<object>(
mouseAt,
allowedEffects,
null,
s => generateDragData());
return;
}
public void DragOnMouseMove<TInput>(
Point mouseAt,
DragDropEffects allowedEffects,
TInput generateParm,
Func<TInput, object> generateDragData)
{
// boilerplate null check on generateDragData
if (this.DragShouldStart(mouseAt))
{
object data = generateDragData(generateParm);
this.owningControl.DoDragDrop(data, allowedEffects);
this.Reset();
}
return;
}
public void ListenForDrag(int x, int y)
{
this.dragDeadZone = new Rectangle(new Point(x - (deadZoneSize.Width / 2), y - (deadZoneSize.Height / 2)), deadZoneSize);
return;
}
public void Reset() { this.dragDeadZone = Rectangle.Empty; }
private bool DragShouldStart(Point mouseClientLocation)
{
if (this.IsActive && !this.dragDeadZone.Contains(mouseClientLocation))
{ return true; }
return false;
}
}
Usage would then be something like:
private DragState dragState = new DragState(this); // pseudo
// MouseMove event handler
private void HandleMouseMove(object sender, MouseEventArgs e)
{
// Perform the drag/drop.
this.dragState.DragOnMouseMove(
new Point(e.X, e.Y),
DragDropEffects.Copy,
() => this.BuildDataToDragDrop());
return;
}
private void HandleMouseDown(object sender, MouseEventArgs e)
{
if (this.CanDragDrop(sender, e)) this.dragState.ListenForDrag(e.X, e.Y);
}
private void HandleMouseUp(object sender, MouseEventargs e)
{
this.dragState.Reset();
}
Given that I haven't generalized it yet (if comments here are positive and I need something like this a second time, I probably will), is this reasonable for production code, or am I being a Complicator? If you saw this in code you were maintaining, would you call me nasty names? :)
All comments are welcome, including name suggestions. (I don't really like DragState
) :)
For example, assuming that this is a good idea at all, I'm not sure yet whether it makes more sense to make the extensibility points functional in parms (via Func<>
and Action<>
like it is now) or OO (via an abstract DragStateBase
with mandatory overrides like OnGenerateData<TInput>(TInput parm)
and OnDropCompleted(DragDropEffects result)
.)