I am trying to create a custom control in WPF that is an ItemsControl (with a customizable data template) that supports dragging items from one container to another. The dragging logic is pretty straight forward and I have managed to get that working.
The problem is that I'm trying to show a simple drag adorner (that's essentially a screenshot of the item/datatemplate being dragged). While I have managed to display the adorner and get it to follow the mouse cursor, it is extremely laggy. I have played around with two methods of building the adorner - first one would be attaching a content presenter to my custom adorner; the second would be actually overriding the OnRender method and drawing it myself. Both methods feature really poor performance.
This is how I've implemented my adorner:
public class ActionDragAdorner: Adorner
{
private VisualCollection _Visuals;
private ContentPresenter _ContentPresenter;
private Rectangle _rect;
public FrameworkElement AdornedElement { get; protected set; }
public Point InitialClickLocation { get; set; }
public Point CentralOffset
{
get
{
return new Point(-_rect.Width / 2, -_rect.Height / 2);
}
}
public ActionDragAdorner(FrameworkElement adornedElement)
: base(adornedElement)
{
_Visuals = new VisualCollection(this);
_ContentPresenter = new ContentPresenter();
_Visuals.Add(_ContentPresenter);
AdornedElement = adornedElement;
_rect = new Rectangle();
_rect.Width = adornedElement.ActualWidth;
_rect.Height = adornedElement.ActualHeight;
_rect.Fill = new VisualBrush(adornedElement);
IsHitTestVisible = false;
Content = _rect;
_ContentPresenter.Arrange(new Rect(0, 0, _rect.Width, _rect.Height));
this.Width = _rect.Width;
this.Height = _rect.Height;
}
public ActionDragAdorner(FrameworkElement adornedElement, Visual content)
: this(adornedElement)
{
Content = content;
}
protected override Size MeasureOverride(Size constraint)
{
_ContentPresenter.Measure(constraint);
return new Size(AdornedElement.ActualWidth, AdornedElement.ActualHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
_ContentPresenter.Arrange(new Rect(0, 0,
finalSize.Width, finalSize.Height));
return new Size(AdornedElement.ActualWidth, AdornedElement.ActualHeight);
}
protected override Visual GetVisualChild(int index)
{ return _Visuals[index]; }
protected override int VisualChildrenCount
{ get { return _Visuals.Count; } }
public object Content
{
get { return _ContentPresenter.Content; }
set { _ContentPresenter.Content = value; }
}
}
I'm starting the dragging operation in the PreviewMouseMove event, when the left button is pressed. Due to the fact the the DragDrop.DoDragDrop is blocking, the only way to update the position of the adorner (to track the mouse cursor) is by overriding the OnGiveFeedback event for my custom control:
protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
{
base.OnGiveFeedback(e);
GetCursorPos(ref pointRef);
Point relPos = this.PointFromScreen(pointRef.GetPoint());
Point elementPos = dragAdorner.AdornedElement.TranslatePoint(new Point(0, 0), this);
Point initialClick = dragAdorner.InitialClickLocation;
Point pos = new Point(relPos.X - initialClick.X,
relPos.Y - elementPos.Y - initialClick.Y);
Rect target = new Rect(pos, dragAdorner.DesiredSize);
dragAdorner.Arrange(target);
}
Focusing on user experience, we need to have a fluid adorner following the cursor - especially since the adorner itself is really simple - a border with a textblock inside. During performance profiling it seems that the UI thread has zero FPS dropouts that seem to be caused by too many layout updates (due to the Arrange call used to reposition the adorner). I've tried everything I can think of, including manually doing the render transformations. If dragging the item slowly, performance seems to be ok - however if I move the mouse faster the UI thread drops to zero FPS - probably trying to do too many layout updates too quickly; this is also backed up by the performance profiler, as during these zero FPS moments, only layout update calls are handled (with no render calls)
I've also looked at other samples online that feature drag and drop operations using adorners, but these also seem to be laggy.
The quesion: How can I make the adorner follow the mouse cursor in a fluent fashion, without choppy actions and decent FPS?