punker76/gong-wpf-dragdrop

Code submit: display visual of dragged item instead of using template

punker76 opened this issue · 3 comments

Original author: cborde...@gmail.com (June 28, 2011 17:10:07)

I made a quick modification to the DragDrop.CreateDragAdorner() method so that by default (when an adorner template is not specified), an image is extracted from the dragged item(s) and that is used as the adorner instead.

I'm not sure why this was never added as a feature before, it's so easy to implement...

Here is my modification to that method, plus a new helper method to do the image generation:

static void CreateDragAdorner()
{
    var template = GetDragAdornerTemplate(m_DragInfo.VisualSource);

    // This block was added to create a Data Template on the fly with an image generated
    // from m_DragInfo.VisualSourceItem. -- Chris Bordeman cbordeman@gmail.com
    if (template == null)
    {
        template = new DataTemplate();

        var factory = new FrameworkElementFactory(typeof(Image));

        var bs = CaptureScreen(m_DragInfo.VisualSourceItem);
        factory.SetValue(Image.SourceProperty,  bs);
        if (m_DragInfo.VisualSourceItem is FrameworkElement)
        {
            factory.SetValue(FrameworkElement.WidthProperty,((FrameworkElement) m_DragInfo.VisualSourceItem).ActualWidth);
            factory.SetValue(FrameworkElement.HeightProperty, ((FrameworkElement)m_DragInfo.VisualSourceItem).ActualHeight);
        }
        template.VisualTree = factory;
    }

    // The rest of the code was originally inside an "if (template != null)" 
    // block. -- Chris Bordeman cbordeman@gmail.com

    UIElement rootElement = null;
    Window parentWindow = m_DragInfo.VisualSource.GetVisualAncestor<Window>();
    UIElement adornment = null;

    if (parentWindow != null)
    {
        rootElement = parentWindow.Content as UIElement;
    }
    if (rootElement == null)
    {
        rootElement = (UIElement)Application.Current.MainWindow.Content;
    }

    if (m_DragInfo.Data is IEnumerable && !(m_DragInfo.Data is string))
    {
        if (((IEnumerable)m_DragInfo.Data).Cast<object>().Count() <= 10)
        {
            ItemsControl itemsControl = new ItemsControl();
            itemsControl.ItemsSource = (IEnumerable)m_DragInfo.Data;
            itemsControl.ItemTemplate = template;

            // The ItemsControl doesn't display unless we create a border to contain it.
            // Not quite sure why this is...
            Border border = new Border();
            border.Child = itemsControl;
            adornment = border;
        }
    }
    else
    {
        ContentPresenter contentPresenter = new ContentPresenter();
        contentPresenter.Content = m_DragInfo.Data;
        contentPresenter.ContentTemplate = template;
        adornment = contentPresenter;
    }

    if (adornment != null)
    {
        adornment.Opacity = 0.5;
        DragAdorner = new DragAdorner(rootElement, adornment);
    }
}

// Helper to generate the image - I grabbed this off Google 
// somewhere. -- Chris Bordeman cbordeman@gmail.com
private static BitmapSource CaptureScreen(Visual target, double dpiX = 96.0, double dpiY = 96.0)
{
    if (target == null)
        return null;

    var bounds = VisualTreeHelper.GetDescendantBounds(target);

    var rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
                                     (int)(bounds.Height * dpiY / 96.0),
                                     dpiX,
                                     dpiY,
                                     PixelFormats.Pbgra32);

    var dv = new DrawingVisual();
    using (var ctx = dv.RenderOpen())
    {
        var vb = new VisualBrush(target);
        ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
    }

    rtb.Render(dv);

    return rtb;
}

Original issue: http://code.google.com/p/gong-wpf-dragdrop/issues/detail?id=38

From cborde...@gmail.com on June 28, 2011 17:44:10
One mistake: should also set the alignments of the generated image control in addition to setting the ActualWidth/Height:

factory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Left);
factory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Top);

From aedward...@gmail.com on July 14, 2011 01:19:00
This looks pretty cool. Any chance of getting it into the next build?

improved in version 0.1.3.6