演练:在运行时更新功能区上的控件

此演练演示如何在功能区加载到 Office 应用程序中后使用功能区对象模型更新功能区上的控件。

**适用于:**本主题中的信息适用于以下应用程序的文档级项目和应用程序级项目:Excel 2007 和 Excel 2010;InfoPath 2010;Outlook 2007 和 Outlook 2010;PowerPoint 2007 和 PowerPoint 2010;Project 2010;Visio 2010;Word 2007 和 Word 2010。有关更多信息,请参见按 Office 应用程序和项目类型提供的功能

该示例从 Northwind 示例数据库提取数据以在 Microsoft Office Outlook 中填充组合框和菜单。 在这些控件中选择的项目会自动填充相应的字段,如电子邮件中的**“收件人”“主题”**。

本演练阐释了以下任务:

  • 创建新的 Outlook 外接程序项目。

  • 设计自定义功能区组。

  • 将自定义组添加到内置选项卡。

  • 在运行时更新功能区上的控件。

提示

以下说明中的某些 Visual Studio 用户界面元素在计算机上出现的名称或位置可能会不同。 您安装的 Visual Studio 版本以及使用的设置决定了这些元素。 有关更多信息,请参见 使用设置

系统必备

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

-

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

创建新的 Outlook 外接程序项目

首先,创建一个 Outlook 外接程序项目。

创建新的 Outlook 外接程序项目

  1. 在 Visual Studio 中,创建名为 Ribbon_Update_At_Runtime 的 Outlook 2007 或 Outlook 2010 外接程序项目。

  2. 在**“新建项目”对话框中选择“创建解决方案的目录”**。

  3. 将项目保存到默认项目目录中。

    有关更多信息,请参见如何:在 Visual Studio 中创建 Office 项目

设计自定义功能区组

当用户撰写新邮件时,将会显示此示例的功能区。 若要为功能区创建一个自定义组,请先将一个功能区项添加到您的项目,然后再在功能区设计器中设计该组。 此自定义组会帮助您通过从数据库提取姓名和订单历史记录来生成发送给客户的后续电子邮件。

设计自定义组

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

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

  3. 将新功能区的名称更改为 CustomerRibbon,然后单击**“添加”**。

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

  4. 单击选中功能区设计器。

  5. 在**“属性”窗口中,单击“RibbonType”属性旁边的下拉箭头,然后单击“Microsoft.Outlook.Mail.Compose”**。

    这使得功能区可以在用户在 Outlook 中撰写新邮件时显示。

  6. 在功能区设计器中,单击选中**“Group1”**。

  7. 在**“属性”窗口中,将“Label”**设置为“Customer Purchases”(客户购买)。

  8. 从**“工具箱”“Office 功能区控件”选项卡中,将“ComboBox”拖到“Customer Purchases”(客户购买)**组上。

  9. 单击选中**“ComboBox1”**。

  10. 在**“属性”窗口中,将“Label”**设置为“Customers”(客户)。

  11. 从**“工具箱”“Office 功能区控件”中,将“Menu”拖到“Customer Purchases”(客户购买)**组上。

  12. 在**“属性”窗口中,将“Label”**设置为“Product Purchased”(购买的产品)。

  13. 将**“Dynamic”**设置为 true。

    这样,在功能区加载到 Office 应用程序中之后,就可在运行时添加和移除菜单上的控件。

将自定义组添加到内置选项卡

内置选项卡是 Outlook 资源管理器或检查器的功能区上已存在的选项卡。 在此过程中,您会将自定义组添加到一个内置选项卡,然后指定自定义组在该选项卡上的位置。

