65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Win32 Interop 确定 Excel 是否处于编辑模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (6投票s)

2009年4月13日

CPOL

3分钟阅读

viewsIcon

47853

downloadIcon

763

在本文中,人们可以找到一个可能的解决方案,用于检查或接收 Excel 应用程序是否处于编辑模式的通知

引言

在提出我们自己的任何解决方案之前,通常首先检查网络以查找是否已经存在官方解决方案。
关于这个问题,每个人都可以找到一些文章,社区论坛聊天,其中有很多关于如何规避 Excel COM API 无法确定 Excel 应用程序本身是否处于编辑模式的想法。

您可以在此处查看历史记录。

背景

编辑模式到底意味着什么?当用户双击或开始在单元格上输入,按下 F2 键或使用工具栏下方的编辑器栏时,Excel 就会启动编辑模式。

当编辑模式开启时,一些菜单项和功能将无法使用,这表明应用程序处于编辑模式(菜单项被禁用)。
这与在 Visual Basic for Application 模块中编写宏并希望启动它的效果相同。
基本上,这很好,但如果您想编写一个加载项代码模块,情况就不再如此,从加载项代码调用一些内置功能会导致异常,或者您的自定义菜单、工具栏项仍然启用,并且与内置工具栏项不同步。
没有事件或 API 属性可以指示 Excel 进入编辑模式,从而使您能够及时禁用您的自定义内容。

即使 Application.Isready 属性 也并不总是有效,并且从不同的线程或通过计时器轮询属性并不是最佳方法。

因此,我们需要的是在编辑模式开启或关闭时引发的事件。除此之外,一个用于提供有关 Excel 当前是否处于编辑模式的信息的属性,并且可以随时从您的代码中检索,也会很有用。

Using the Code

事件提供程序是为此目的定义的

public class ExcelEditorModeEventPublisher : NativeWindow, IDisposable
{ 

及其事件访问器

public event EventHandler EditModeOn
{
public event EventHandler EditModeOff
{

和属性

public bool IsExcelInEditMode
{

可能的解决方案是观察 Excel 的编辑器窗口(class = "Excel6")以查看其样式是否发生了变化。
激活编辑器窗口将通过调用某些 Windows 32 API 调用来完成,例如 ShowWindow, SetWindowPos, 或 Excel 内部的样式更改 SetWindowLong
这些 API 调用会生成可以相对容易检测到的特定窗口消息。为了能够做到这一点,我们需要通过子类化或钩住编辑器窗口来钩住它。我现在选择了子类化,因为 .NET 提供了本机窗口类以进行安全子类化。您可以自由地更改此方法以使用挂钩,通过 SetWindowsHookEx

在开始观察之前,应该在构造函数中找到编辑器窗口

internal ExcelEditorModeEventPublisher
	(Microsoft.Office.Interop.Excel.Application excelApp)
{
     if (excelApp == null)
        throw new ArgumentNullException("excelApp");
     // first get the container window for each workbooks
    IntPtr workbookContainer = Win32.FindWindowEx(new IntPtr( excelApp.Hwnd ), 
                                                  IntPtr.Zero, 
                                                  "XLDESK", String.Empty);
    // check if the search was positive
    if (workbookContainer != IntPtr.Zero)
    {   // continue with finding the editor window
        IntPtr editorWindow = Win32.FindWindowEx(workbookContainer, 
                                                 IntPtr.Zero,
                                                 "EXCEL6", String.Empty);
        // subclass it if the search was successful
        if (editorWindow != IntPtr.Zero)
        {
            InitObservation(editorWindow);
            return;
        }
    }
     throw new Exception("Unfortunately, Excel editor window cannot be detected!");
}

现在我们准备好通过目标窗口句柄开始观察

private void InitObservation(IntPtr editorWindow)
{
    AssignHandle(editorWindow);
    CheckWindowState();
}

我们首先获取发送到编辑器窗口的所有消息,然后才能通过 窗口过程处理它们。

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        // win32 message if style of the targetwindow has changed by any Win32 API call
        // WS_VISIBLE flag or any other
        case (int)Win32.WM.WM_STYLECHANGED:
            CheckWindowState();
            break;
        // win32 message if position of the targetwindow has changed by 
        // SetWindowPos API call
        // SWP_SHOWWINDOW for instance
        case (int)Win32.WM.WM_WINDOWPOSCHANGED:
            CheckWindowState();
            break;
    }
    base.WndProc(ref m);
}

因此,我们能够通知我们的侦听器关于目标编辑器窗口的激活和停用。

private void CheckWindowState()
{
    // check if the window now visible and also enabled
    if (Win32.IsWindowVisible(this.Handle) && Win32.IsWindowEnabled(this.Handle))
    {
        if (!_isEditorWindowActive)
        {   // change the state and raise event
            _isEditorWindowActive = true;
            OnRaiseSafetyEvent(_editModeOn, new object[] {this, EventArgs.Empty} );
        }
    }
    // check if the mode has changed back to non-edit mode
    else
    {
        if (_isEditorWindowActive)
        {   // change the state and raise event
            _isEditorWindowActive = false;
            OnRaiseSafetyEvent( _editModeOff, new object[]{this, EventArgs.Empty} );
        }
    }
}

历史

  • 维克多·哈莫里 2009 年 4 月 13 日 - 2. 版本添加了对 Excel 消息处理程序的保护,如果客户端订阅者由于任何原因失败(抛出异常)
© . All rights reserved.