Excel 中单文档界面的编程

了解 Excel 中单文档界面的编程注意事项。

比较 Excel 2010 和 Excel 2013 中的单文档界面和多文档界面

Excel 2013 中的一个新功能是单文档界面 (SDI)。 SDI 是将图形用户界面 (UI) 应用程序组织到操作系统窗口管理器单独处理的各个窗口的方法。 在 Excel 2013 中,每个 Excel 窗口只能包含一个工作簿,并且每一个都有它自己的功能区 UI(请参见图 1)。 默认情况下,当您打开一个新工作簿时,它将显示在另一个 Excel 窗口中,即使是同一个 Excel 实例也是如此。

图 1. Excel 2013 中的单文档界面

Excel 2013 中的单文档界面

这与多文档界面 (MDI)(其中,单个父窗口用于包含多个嵌套子窗口)相反,仅父窗口具有菜单或工具栏。 在 Excel 2010 中,单个 Excel 实例中的每个工作簿利用一个通用的功能区 UI(请参见图 2)。

图 2. Excel 2010 中的多文档界面

Excel 2010 中的多文档界面

Excel 2010 使用 MDI,这意味着有一个应用程序级窗口,用于保存在特定 Excel 实例中打开的所有工作簿。 工作簿的窗口可以在 Excel 应用窗口内部进行排列,所有窗口共享相同的功能区 UI。 Excel 中的 SDI 意味着每个工作簿都有自己的顶级应用窗口,并具有自己的相应功能区 UI。

注意

Excel 中没有 MDI 兼容性选项。

在双显示器系统中,Excel 中的 SDI 通过将每个工作簿拖动到其他监视器并排比较两个工作簿。 每个工作簿都独立于另一个工作簿运行。

若要查看操作中的 SDI 和 MDI,如果 Excel 2010 和 Excel 2013 均可用,请执行下列步骤。

比较 MDI 和 SDI 接口的进程数

  1. 在 Windows“开始”菜单中,启动 Excel 2010。
  2. 启动 Excel 的第二个匹配项。 验证是否显示两个 Excel 窗口。
  3. 在 Windows 任务栏中,选择启动任务管理器
  4. 选择“进程”选项卡,然后向下滚动,直到您看到两个 Excel.exe 条目。 这会告诉你,默认情况下,每次调用一个新实例时,Excel 都会打开一个新实例, (两个 excel 实例) 。
  5. 关闭 Excel 的两个实例。
  6. 在 Windows“开始”菜单中,选择 Excel 2013。
  7. 启动 Excel 的第二个匹配项。 验证是否显示两个 Excel 窗口。
  8. 重新启动任务管理器。
  9. 在“ 进程 ”选项卡上向下滚动,直到看到 Excel.exe。 请注意,虽然你打开了两个 Excel 实例,但这两个工作簿包含在同一个 Excel 实例中。

若要查看 SDI 和 MDI 在 Excel 实例内部的工作方式,请执行以下步骤。

比较 MDI 和 SDI 接口的 Excel 实例数

  1. 在 Windows“开始”菜单中,选择 Excel 2010。
  2. 选择 Excel 窗口以使其处于活动状态,并验证 Book1 是否为当前工作簿。
  3. 按 CTRL + N 打开另一个工作簿。 请验证 Book2 现在是否为当前工作簿。
  4. 最小化 Book2,然后查看 Book1。 这两个工作簿都包含在同一个 Excel 实例中。
  5. 关闭 Excel。
  6. 在 Windows“开始”菜单中,选择 Excel 2013。
  7. 选择 Excel 窗口以使其处于活动状态,并验证 Book1 是否为当前工作簿。
  8. 按 CTRL + N 打开另一个工作簿。 验证 Book2 是否在单独的窗口中打开 (但仍在同一个 Excel 实例中)。
  9. 关闭 Excel。

注意

可以使用以下命令行开关打开多个 Excel 实例: excel.exe /x。 此开关会在一个新的进程中启动 Excel。

在本文中,我们将讨论 SDI 在 Excel UI 中的实施以及它如何影响 Excel 中的可编程性。

用户界面中的更改内容

如果在打开 Excel 工作簿后仔细查看,则不再看到功能区右上角 ( 最小化最大化还原) 的窗口状态按钮。 图 3 显示了 Excel 和 Excel 2007 中可用的窗口状态按钮。 由于顶级窗口现在直接与单个工作簿或工作簿视图相关联,因此 Excel 中不再需要窗口管理 UI。

图 3. Excel 2010 中的 Windows 状态 UI

Excel 2010 中的 Windows 状态 UI

此外,从 Excel 开始,单个 Excel 实例窗口中不再有多个工作簿窗口,如图 4 所示。

图 4. 单个 Excel 实例窗口中的多个工作簿

单个 Excel 实例中的多个工作簿

重新计算和公式

Excel 中的重新计算仍将是“全局的”,这意味着它们发生在同一 Excel 实例中的工作簿中。 在 Excel 的同一实例中打开的工作簿之间引用的公式将一起参与计算,并将共享同一工作簿计算模式, (自动、自动(数据表除外)和手动) 。