将自定义组添加到内置选项卡

  1. 单击选中**“TabAddins(内置)”**选项卡。

  2. 在**“属性”窗口中展开“ControlId”属性,然后将“OfficeId”**设置为“TabNewMailMessage”。

    这会将**“Customer Purchases”(客户购买)组添加到在新邮件中显示的功能区的“邮件”**选项卡上。

  3. 单击选中**“Customer Purchases”(客户购买)**组。

  4. 在**“属性”窗口中,展开“Position”属性,单击“PositionType”属性旁的下拉箭头,然后单击“BeforeOfficeId”**。

  5. 将**“OfficeId”**属性设置为“GroupClipboard”。

    这会将**“Customer Purchases”(客户购买)组放置在“邮件”选项卡的“剪贴板”**组之前。

创建数据源

使用**“数据源”**窗口向您的项目中添加类型化数据集。

创建数据源

  1. 在**“数据”菜单上,单击“添加新数据源”**。

    这将启动**“数据源配置向导”**。

  2. 选择**“数据库”,然后单击“下一步”**。

  3. 选择**“数据库”,然后单击“下一步”**。

  4. 选择到 Northwind 示例 Microsoft SQL Server Compact 3.5 数据库的数据连接,或者使用**“新建连接”**按钮添加新连接。

  5. 选择或创建连接后,单击**“下一步”**。

  6. 单击**“下一步”**,保存连接字符串。

  7. 在**“选择数据库对象”页面上,展开“表”**。

  8. 选中下面每个表旁边的复选框:

    1. Customers(客户)

    2. Order Details(订单详细信息)

    3. Orders(订单)

    4. Products(产品)

  9. 单击**“完成”**。

在运行时更新自定义组中的控件

使用功能区对象模型可以执行下列任务:

  • 将客户姓名添加到**“Customers”(客户)**组合框。

  • 将表示销售订单和售出产品的菜单和按钮控件添加到**“Products Purchased”(购买的产品)**菜单。

  • 使用**“Customers”(客户)组合框和“Products Purchased”(购买的产品)**菜单中的数据填充新邮件的 ToSubjectBody 字段。

