演练:在运行时在应用程序级项目中向工作表中添加控件

更新:2011 年 4 月

可以使用 Excel 外接程序向任何打开的工作表中添加控件。 本演练演示如何使用功能区让用户可以向工作表中添加 ButtonNamedRangeListObject。 有关信息,请参见在运行时向 Office 文档添加控件

**适用于:**本主题中的信息适用于 Excel 2007 和 Excel 2010 的应用程序级项目。有关更多信息,请参见按 Office 应用程序和项目类型提供的功能

本演练阐释了以下任务:

  • 提供用于向工作表中添加控件的用户界面 (UI)。

  • 向工作表中添加控件。

  • 从工作表中移除控件。

提示

对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置

系统必备

您需要以下组件来完成本演练:

-

Visual Studio 2010 的一个版本,其中包含 Microsoft Office 开发工具。有关更多信息,请参见[将计算机配置为开发 Office 解决方案](bb398242\(v=vs.100\).md)。
  • Microsoft Office Excel 2007 或 Excel 2010。

创建新的 Excel 外接程序项目

从创建 Excel 外接程序项目开始。

创建新的 Excel 外接程序项目

  1. 在 Visual Studio 中,创建一个名为 ExcelDynamicControls 的 Excel 外接程序项目。 有关更多信息,请参见如何:在 Visual Studio 中创建 Office 项目

  2. 如果项目面向 .NET Framework 4,请添加对 Microsoft.Office.Tools.Excel.v4.0.Utilities.dll 程序集的引用。 在本演练后面的部分中,需要此引用才能以编程方式向工作表中添加 Windows 窗体控件。

提供用于向工作表中添加控件的用户界面

向 Excel 功能区中添加自定义选项卡。 用户可以选中该选项卡上的复选框,以向工作表中添加控件。

提供用于向工作表中添加控件的用户界面

  1. 在**“项目”菜单上,单击“添加新项”**。

  2. 在**“添加新项”对话框中,选择“功能区(可视化设计器)”,再单击“添加”**。

    名为 Ribbon1.cs 或 Ribbon1.vb 的文件将在功能区设计器中打开,并显示一个默认选项卡和组。

  3. 从**“工具箱”“Office 功能区控件”选项卡中将 CheckBox 控件拖到“group1”**上。

  4. 单击**“CheckBox1”**将其选定。

  5. 在**“属性”**窗口中,更改下列属性。

    Property

    名称

    Button

    标签

    Button

  6. 向**“group1”**再添加一个复选框,然后更改下列属性。

    Property

    名称

    NamedRange

    标签

    NamedRange

  7. 向**“group1”**中添加第三个复选框,然后更改以下属性。

    Property

    名称

    ListObject

    标签

    ListObject

将控件添加到工作表

托管控件只能添加到充当容器的宿主项。 由于外接程序项目可以处理任何打开的工作簿,因此外接程序在添加控件之前会将工作表转换为宿主项,或获取现有的宿主项。 将代码添加到每个控件的 Click 事件处理程序,以生成基于打开的工作表的 Worksheet 宿主项。 然后,在工作表中的当前选定内容处添加 ButtonNamedRangeListObject

