PowerPoint并没有直接暴露这些事件,但是可以通过将全局鼠标钩子与PowerPoint公开的形状参数相结合来实现自己的事件。
本答案涵盖了处理MouseEnter和MouseLeave等其他事件更困难的情况。要处理MouseDown和MouseUp等其他事件,可以使用提供的PowerPoint事件Application_WindowSelectionChange,就像Steve Rindsberg所建议的那样。
要获取全局鼠标钩子,您可以使用在http://globalmousekeyhook.codeplex.com/找到的出色的C#“应用程序和全局鼠标键盘钩子”库。
您需要下载并引用该库,然后使用以下代码设置鼠标钩子:
// 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和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)
{
List<PPShape> activeShapes = new List<PPShape>();
foreach (PPShapes in _shapesOnSlide)
{
if (MouseWithinShape(s, e))
{
activeShapes.Add(s);
}
}
foreach (PPShape s in activeShapes)
{
if (!_shapesEntered.Contains(s))
{
s.OnMouseEntered();
_shapesEntered.Add(s);
}
}
HashSet<long> activeIds = new HashSet<long>(activeShapes.Select(s => s.Id));
_shapesEntered.RemoveAll(s => {
if (!activeIds.Contains(s.Id)) {
s.OnMouseLeave();
return true;
}
else
{
return false;
}
});
}
其中_shapesOnSlide
是一个列表,包含当前幻灯片上的所有形状,_shapesEntered
是一个列表,包含已输入但尚未离开的形状,PPShape
是PowerPoint形状的包装对象(如下所示)。
MouseWithinShape方法测试鼠标是否在幻灯片上的形状内。它通过将形状的X和Y坐标(PowerPoint以点为单位公开)转换为屏幕坐标,并测试鼠标是否在该边界框内来实现。
private bool MouseWithinShape(PPShape shape, System.Windows.Forms.MouseEventArgs e)
{
RectangleF shapeRect = shape.Rectangle;
Rectangle shapeRectInScreenPixels = PointsToScreenPixels(shapeRect);
return shapeRectInScreenPixels.Contains(e.Location);
}
private Rectangle PointsToScreenPixels(RectangleF shapeRectangle)
{
int x1 = pointsToScreenPixelsX(shapeRectangle.X);
int y1 = pointsToScreenPixelsY(shapeRectangle.Y);
int x2 = pointsToScreenPixelsX(shapeRectangle.X + shapeRectangle.Width);
int y2 = pointsToScreenPixelsY(shapeRectangle.Y + shapeRectangle.Height);
x1 -= 6;
x2 += 6;
y1 -= 6;
y2 += 6;
return new Rectangle(x1, y1, x2-x1, y2-y1);
}
private int pointsToScreenPixelsX(float point)
{
return PresStatic.Windows[1].PointsToScreenPixelsX(point);
}
private int pointsToScreenPixelsY(float point)
{
return PresStatic.Windows[1].PointsToScreenPixelsY(point);
}
最后,我们实现了一个自定义的
PPShape
对象,它公开了我们想要监听的事件。
using System.Drawing;
using Microsoft.Office.Interop.PowerPoint;
using System;
namespace PowerPointDynamicLink.PPObject
{
class PPShape
{
#region Fields
protected Shape shape;
public Shape Shape
{
get { return shape; }
}
protected long id;
public long Id
{
get { return id; }
set { id = value; }
}
protected string name;
public string Name
{
get { return name; }
set { name = value; }
}
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
public PPShape(Shape shape)
{
this.shape = shape;
this.name = shape.Name;
this.id = shape.Id;
}
#endregion
#region Event handling
#region MouseEntered
public event EventHandler MouseEntered = delegate { };
internal void OnMouseEntered()
{
MouseEntered(this, new EventArgs());
}
#endregion
#region MouseLeave
public event EventHandler MouseLeave = delegate { };
internal void OnMouseLeave()
{
MouseLeave(this, new EventArgs());
}
#endregion
#endregion
}
}
为了完全详尽,还需要处理多个额外元素,这些元素在此处没有涉及。这包括暂停鼠标钩子(mouse hook)当PowerPoint窗口失活时,处理多个PowerPoint窗口和多个屏幕等。