使用功能区对象模型更新自定义组中的控件

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

  2. 在**“添加引用”对话框中,单击“.NET”选项卡,选择“System.Data.Linq”程序集,然后单击“确定”**。

    此程序集包含为使用语言集成查询 (LINQ) 而提供的类。 您将要使用 LINQ 通过 Northwind 数据库中的数据填充自定义组中的控件。

  3. 在**“解决方案资源管理器”**中,单击选中 CustomerRibbon.cs 或 CustomerRibbon.vb。

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

    功能区代码文件会在代码编辑器中打开。

  5. 将以下语句添加到功能区代码文件顶部。 使用这些语句可以方便地访问 LINQ 命名空间以及 Outlook 主互操作程序集 (PIA) 的命名空间。

    Imports System.Data.Linq
    Imports System.Linq
    Imports System.Data.Linq.Mapping
    Imports System.Linq.Expressions
    Imports Outlook = Microsoft.Office.Interop.Outlook
    Imports Ribbon_Update_At_Runtime.NorthwindDataSetTableAdapters
    
    using System.Data.Linq;
    using System.Linq;
    using System.Data.Linq.Mapping;
    using System.Linq.Expressions;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using System.Data;
    using System.IO;
    using Ribbon_Update_At_Runtime.NorthwindDataSetTableAdapters;
    
  6. 在 CustomerRibbon 类内添加以下代码。 此代码声明数据表和表适配器,以用于存储来自 Northwind 数据库的“Customer”(客户)、“Orders”(订单)、“Order Details”(订单详细信息)和“Product”(产品)表的信息。

        'Declare the Northwind data set.
    
        Dim nwDataSet As NorthwindDataSet = New NorthwindDataSet()
    
        'Declare the data tables.
    
        Dim customerTable As NorthwindDataSet.CustomersDataTable
        Dim orderTable As NorthwindDataSet.OrdersDataTable
        Dim orderDetailsTable As NorthwindDataSet.Order_DetailsDataTable
        Dim productsTable As NorthwindDataSet.ProductsDataTable
    
        'Declare the data table adapters for each table.
    
        Dim customersTableAdapter As CustomersTableAdapter = New CustomersTableAdapter()
        Dim ordersTableAdapter As OrdersTableAdapter = New OrdersTableAdapter()
        Dim detailsTableAdapter As Order_DetailsTableAdapter = New Order_DetailsTableAdapter()
        Dim productsTableAdapter As ProductsTableAdapter = New ProductsTableAdapter()
    
    
    //Declare the Northwind dataset.
    NorthwindDataSet nwDataSet = new NorthwindDataSet();
    
    //Declare the data tables.
    
    NorthwindDataSet.CustomersDataTable customerTable;
    NorthwindDataSet.OrdersDataTable orderTable;
    NorthwindDataSet.Order_DetailsDataTable orderDetailsTable;
    NorthwindDataSet.ProductsDataTable productsTable;
    
    //Declare the data table adapters for each table.
    
    CustomersTableAdapter customerTableAdapter = new CustomersTableAdapter();
    OrdersTableAdapter ordersTableAdapter = new OrdersTableAdapter();
    Order_DetailsTableAdapter detailsTableAdapter = new Order_DetailsTableAdapter();
    ProductsTableAdapter productsTableAdapter = new ProductsTableAdapter();
    
  7. 将下列代码块之一添加到 CustomerRibbon 类。 此代码添加三个帮助器方法,以用于在运行时为功能区创建控件。

    如果您的项目以 .NET Framework 4 为目标,请添加以下代码。 

    
    Private Function CreateRibbonDropDownItem() As RibbonDropDownItem
        Return Me.Factory.CreateRibbonDropDownItem()
    End Function
    
    Private Function CreateRibbonMenu() As RibbonMenu
        Return Me.Factory.CreateRibbonMenu()
    End Function
    
    Private Function CreateRibbonButton() As RibbonButton
        Dim button As RibbonButton = Me.Factory.CreateRibbonButton()
        AddHandler (button.Click), AddressOf Button_Click
        Return button
    End Function
    
    private RibbonDropDownItem CreateRibbonDropDownItem()
    {
        return this.Factory.CreateRibbonDropDownItem();
    }
    private RibbonMenu CreateRibbonMenu()
    {
        return this.Factory.CreateRibbonMenu();
    }
    private RibbonButton CreateRibbonButton()
    {
        RibbonButton button = this.Factory.CreateRibbonButton();
        button.Click += new RibbonControlEventHandler(button_Click);
        return button;
    }
    

    如果您的项目以 .NET Framework 3.5 为目标,请添加以下代码。

    
    Private Function CreateRibbonDropDownItem() As RibbonDropDownItem
        Return New RibbonDropDownItem()
    End Function
    
    Private Function CreateRibbonMenu() As RibbonMenu
        Return New RibbonMenu()
    End Function
    
    Private Function CreateRibbonButton() As RibbonButton
        Dim button As RibbonButton = New RibbonButton()
        AddHandler (button.Click), AddressOf Button_Click
        Return button
    End Function
    
    private RibbonDropDownItem CreateRibbonDropDownItem()
    {
        return new RibbonDropDownItem();
    }
    private RibbonMenu CreateRibbonMenu()
    {
        return new RibbonMenu();
    }
    private RibbonButton CreateRibbonButton()
    {
        RibbonButton button = new RibbonButton();
        button.Click += new EventHandler<RibbonControlEventArgs>(button_Click);
        return button;
    }
    
  8. 用下面的代码替换 CustomerRibbon_Load 事件处理程序方法。 这段代码使用 LINQ 查询执行下列任务:

    • 使用 Northwind 数据库中 20 名客户的 ID 和姓名填充**“Customers”(客户)**组合框。

    • 调用 PopulateSalesOrderInfo 帮助器方法。 此方法用属于当前选定客户的销售订单数量更新**“ProductsPurchased”(购买的产品)**菜单。

        Private Sub CustomerRibbon_Load(ByVal sender As System.Object, _
       ByVal e As Microsoft.Office.Tools.Ribbon.RibbonUIEventArgs) _
           Handles MyBase.Load
    
            customerTable = nwDataSet.Customers
            customersTableAdapter.Fill(customerTable)
    
            Dim customerQuery = From customers In customerTable.AsEnumerable.Take(20) _
                    Select CustomerID = customers.Customer_ID, _
                    CustomerName = customers.Contact_Name
    
            ' Execute the query.
            For Each item In customerQuery
                Me.ComboBox1.Items.Add(CreateRibbonDropDownItem())
    
                Me.ComboBox1.Items.Last().Label = item.CustomerID.ToString() _
                    + "|" + item.CustomerName
            Next item
    
            Me.ComboBox1.Text = Me.ComboBox1.Items.First().Label
            PopulateSalesOrderInfo()
        End Sub
    
    
    private void CustomerRibbon_Load(object sender, RibbonUIEventArgs e)
    {
        customerTable = nwDataSet.Customers;
        customerTableAdapter.Fill(customerTable);
    
        var customerQuery = from customers in customerTable.AsEnumerable().Take(20)
                            select new
                            {
                                CustomerID = customers.Field<string>("Customer ID"),
                                CustomerName = customers.Field<string>("Contact Name")
                            };
    
    
        // Execute the query.
        foreach (var item in customerQuery)
        {
            this.comboBox1.Items.Add(CreateRibbonDropDownItem());
            this.comboBox1.Items.Last().Label =
            item.CustomerName + "|" + item.CustomerID.ToString();
        }
        this.comboBox1.Text = this.comboBox1.Items.First().Label;
        PopulateSalesOrderInfo();
    }
    
  9. 向 CustomerRibbon 类中添加下面的代码。 这段代码使用 LINQ 查询执行下列任务:

    • 为每个与选定客户相关的销售订单向**“ProductsPurchased”(购买的产品)**菜单添加一个子菜单。

    • 为销售订单相关的产品向每个子菜单添加按钮。

    • 向每个按钮添加事件处理程序。

    Private Sub PopulateSalesOrderInfo()
    
        Dim tempArray As [String]() = comboBox1.Text.Split(New [Char]() {"|"c})
        Menu1.Items.Clear()
    
        orderTable = nwDataSet.Orders
        orderDetailsTable = nwDataSet.Order_Details
        productsTable = nwDataSet.Products
    
        ordersTableAdapter.Fill(orderTable)
        detailsTableAdapter.Fill(orderDetailsTable)
        productsTableAdapter.Fill(productsTable)
    
        Dim orderQuery = From order In orderTable.AsEnumerable() _
                         Where order.Customer_ID.ToString() = tempArray(0) _
                         Select New With {.SalesOrderID = order.Order_ID}
    
        For Each orderItem In orderQuery
            Me.Menu1.Items.Add(CreateRibbonMenu())
    
            Dim orderMenu As RibbonMenu = CType(Menu1.Items.Last(), RibbonMenu)
            orderMenu.Dynamic = True
            orderMenu.Label = orderItem.SalesOrderID.ToString()
            orderMenu.Tag = orderItem.SalesOrderID
    
            Dim productQuery = From orderDetail In orderDetailsTable.AsEnumerable(), _
                                   product In productsTable.AsEnumerable() _
                               Where orderDetail.Product_ID = _
                                   product.Product_ID _
                               And orderDetail.Order_ID = _
                               orderMenu.Tag _
            Select productName = product.Product_Name
    
            For Each productItem In productQuery
                Dim button As RibbonButton = CreateRibbonButton()
                button.Label = productItem
                orderMenu.Items.Add(button)
            Next productItem
        Next orderItem
    End Sub
    
    private void PopulateSalesOrderInfo()
    {
        String[] tempArray = comboBox1.Text.Split(new Char[] { '|' });
        menu1.Items.Clear();
    
        orderTable = nwDataSet.Orders;
        orderDetailsTable = nwDataSet.Order_Details;
        productsTable = nwDataSet.Products;
    
        ordersTableAdapter.Fill(orderTable);
        detailsTableAdapter.Fill(orderDetailsTable);
        productsTableAdapter.Fill(productsTable);
    
        var orderQuery = from orders in orderTable.AsEnumerable()
                         where orders.Field<string>("Customer ID") == tempArray[1]
                         select new { OrderID = orders.Field<int>("Order ID") };
    
        foreach (var orderItem in orderQuery)
        {
            menu1.Items.Add(CreateRibbonMenu());
    
            RibbonMenu orderMenu = (RibbonMenu)menu1.Items.Last();
            orderMenu.Dynamic = true;
            orderMenu.Label = orderItem.OrderID.ToString();
            orderMenu.Tag = orderItem.OrderID;
    
            var productQuery = from orderDetail in orderDetailsTable.AsEnumerable()
                               join product in productsTable.AsEnumerable() on
                                   orderDetail.Field<int>("Product ID")
                               equals product.Field<int>("Product ID")
                               where orderDetail.Field<int>("Order ID") ==
                                   orderItem.OrderID
                               select new { ProductName = product.Field<string>("Product Name") };
    
            foreach (var productItem in productQuery)
            {
                RibbonButton button = CreateRibbonButton();
                button.Label = productItem.ProductName;
                orderMenu.Items.Add(button);
            }
        }
    }
    
  10. 在**“解决方案资源管理器”**中双击功能区代码文件。

    功能区设计器打开。

  11. 在功能区设计器中,双击**“Customers”(客户)**组合框。

    功能区代码文件会在代码编辑器中打开,并显示 ComboBox1_TextChanged 事件处理程序。

  12. 用下面的代码替换 ComboBox1_TextChanged 事件处理程序。 这段代码执行下列任务:

    • 调用 PopulateSalesOrderInfo 帮助器方法。 此方法用与当前选定客户相关的销售订单更新**“ProductsPurchased”(购买的产品)**菜单。

    • 调用 PopulateMailItem 帮助器方法并传入当前文本(即所选的客户姓名)。 此方法填充新邮件的 ToSubjectBody 字段。

    Private Sub ComboBox1_TextChanged(ByVal sender As System.Object, _
        ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _
            Handles ComboBox1.TextChanged
        PopulateSalesOrderInfo()
        PopulateMailItem(ComboBox1.Text)
    End Sub
    
    private void comboBox1_TextChanged(object sender,
        RibbonControlEventArgs e)
    {
        PopulateSalesOrderInfo();
        PopulateMailItem(comboBox1.Text);
    }
    
  13. 向 CustomerRibbon 类添加以下 Click 事件处理程序。 这段代码将选定产品的名称添加到新邮件的 Body 字段。

    Private Sub Button_Click(ByVal sender As System.Object, _
        ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs)
        Dim application As Outlook.Application = Globals.ThisAddIn.Application
        Dim inspector As Outlook.Inspector = application.ActiveInspector()
        Dim myMailItem As Outlook.MailItem = CType(inspector.CurrentItem,  _
            Outlook.MailItem)
        Dim myButton As RibbonButton = CType(sender, RibbonButton)
        myMailItem.Subject = "Following up on your order"
        myMailItem.Body = myMailItem.Body + ControlChars.Lf + "* " _
            + myButton.Label
    End Sub
    
    void button_Click(object sender, RibbonControlEventArgs e)
    {
        Outlook.Application application = Globals.ThisAddIn.Application;
        Outlook.Inspector inspector = application.ActiveInspector();
        Outlook.MailItem myMailItem = (Outlook.MailItem)inspector.CurrentItem;
        RibbonButton myCheckBox = (RibbonButton)sender;
        myMailItem.Subject = "Following up on your order";
        myMailItem.Body = myMailItem.Body + "\n" + "* " + myCheckBox.Label;
    }
    
  14. 向 CustomerRibbon 类中添加下面的代码。 这段代码执行下列任务:

    • 使用当前选定的客户的电子邮件地址填充新邮件的 To 行。

    • 将文本添加到新邮件的 SubjectBody 字段。

    Private Sub PopulateMailItem(ByVal addressToLine As String)
        Dim application As Outlook.Application = Globals.ThisAddIn.Application
        Dim inspector As Outlook.Inspector = application.ActiveInspector()
        Dim myMailItem As Outlook.MailItem = _
            CType(inspector.CurrentItem, Outlook.MailItem)
    
        myMailItem.To = ""
        Dim tempArray As [String]() = addressToLine.Split(New [Char]() {"|"c})
        myMailItem.To = tempArray(1) + "@example.com"
        myMailItem.Subject = "Following up on your order"
        myMailItem.Body = "Hello " + tempArray(1) + "," _
            + ControlChars.Lf + "We would like to get your feedback" + _
            "on the following products that you recently ordered: "
    End Sub
    
    private void PopulateMailItem(string addressToLine)
    {
        Outlook.Application application = Globals.ThisAddIn.Application;
        Outlook.Inspector inspector = application.ActiveInspector();
        Outlook.MailItem myMailItem = (Outlook.MailItem)inspector.CurrentItem;
    
        myMailItem.To = "";
        String[] tempArray = addressToLine.Split(new Char[] { '|' });
        myMailItem.To = tempArray[0] + "@example.com";
        myMailItem.Subject = "Following up on your order";
        myMailItem.Body = "Hello " + tempArray[0] + "," +
            "\n" + "We would like to get your feedback on the " +
            "following products that you recently ordered: ";
    }
    

测试自定义组中的控件

当您在 Outlook 中打开新邮件窗体时,一个名为**“Customer Purchases”(客户购买)的组会出现在功能区的“邮件”**选项卡上。

若要创建客户后续电子邮件,请选择一个客户,然后选择该客户购买的产品。 将在运行时使用 Northwind 数据库中的数据更新**“Customer Purchases”(客户购买)**组中的控件。

测试自定义组中的控件

  1. 按 F5 运行项目。

    Outlook 即会启动。

  2. 在 Outlook 中的**“文件”菜单上,指向“新建”,然后单击“邮件”**。

    将进行以下操作:

    • 显示一个新邮件检查器窗口。

    • 在功能区的**“邮件”选项卡上,“Customer Purchases”(客户购买)组会出现在“剪贴板”**组之前。

    • 该组中的**“Customers”(客户)**组合框使用 Northwind 数据库中的客户的姓名进行更新。

  3. 在功能区的**“邮件”选项卡上,从“Customer Purchases”(客户购买)组的“Customers”(客户)**组合框中选择一个客户。

    将进行以下操作:

    • **“Products Purchased”(购买的产品)**菜单将更新为显示所选客户的每个销售订单。

    • 每个销售订单子菜单都更新为以该顺序显示购买的产品。

    • 选定客户的电子邮件地址会添加到邮件的**“收件人”**行,邮件的主题和正文会使用文本进行填充。

  4. 单击**“Products Purchased”(购买的产品)**菜单,指向任何销售订单,然后单击该销售订单中的一个产品。

    该产品名称将添加到邮件的正文。

后续步骤

可从以下主题了解有关如何自定义 Office 用户界面的更多信息:

请参见

任务

如何:开始自定义功能区

演练:使用功能区设计器创建自定义选项卡

如何:更改功能区上选项卡的位置

如何:自定义内置选项卡

如何:自定义 Microsoft Office 菜单

如何:将功能区从功能区设计器导出为功能区 XML

如何:显示外接程序用户界面错误

概念

在运行时访问功能区

功能区设计器

功能区对象模型概述

自定义 Outlook 功能区

其他资源

功能区概述

语言集成查询 (LINQ)