向工作表中添加控件

  1. 在功能区设计器中双击**“Button”**。

    **“Button”**复选框的 Click 事件处理程序在代码编辑器中打开。

  2. 用下面的代码替换 Button_Click 事件处理程序。

    此代码使用 GetVstoObject 方法获取表示工作簿中的第一个工作表的宿主项,然后将 Button 控件添加到当前选定的单元格。

    Private Sub Button_Click(ByVal sender As System.Object, _
        ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _
            Handles Button.Click
    
        Dim NativeWorksheet As Microsoft.Office.Interop.Excel.Worksheet =
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1)
    
        ' Use the following line of code in projects that target the .NET Framework 4.
        Dim worksheet As Microsoft.Office.Tools.Excel.Worksheet =
            Globals.Factory.GetVstoObject(NativeWorksheet)
    
        ' In projects that target the .NET Framework 3.5, use the following line of code.
        ' Dim worksheet = CType(Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1),  _
        '    Excel.Worksheet).GetVstoObject()
    
        Dim buttonName As String = "MyButton"
    
        If CType(sender, RibbonCheckBox).Checked Then
            Dim selection As Excel.Range = Globals.ThisAddIn.Application.Selection
            If Not (selection Is Nothing) Then
                Dim button As New Microsoft.Office.Tools.Excel.Controls.Button()
                worksheet.Controls.AddControl(button, selection, buttonName)
            End If
        Else
            worksheet.Controls.Remove(buttonName)
        End If
    End Sub
    
    private void Button_Click(object sender, RibbonControlEventArgs e)
    {
        // Use the following line of code in projects that target the .NET Framework 4.
        Worksheet worksheet = Globals.Factory.GetVstoObject(
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]);
    
        // In projects that target the .NET Framework 3.5, use the following line of code.
        // Worksheet worksheet = 
        //     ((Excel.Worksheet)Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]).GetVstoObject();
    
        string buttonName = "MyButton";
    
        if (((RibbonCheckBox)sender).Checked)
        {
            Excel.Range selection = Globals.ThisAddIn.Application.Selection as Excel.Range;
            if (selection != null)
            {
                Microsoft.Office.Tools.Excel.Controls.Button button =
                    new Microsoft.Office.Tools.Excel.Controls.Button();
                worksheet.Controls.AddControl(button, selection, buttonName);
            }
        }
        else
        {
            worksheet.Controls.Remove(buttonName);
        }
    }
    
  3. 在**“解决方案资源管理器”**中,选择 Ribbon1.cs 或 Ribbon1.vb。

  4. 在**“视图”菜单上,单击“设计器”**。

  5. 在功能区设计器中双击**“NamedRange”**。

  6. 用下面的代码替换 NamedRange_Click 事件处理程序。

    此代码使用 GetVstoObject 方法获取表示工作簿中的第一个工作表的宿主项,然后为当前选定的一个或多个单元格定义 NamedRange 控件。

    Private Sub NamedRange_Click(ByVal sender As System.Object, _
        ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _
            Handles NamedRange.Click
    
        Dim NativeWorksheet As Microsoft.Office.Interop.Excel.Worksheet =
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1)
    
        ' Use the following line of code in projects that target the .NET Framework 4.
        Dim worksheet As Microsoft.Office.Tools.Excel.Worksheet =
            Globals.Factory.GetVstoObject(NativeWorksheet)
    
        ' In projects that target the .NET Framework 3.5, use the following line of code.
        ' Dim worksheet = CType(Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1),  _
        '    Excel.Worksheet).GetVstoObject()
    
        Dim rangeName As String = "MyNamedRange"
    
        If CType(sender, RibbonCheckBox).Checked Then
            Dim selection As Excel.Range = Globals.ThisAddIn.Application.Selection
            If Not (selection Is Nothing) Then
                Dim namedRange As NamedRange = _
                    worksheet.Controls.AddNamedRange(selection, rangeName)
            End If
        Else
            worksheet.Controls.Remove(rangeName)
        End If
    End Sub
    
    private void NamedRange_Click(object sender, RibbonControlEventArgs e)
    {
        // Use the following line of code in projects that target the .NET Framework 4.
        Worksheet worksheet = Globals.Factory.GetVstoObject(
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]);
    
        // In projects that target the .NET Framework 3.5, use the following line of code.
        // Worksheet worksheet = 
        //     ((Excel.Worksheet)Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]).GetVstoObject();
    
        string Name = "MyNamedRange";
    
        if (((RibbonCheckBox)sender).Checked)
        {
            Excel.Range selection = Globals.ThisAddIn.Application.Selection as Excel.Range;
            if (selection != null)
            {
                worksheet.Controls.AddNamedRange(selection, Name);
            }
        }
        else
        {
            worksheet.Controls.Remove(Name);
        }
    }
    
  7. 在功能区设计器中双击**“ListObject”**。

  8. 用下面的代码替换 ListObject_Click 事件处理程序。

    此代码使用 GetVstoObject 方法获取表示工作簿中的第一个工作表的宿主项,然后为当前选定的一个或多个单元格定义 ListObject

    Private Sub ListObject_Click(ByVal sender As System.Object, _
        ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _
            Handles ListObject.Click
    
        Dim NativeWorksheet As Microsoft.Office.Interop.Excel.Worksheet =
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1)
    
        ' Use the following line of code in projects that target the .NET Framework 4.
        Dim worksheet As Microsoft.Office.Tools.Excel.Worksheet =
            Globals.Factory.GetVstoObject(NativeWorksheet)
    
        ' In projects that target the .NET Framework 3.5, use the following line of code.
        ' Dim worksheet = CType(Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets(1),  _
        '    Excel.Worksheet).GetVstoObject()
    
        Dim listObjectName As String = "MyListObject"
    
        If CType(sender, RibbonCheckBox).Checked Then
            Dim selection As Excel.Range = _
                Globals.ThisAddIn.Application.Selection
            If Not (selection Is Nothing) Then
                worksheet.Controls.AddListObject(selection, listObjectName)
            End If
        Else
            worksheet.Controls.Remove(listObjectName)
        End If
    End Sub
    
    private void ListObject_Click(object sender, RibbonControlEventArgs e)
    {
        // Use the following line of code in projects that target the .NET Framework 4.
        Worksheet worksheet = Globals.Factory.GetVstoObject(
            Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]);
    
        // In projects that target the .NET Framework 3.5, use the following line of code.
        // Worksheet worksheet = 
        //     ((Excel.Worksheet)Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1]).GetVstoObject();
    
        string listObjectName = "MyListObject";
    
        if (((RibbonCheckBox)sender).Checked)
        {
            Excel.Range selection = Globals.ThisAddIn.Application.Selection as Excel.Range;
            if (selection != null)
            {
                worksheet.Controls.AddListObject(selection, listObjectName);
            }
        }
        else
        {
            worksheet.Controls.Remove(listObjectName);
        }
    }
    
  9. 将以下语句添加到功能区代码文件顶部。

    Imports Excel = Microsoft.Office.Interop.Excel
    Imports Microsoft.Office.Tools.Excel
    Imports Microsoft.Office.Tools.Excel.Extensions
    
    using Excel = Microsoft.Office.Interop.Excel;
    using Microsoft.Office.Tools.Excel;
    using Microsoft.Office.Tools.Excel.Extensions;
    