在 MDI 中,只有一个公式栏用于处理该 Excel 实例中所有打开的工作簿。 在 SDI 中,每个工作簿有一个公式栏。 对于 SDI,编辑公式中的跨工作簿引用时,源和目标工作簿公式栏都将显示当前正在编辑的公式,如图 5 中显示。

图 5. 更新跨工作簿的公式

更新跨工作簿的公式

自定义任务窗格

附加到 MDI 中顶级窗口的自定义任务窗格现在附加到 SDI 中特定工作簿的窗口。 切换到其他工作簿将激活该工作簿窗口,该窗口不一定附加自定义任务窗格,除非开发人员的代码已更新为专门显示该工作簿的自定义任务窗格。

总之,作为开发人员,你需要:

  • 确保对于要显示自定义任务窗格的任何工作簿,请编写代码来显式执行此操作。
  • 如果您希望所有自定义任务窗格反映相同的状态,则请确保您显式处理了对所有实例中的自定义任务窗格状态的更新。 例如,用户将一个复选框切换为“开”,您希望在 Excel 所有实例中的所有自定义任务窗格中反映此状态。

自定义功能区

自定义功能区选项卡和控件,这些选项卡和控件假定以前版本的 Excel 中每个应用程序实例都有一个功能区 UI,现在将传播到 Excel 中的每个工作簿功能区。 而在 MDI 中,自定义功能区开发人员无需在 Excel 功能区 UI 的不同实例上考虑其控件的多个实例,而使用 SDI 时,他们需要考虑到这种情况。

如果要使所有功能区 UI 控件在打开的工作簿中保持相同的状态,则需要:

  • 确保代码能够在工作簿窗口中循环并更新控件的状态。

OR

  • 缓存控件的状态,以便当用户切换到另一个工作簿时,可以捕获该事件并将控件更新为窗口开关的一部分。

此外,请考虑开发代码以通过使用 Application.Commandbar 来访问功能区来添加自定义 UI 控件的情况。 当您稍后尝试访问该控件时,您的代码将需要考虑以下事实:活动工作簿可能不是您添加该控件的那个工作簿。

VBA 代码的注意事项

迁移到 SDI 后,所有 Excel 应用程序级 窗口方法、事件和属性均不受影响,并且其工作方式与以前版本的 Excel (例如 Application.ActiveWindowApplication.Windows等) 。

在 Excel 中,所有工作簿级别的窗口方法、事件和属性现在都在顶级窗口上运行 (,例如,Workbook.WindowActivate切换到特定工作簿时仍会触发该事件,Workbook.Resize在调整工作簿大小时仍会触发该事件,并且 ThisWorkbook.Windows(1).HeightThisWorkbook.Windows(1).WidthThisWorkbook.Windows(1).MinimizeThisWorkbook.Windows(1).LeftThisWorkbook.Windows(1).RightThisWorkbook.Windows(1).Maximize、 等将在活动工作簿) 的顶级窗口中运行。

下表中列出了特殊情况。

表 1. 使用 SDI 的对象模型行为

功能 说明 SDI 含义
Application.Visible 返回或设置一个 Boolean 值,它确定对象是否可见。 读/写。 如果所有窗口都处于隐藏状态:
  • Application.Visible 变为 False

  • Application.Visible 设置为 True 将显示所有隐藏的窗口

  • 通过 shell 打开文档将仅显示该窗口,并且 Application.Visible 现在为 True

此外:
  • Application.Visible = False 隐藏所有内容,而 Application.Visible = True 显示所有内容,忽略任何文档级别的设置

  • 如果所有窗口通过窗口级别的设置隐藏,则应用程序级别的设置也将切换

  • 至少显示一个窗口表示应用程序级别的设置为 True

Application.ShowWindowsInTaskbar 如果每个打开的工作簿都有单独的 Windows 任务栏按钮,则为 True。 默认值为 True。 读/写 Boolean 此设置在 Excel 中已弃用。
Application.Caption 返回或设置一个 String 值,它代表出现在 Microsoft Excel 主窗口标题栏中显示的名称。 更新该 Excel 实例的所有窗口。
Application.Hwnd 返回一个 Long 类型的值,该值表示 Microsoft Excel 窗口的最高级别的窗口句柄。 只读。 将返回活动窗口的句柄。
Application.FormulaBarHeight 支持用户指定行中公式栏的高度。 读/写 Long 运行于当前活动工作簿窗口上;并非所有窗口都适用于此 Excel 实例。
Application.DisplayFormulaBar 如果显示编辑栏,则该属性值为 True。 读/写 Boolean 运行于该 Excel 实例的所有窗口上。
Workbook.Windows 返回一个 Windows 集合,该集合代表指定工作簿中的所有窗口。 只读 Windows Object 行为没有变化。 返回该工作簿的窗口集合,例如任务窗格、其他视图。
Workbook.WindowResize 任何工作簿窗口调整大小时将发生此事件。 行为没有变化。 当重新调整工作簿窗口(顶级)大小时触发。
Window.Caption 返回或设置一个 Variant 值,它代表文档窗口标题栏中显示的名称。 行为没有变化。
Workbook.Protect(Password, Structure, Windows) 保护工作簿使其不被修改。 无论 Windows 参数(TrueFalse)的值如何,都将不会启用窗口结构保护。 如果已指定 True,则不会显示运行时错误,但这部分过程调用将返回 NO-OP

