通过VSTO在PowerPoint设计师捕捉鼠标事件鼠标、设计师、事件、VSTO

2023-09-04 22:37:54 作者:再也想不到合适的了

我开发一个附加在PowerPoint中(2013年)用C#/ VSTO。该加载项将工作当用户在的的设计模式,而不是presentation模式的。

I am developing an add-in for PowerPoint (2013) with C# / VSTO. The add-in will work when the user is in design mode, not presentation mode.

我如何能赶上鼠标事件对于形状/幻灯片上的对象,例如鼠标悬停,鼠标按下等?我要听这些事件,以创造靠近物体/形状定制UI。是否有任何事件,我可以听,还是需要使用更先进的方法,如创建一个全球性的鼠标侦听器,转换坐标到PowerPoint中的形状,并通过幻灯片上的形状循环,看看鼠标是否在的任何形状的边界?我也将AP preciate其他创造性的解决问题的方法。

How can I catch mouse events with regard to shapes/objects on the slides, e.g. mouseOver, mouseDown etc? I want to listen to these events in order to create custom UI located near the objects / shapes. Are there any events I can listen to, or is it necessary to use more advanced methods, such as creating a global mouse listener, translate the coordinates to the PowerPoint shapes, and loop through the shapes on the slide to see whether the mouse is within the boundaries of any of the shapes? I will also appreciate other creative solutions to the problem.