从工作表中移除控件

保存并关闭工作表后,控件不会保留。 您应该在保存工作表之前,以编程方式移除所有生成的 Windows 窗体控件,否则在再次打开工作簿时,将只显示控件的轮廓。 将代码添加到 WorkbookBeforeSave 事件,该事件将 Windows 窗体控件从生成的宿主项的控件集合中移除。 有关更多信息,请参见在 Office 文档中保存动态控件

从工作表中移除控件

  1. 在**“解决方案资源管理器”**中,选择 ThisAddIn.cs 或 ThisAddIn.vb。

  2. 在**“视图”菜单上单击“代码”**。

  3. 将下面的方法添加到 ThisAddIn 类中。 此代码获取工作簿中的第一个工作表,然后使用 HasVstoObject 方法来检查该工作表是否具有生成的工作表对象。 如果生成的工作表对象具有控件,此代码将获取该工作表对象,并循环访问控件集合,同时移除控件。

    Sub Application_WorkbookBeforeSave _
        (ByVal workbook As Microsoft.Office.Interop.Excel.Workbook, _
         ByVal SaveAsUI As Boolean, ByRef Cancel As Boolean) _
         Handles Application.WorkbookBeforeSave
    
        Dim worksheet As Excel.Worksheet = workbook.Worksheets(1)
    
        ' Use the following code in projects that target the .NET Framework 4.
        If Globals.Factory.HasVstoObject(worksheet) And
            Globals.Factory.GetVstoObject(worksheet).Controls.Count > 0 Then
            Dim vstoWorksheet As Worksheet = Globals.Factory.GetVstoObject(worksheet)
    
            ' In projects that target the .NET Framework 3.5, use the following code.
            ' If worksheet.HasVstoObject() And worksheet.GetVstoObject().Controls.Count > 0 Then
            ' Dim vstoWorksheet As Worksheet = worksheet.GetVstoObject()
    
            While vstoWorksheet.Controls.Count > 0
                Dim vstoControl As Object = vstoWorksheet.Controls(0)
                vstoWorksheet.Controls.Remove(vstoControl)
            End While
        End If
    End Sub
    
    void Application_WorkbookBeforeSave(Microsoft.Office.Interop.Excel.Workbook workbook, 
        bool SaveAsUI, ref bool Cancel)
    {
        Excel.Worksheet worksheet =
            workbook.Worksheets[1] as Excel.Worksheet;
    
        // Use the following lines of code in projects that target the .NET Framework 4.
        if (Globals.Factory.HasVstoObject(worksheet) && 
            Globals.Factory.GetVstoObject(worksheet).Controls.Count > 0)
        {
            Worksheet vstoWorksheet = Globals.Factory.GetVstoObject(worksheet);
    
        // In projects that target the .NET Framework 3.5, use the following line of code.
        // if (worksheet.HasVstoObject() && worksheet.GetVstoObject().Controls.Count > 0)
        // {
        //     Worksheet vstoWorksheet = worksheet.GetVstoObject();               
    
            while (vstoWorksheet.Controls.Count > 0)
            {
                object vstoControl = vstoWorksheet.Controls[0];
                vstoWorksheet.Controls.Remove(vstoControl);
            }
    
        }
    }
    
  4. 在 C# 中,必须为 WorkbookBeforeSave 事件创建事件处理程序。 可以将此代码放在 ThisAddIn_Startup 方法中。 有关创建事件处理程序的更多信息,请参见如何:在 Office 项目中创建事件处理程序。 用下面的代码替换 ThisAddIn_Startup 方法。

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        this.Application.WorkbookBeforeSave += 
            new Microsoft.Office.Interop.Excel.AppEvents_WorkbookBeforeSaveEventHandler
                (Application_WorkbookBeforeSave);
    }
    