注意

自定义代码中无需任何更改,以便 XLM 命令继续在 SDI Excel 中如期运行。

弃用保护工作簿窗口

在 SDI 中,每个工作簿都有自己的顶级窗口,您可以还原、最小化和关闭。 为了尽量减少在移动、调整大小或关闭此顶级窗口时可能遇到的任何困惑,Excel 中的“保护工作簿”功能中的 Windows 选项不再可用 (请参阅图 6) 。 表 2 进一步描述了此操作。

图 6. “保护工作簿”对话框的 Windows 选项已禁用

“保护工作簿”对话框的 Windows 选项

操作 行为
打开在先前版本的 Excel 中创建的工作簿,启用“窗口保护” Excel 将识别窗口位置和大小属性,但不会阻止用户排列或关闭这些窗口。
查看“保护结构和 Windows”对话框 Excel 将显示对话框,但 Windows 选项处于 禁用状态。

SDI 问题的解决方案

以下部分提供了您使用 SDI 时可能会遇到的问题的解决方法。

  • 通过模式用户窗体以编程方式打开工作簿时,无法单击红色的“X” 关闭 按钮关闭工作簿。 要解决此问题,建议将以下代码添加到用户表单 Layout 事件过程中,然后以无模式形式打开该用户表单。

      Private Sub UserForm_Layout()
          Static fSetModal As Boolean
          If fSetModal = False Then
              fSetModal = True
              Me.Hide
              Me.Show 1
          End If
      End Sub
    

    另一个选项是打开工作簿窗口,激活任何其他窗口,然后重新激活工作簿窗口。 您现在应该能够使用“关闭”按钮关闭工作簿。

  • 假定您的 VBA 代码打开多个工作簿,并使用 DataEntryMode 属性控制数据条目和工作簿关闭。 在 Excel SDI 模型中,由于每个工作簿都包含在其自己的进程中,因此在一个工作簿中使用的 DataEntryMode 属性无法识别其他工作簿的存在,因此对它们的交互影响不大或没有影响。 要解决此问题,有几个选项。 可以通过分别使用 Window.Visible = False 或 隐藏额外的工作簿或 Sheet.Visible = False工作表。 使用 Workbook.BeforeClose(Cancel) = True检测和取消任何关闭事件。

  • 通过命令条形码和 XLA 文件添加到 Excel 工作簿的工具栏在关闭并重新打开工作簿后才会显示。 从 Excel 2007 开始,已弃用使用命令栏自定义 UI。 正如文章Customizing the 2007 Office Fluent Ribbon for Developers 中所详细介绍,最佳解决方案是通过使用 XML 文件自定义功能区 UI。

    另一个选项是使用应用程序级事件来检测打开的新工作簿,然后使用 Application.Windows 而不是工作簿来添加功能区控件。 下面是可用于完成此操作的示例代码。

      Private Sub Workbook_Open()
          ToolBarsAdd
      End Sub
    
      Sub ToolBarsAdd()
          Dim oBar As CommandBar
    
          ToolBarsDelete
          Set oBar = Application.CommandBars.Add(Name:="MyToolBar")
          '
          With oBar
              With .Controls.Add(Type:=msoControlButton)
                  .OnAction = "SayHello"
                  .FaceId = 800
              End With
          End With
          oBar.Visible = True
      End Sub
    
      Sub SayHello()
          MsgBox "Hello from '" & ActiveWorkbook.Name & "'"
      End Sub
    

    然后,下面的代码将用于在关闭工作簿之前删除工具栏。

      Private Sub Workbook_BeforeClose(Cancel As Boolean)
          ToolBarsDelete
      End Sub
    
      Sub ToolBarsDelete()
      Dim wnd As Window
      On Error Resume Next
          For Each wnd In Application.Windows
              wnd.Activate
              Application.CommandBars("MyToolBar ").Delete
          Next wnd
      End Sub
    
  • 在 Excel 2010 中,默认情况下,无模式用户窗体在所有 Excel 窗口顶部显示为顶级窗口。 在 Excel 2013 中,仅当显示用户表单时,无模式用户表单才会显示在活动的工作簿窗口之上。 Excel 最有价值的专业 (MVP) Jan Karel Pieterse 在他的网页上 https://www.jkp-ads.com/articles/keepuserformontop.asp提供了问题的说明和解决方案。

摘要

Excel 2013 中的新单文档界面使其可以轻松使用多个工作簿。 为方便起见,您可以将工作簿拖动到不同的显示器。 您只需记住,每个工作簿只有一个顶级窗口和一个功能区 UI 菜单。 这可能要求您在工作簿之间移动时更新任何现有代码以缓存控件和设置的状态。

另请参阅

支持和反馈

有关于 Office VBA 或本文档的疑问或反馈? 请参阅 Office VBA 支持和反馈,获取有关如何接收支持和提供反馈的指南。