1
votes

I would like to implement a PowerPoint add-in with a custom task pane from which the users can drag complex objects objects onto the slide (these objects would consist of multiple shapes, for instance two triangles in a circle or something like that).

I managed to achieve dropping simple text boxes onto slides by calling DoDragDrop in a user control and passing the desired text as the method's first parameter. However I don't know if it's possible to pass a more copmlex object like multiple shapes as the data parameter of the DoDragDrop method.

The other approach I tried is to call DoDragDrop with an empty string (that way nothing gets dropped onto the slide), and after DoDragDrop returned I can add shapes to the slide using the Globals.ThisAddIn.Application.ActiveWindow.View.Slide.AddShape method, however I couldn't find a way to get the current position of the mouse pointer.

So is it somehow possible to call DoDragDrop and pass multiple shapes to it, or get the cursor position after DoDragDrop returned?

UPDATE: I found a solution for PowerPoint 2013, it can be done with the new AfterDragDropOnSlide event (it's not trivial though, details here: http://social.msdn.microsoft.com/Forums/en-US/724f1737-afa5-4762-8740-5b3745e06f8a/afterdragdroponslide-event-in-ppt-2013?forum=isvvba).

So the question is that is the same thing possible in PowerPoint 2010?

1

1 Answers

3
votes

After calling DoDragDrop with a String.Empty first parameter, it is possible to get the current cursor coordinates.
Getting the coordinates needs some Win32 boilerplate:

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int X;
    public int Y;

    public POINT(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

class Win32API
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetCursorPos(out POINT lpPoint);
}

private POINT GetCursorPosition()
{
    POINT point = new POINT();

    Win32API.GetCursorPos(out point);

    Win32API.ScreenToClient(new IntPtr(Globals.ThisAddIn.Application.ActiveWindow.HWND), ref point);

    return point;
}

Then these coordinates have to be converted into the slide's coordinate system (the slide has a different size in points than how it appears on the screen in pixels).

private POINT ConvertScreenPointToSlideCoordinates(POINT point)
{
    // Get the screen coordinates of the upper-left hand corner of the slide.
    POINT refPointUpperLeft = new POINT(
        Globals.ThisAddIn.Application.ActiveWindow.PointsToScreenPixelsX(0),
        Globals.ThisAddIn.Application.ActiveWindow.PointsToScreenPixelsY(0));

    // Get the size of the slide (in points of the slide's coordinate system).
    var slide = Globals.ThisAddIn.Application.ActiveWindow.View.Slide;
    var slideWidth = slide.CustomLayout.Width;
    var slideHeight = slide.CustomLayout.Height;

    // Get the screen coordinates of the bottom-right hand corner of the slide.
    POINT refPointBottomRight = new POINT(
        Globals.ThisAddIn.Application.ActiveWindow.PointsToScreenPixelsX(slideWidth),
        Globals.ThisAddIn.Application.ActiveWindow.PointsToScreenPixelsY(slideHeight));

    // Both reference points have to be converted to the PowerPoint window's coordinate system.
    Win32API.ScreenToClient(new IntPtr(Globals.ThisAddIn.Application.ActiveWindow.HWND), ref refPointUpperLeft);
    Win32API.ScreenToClient(new IntPtr(Globals.ThisAddIn.Application.ActiveWindow.HWND), ref refPointBottomRight);

    // Convert the point to the slide's coordinate system (convert the pixel coordinate inside the slide into a 0..1 interval, then scale it up by the slide's point size).
    return new POINT(
        (int)Math.Round((double)(point.X - refPointUpperLeft.X) / (refPointBottomRight.X - refPointUpperLeft.X) * slideWidth),
        (int)Math.Round((double)(point.Y - refPointUpperLeft.Y) / (refPointBottomRight.Y - refPointUpperLeft.Y) * slideHeight));
}

The code doing the drag&drop:

DoDragDrop(String.Empty, DragDropEffects.Copy);

var point = GetCursorPosition();
var convertedPoint = this.ConvertScreenPointToSlideCoordinates(point);

// Insert something at the cursor's location:
var slide = Globals.ThisAddIn.Application.ActiveWindow.View.Slide;
slide.Shapes.AddShape(Microsoft.Office.Core.MsoAutoShapeType.msoShapeCloud, convertedPoint.X, convertedPoint.Y, 100, 100);