测试解决方案

通过在功能区的自定义选项卡中选择控件来向工作表中添加控件。 保存工作表时,将移除这些控件。

测试解决方案。

  1. 按 F5 运行项目。

  2. 选择 Sheet1 中的任何单元格。

  3. 单击**“外接程序”**选项卡。

  4. 在**“group1”组中,单击“Button”**。

    一个按钮显示在所选单元格中。

  5. 选择 Sheet1 中的另一个单元格。

  6. 在**“group1”组中,单击“NamedRange”**。

    将为所选单元格定义一个命名区域。

  7. 选择 Sheet1 中的一系列单元格。

  8. 在**“group1”组中,单击“ListObject”**。

    将为所选单元格添加一个列表对象。

  9. 保存工作表。

    您添加到 Sheet1 的控件不再显示。

后续步骤

您可以从本主题中了解关于 Excel 外接程序项目中的控件的更多信息:

  • 若要了解如何将控件保存到工作表的更多信息,请参见 Office 开发示例和演练中的“Excel 外接程序动态控件示例”。

请参见

概念

在 Excel 工作表中使用 Windows 窗体控件

NamedRange 控件

ListObject 控件

其他资源

Excel 解决方案

Office 文档上的控件

修订记录

日期

修订记录

原因

2011 年 4 月

增加了描述项目面向 .NET Framework 4.0 时必须添加的程序集引用的文字。

内容 Bug 修复