我试图寻找没有任何的运气答案。但是,我知道这是某种可能的,因为其他加载项正在做什么,我想要的。一个例子是想节( https://www.youtube.com/watch?v=qsnciEZi5X0 ),在那里你操纵的对象是普通PowerPoint对象,如TextFrames和型材。

I have tried to search for an answer without any luck. However, I know that it is somehow possible because other add-ins are doing what I want. One example is Think-Cell (https://www.youtube.com/watch?v=qsnciEZi5X0), where the objects you manipulate are "ordinary" PowerPoint objects such as TextFrames and Shapes.

我与.net 4.5的Windows 8.1 Pro的工作。

I'm working with .Net 4.5 on Windows 8.1 Pro.

推荐答案

PowerPoint不公开直接这些事件,但它可以通过组合全局鼠标钩子与PowerPoint中暴露形状参数来实现自己的活动。

PowerPoint does not expose these events directly, but it is possible to implement your own events by combining global mouse hooks with shape parameters that PowerPoint expose.

这答案覆盖比较困难的情况下用的MouseEnter和鼠标离开。处理其他事件,如的MouseDown和MouseUp,你可以利用所提供的PowerPoint事件的 Application_WindowSelectionChange 史蒂夫·Rindsberg建议。

This answer covers the more difficult case with MouseEnter and MouseLeave. To handle other events such as MouseDown and MouseUp, you can make use of the provided PowerPoint event Application_WindowSelectionChange as Steve Rindsberg proposed.

要得到全局鼠标钩子,你可以在C#在 HTTP://globalmousekeyhook.$c$cplex.com/

To get a global mouse hook, you can use excellent Application and Global Mouse and Keyboard Hooks .Net Libary in C# found at http://globalmousekeyhook.codeplex.com/

您需要下载并参考库,然后在设置鼠标钩子使用此code

You will need to download and reference the library, and then you set up the mouse hook by using this code

// Initialize the global mouse hook
_mouseHookManager = new MouseHookListener(new AppHooker());
_mouseHookManager.Enabled = true;

// Listen to the mouse move event
_mouseHookManager.MouseMove += MouseHookManager_MouseMove;

MouseMove事件可以被设置为处理这样​​的鼠标事件(例如只的MouseEnter和鼠标离开)

The MouseMove event could be set up to handle the mouse events like this (example with only MouseEnter and MouseLeave)

private List<PPShape> _shapesEntered = new List<PPShape>();       
private List<PPShape> _shapesOnSlide = new List<PPShape>();

void MouseHookManager_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
    // Temporary list holding active shapes (shapes with the mouse cursor within the shape)
    List<PPShape> activeShapes = new List<PPShape>();       

    // Loop through all shapes on the slide, and add active shapes to the list
    foreach (PPShapes in _shapesOnSlide)
    {
        if (MouseWithinShape(s, e))
        {
            activeShapes.Add(s);
        }
    }

    // Handle shape MouseEnter events
    // Select elements that are active but not currently in the shapesEntered list
    foreach (PPShape s in activeShapes)
    {
        if (!_shapesEntered.Contains(s))
        {
            // Raise your custom MouseEntered event
            s.OnMouseEntered();

            // Add the shape to the shapesEntered list
            _shapesEntered.Add(s);
        }
    }

    // Handle shape MouseLeave events
    // Remove elements that are in the shapes entered list, but no longer active
    HashSet<long> activeIds = new HashSet<long>(activeShapes.Select(s => s.Id));
    _shapesEntered.RemoveAll(s => {
        if (!activeIds.Contains(s.Id)) {
            // The mouse is no longer over the shape
            // Raise your custom MouseLeave event
            s.OnMouseLeave();

            // Remove the shape
            return true;
        }
        else
        {
            return false;
        }
    });
}

其中 _shapesOnSlide 是一个列表保存所有形状上的当前幻灯片, _shapesEntered 与名单 PPShape 已输入,但没有离开,和形状是用于PowerPoint形状的包装对象(如下所示)

where the _shapesOnSlide is a list holding all the shapes on the current slide, _shapesEntered is a list with the shapes that have been entered but not yet left, and PPShape is a wrapper object for PowerPoint shapes (seen below)

该MouseWithinShape方法测试鼠标当前是否在幻灯片上的形状中。它是通过平移形状的X和Y坐标(在积分单元的PowerPoint暴露)到屏幕坐标,并且测试鼠标是否是内边界框

The MouseWithinShape method tests whether the mouse is currently within a shape on the slide. It does so by translating the shapes X- and Y-coordinates (which PowerPoint exposes in Points unit) to screen coordinates, and the tests whether the mouse is within that bounding box

/// <summary>
/// Test whether the mouse is within a shape
/// </summary>
/// <param name="shape">The shape to test</param>
/// <param name="e">MouseEventArgs</param>
/// <returns>TRUE if the mouse is within the bounding box of the shape; FALSE otherwise</returns>
private bool MouseWithinShape(PPShape shape, System.Windows.Forms.MouseEventArgs e)
{
    // Fetch the bounding box of the shape
    RectangleF shapeRect = shape.Rectangle;

    // Transform to screen pixels
    Rectangle shapeRectInScreenPixels = PointsToScreenPixels(shapeRect);

    // Test whether the mouse is within the bounding box
    return shapeRectInScreenPixels.Contains(e.Location);
}

/// <summary>
/// Transforms a RectangleF with PowerPoint points to a Rectangle with screen pixels
/// </summary>
/// <param name="shapeRectangle">The Rectangle in PowerPoint point-units</param>
/// <returns>A Rectangle in screen pixel units</returns>
private Rectangle PointsToScreenPixels(RectangleF shapeRectangle)
{
    // Transform the points to screen pixels
    int x1 = pointsToScreenPixelsX(shapeRectangle.X);
    int y1 = pointsToScreenPixelsY(shapeRectangle.Y);
    int x2 = pointsToScreenPixelsX(shapeRectangle.X + shapeRectangle.Width);
    int y2 = pointsToScreenPixelsY(shapeRectangle.Y + shapeRectangle.Height);

    // Expand the bounding box with a standard padding
    // NOTE: PowerPoint transforms the mouse cursor when entering shapes before it actually
    // enters the shape. To account for that, add this extra 'padding'
    // Testing reveals that the current value (in PowerPoint 2013) is 6px
    x1 -= 6;
    x2 += 6;
    y1 -= 6;
    y2 += 6;

    // Return the rectangle in screen pixel units
    return new Rectangle(x1, y1, x2-x1, y2-y1);

}

/// <summary>
/// Transforms a PowerPoint point to screen pixels (in X-direction)
/// </summary>
/// <param name="point">The value of point to transform in PowerPoint point-units</param>
/// <returns>The screen position in screen pixel units</returns>
private int pointsToScreenPixelsX(float point)
{
    // TODO Handle multiple windows
    // NOTE: PresStatic is a reference to the PowerPoint presentation object
    return PresStatic.Windows[1].PointsToScreenPixelsX(point);
}

/// <summary>
/// Transforms a PowerPoint point to screen pixels (in Y-direction)
/// </summary>
/// <param name="point">The value of point to transform in PowerPoint point-units</param>
/// <returns>The screen position in screen pixel units</returns>
private int pointsToScreenPixelsY(float point)
{
    // TODO Handle multiple windows
    // NOTE: PresStatic is a reference to the PowerPoint presentation object
    return PresStatic.Windows[1].PointsToScreenPixelsY(point);
}

最后,我们实现一个自定义的 PPShape 对象公开,我们要听的事件

Finally we implement a custom PPShape object that exposes the events we want to listen to

using System.Drawing;
using Microsoft.Office.Interop.PowerPoint;
using System;

namespace PowerPointDynamicLink.PPObject
{
    class PPShape
    {
        #region Fields
        protected Shape shape;

        /// <summary>
        /// Get the PowerPoint Shape this object is based on
        /// </summary>
        public Shape Shape
        {
            get { return shape; }
        }

        protected long id;
        /// <summary>
        /// Get or set the Id of the Shape
        /// </summary>
        public long Id
        {
            get { return id; }
            set { id = value; }
        }

        protected string name;
        /// <summary>
        /// Get or set the name of the Shape
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// Gets the bounding box of the shape in PowerPoint Point-units
        /// </summary>
        public RectangleF Rectangle
        {
            get
            {
                RectangleF rect = new RectangleF
                {
                    X = shape.Left,
                    Y = shape.Top,
                    Width = shape.Width,
                    Height = shape.Height
                };

                return rect;
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Creates a new PPShape object
        /// </summary>
        /// <param name="shape">The PowerPoint shape this object is based on</param>
        public PPShape(Shape shape)
        {
            this.shape = shape;
            this.name = shape.Name;
            this.id = shape.Id;
        }
        #endregion

        #region Event handling
        #region MouseEntered
        /// <summary>
        /// An event that notifies listeners when the mouse has entered this shape
        /// </summary>
        public event EventHandler MouseEntered = delegate { };

        /// <summary>
        /// Raises an event telling listeners that the mouse has entered this shape
        /// </summary>
        internal void OnMouseEntered()
        {
            // Raise the event
            MouseEntered(this, new EventArgs());
        }
        #endregion

        #region MouseLeave
        /// <summary>
        /// An event that notifies listeners when the mouse has left this shape
        /// </summary>
        public event EventHandler MouseLeave = delegate { };

        /// <summary>
        /// Raises an event telling listeners that the mouse has left this shape
        /// </summary>
        internal void OnMouseLeave()
        {
            // Raise the event
            MouseLeave(this, new EventArgs());
        }
        #endregion
        #endregion
    }
}

要完全详尽的,但是也有一些未涉及,应处理多个额外的元素。这包括诸如暂停鼠标钩子,当PowerPoint窗口停用,处理多个PowerPoint演示窗口和多个屏幕等。

To be completely exhaustive, there are multiple extra elements that should be handled that are not covered here. This includes things such as pausing the mouse hook when the PowerPoint window deactivates, handling multiple PowerPoint windows and multiple screens etc.