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

更新:2007 年 11 月

适用对象

本主题中的信息仅适用于指定的 Visual Studio Tools for Office 项目和 Microsoft Office 版本。

项目类型

  • 文档级项目

  • 应用程序级项目

Microsoft Office 版本

  • Excel 2007

  • Word 2007

  • Outlook 2007

  • PowerPoint 2007

有关更多信息,请参见按应用程序和项目类型提供的功能

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

该示例从 AdventureWorks 示例数据库提取数据以在 Microsoft Office Outlook 中填充组合框和菜单。

本演练阐释以下任务:

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

  • 设计自定义功能区组。

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

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

Bb608628.alert_note(zh-cn,VS.90).gif说明:

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

先决条件

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

  • Visual Studio Tools for Office(Visual Studio 2008 专业版 和 Visual Studio Team System 的可选组件)。

  • Microsoft Office Outlook 2007。

  • 访问针对 SQL Server Express 的 AdventureWorks 示例数据库。有关如何安装 AdventureWorks 数据库的更多信息,请参见演练:安装 AdventureWorks 数据库

默认情况下,Visual Studio Tools for Office 随列出的 Visual Studio 版本一起安装。若要检查它是否已安装,请参见安装 Visual Studio Tools for Office

创建新的 Outlook 外接程序项目

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

创建新的 Outlook 外接程序项目

  1. 在 Visual Studio 中,创建一个名为“MyOutlookAddIn”的 Outlook 2007 外接程序项目。

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

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

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

设计自定义功能区组

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

设计自定义组

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

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

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

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

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

  5. 在“视图”菜单上单击“属性窗口”。

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

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

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

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

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

  10. 单击选中“ComboBox1”。

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

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

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

  14. 将“Dynamic”设置为 true。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 使用“Customers”(客户)组合框和“Products Purchased”(购买的产品)菜单中的数据填充新邮件的 Microsoft.Office.Interop.Outlook.MailItem.ToMicrosoft.Office.Interop.Outlook.MailItem.SubjectMicrosoft.Office.Interop.Outlook.MailItem.Body 字段。

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

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

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

    此程序集包含为使用语言集成查询 (LINQ) 而提供的类。将使用 LINQ 通过 AdventureWorks 数据库中的数据填充自定义组中的控件。有关 LINQ 的更多信息,请参见语言集成查询 (LINQ)

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

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

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

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

    Imports Microsoft.Office.Tools.Ribbon
    Imports System.Data.Linq
    Imports System.Linq
    Imports System.Data.Linq.Mapping
    Imports System.Linq.Expressions
    Imports Outlook = Microsoft.Office.Interop.Outlook
    
    using System.Data.Linq;
    using System.Linq;
    using System.Data.Linq.Mapping;
    using System.Linq.Expressions;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using System.IO;
    
  6. 将以下类添加到功能区代码文件中。将使用这些类存储来自 AdventureWorks 数据库的 Customer、Contact、Sales Order Header、Sales Order Detail 和 Product 表的信息。

    ' Customer Table.
    <Table(Name:="Sales.Customer")> _
    Public Class Customer
        <Column(IsPrimaryKey:=True)> _
            Public CustomerID As Integer
    End Class
    
    ' Contact Table.
    <Table(Name:="Person.Contact")> _
    Public Class Contact
        <Column(IsPrimaryKey:=True)> _
           Public ContactID As Integer
        <Column()> _
        Public LastName As String
    
    End Class
    ' Sales Order Header Table.
    <Table(Name:="Sales.SalesOrderHeader")> _
    Public Class SalesOrderHeader
        <Column(IsPrimaryKey:=True)> _
           Public SalesOrderID As Integer
        <Column()> _
       Public SalesOrderNumber As String
        <Column()> _
        Public CustomerID As Integer
    End Class
    
    ' Sales Order Detail Table.
    <Table(Name:="Sales.SalesOrderDetail")> _
    Public Class SalesOrderDetail
        <Column(IsPrimaryKey:=True)> _
           Public SalesOrderDetailID As Integer
        <Column()> _
       Public SalesOrderID As Integer
        <Column()> _
        Public ProductID As Integer
    End Class
    
    ' Product Table.
    <Table(Name:="Production.Product")> _
    Public Class Product
        <Column(IsPrimaryKey:=True)> _
           Public ProductID As Integer
        <Column()> _
        Public Name As String
    End Class
    ' Data Context Class.
    Partial Public Class AdventureWorks
        Inherits DataContext
        Public Customer As Table(Of Customer)
        Public Contact As Table(Of Contact)
        Public SalesOrderHeader As Table(Of SalesOrderHeader)
        Public SalesOrderDetail As Table(Of SalesOrderDetail)
        Public Product As Table(Of Product)
        Public Sub New(ByVal connection As String)
            MyBase.New(connection)
        End Sub
    End Class
    
    // Customer Table.
    [Table(Name = "Sales.Customer")]
    public class Customer
    {
        [Column(IsPrimaryKey = true)]
        public int CustomerID;
    }
    
    // Contact Table.
    [Table(Name = "Person.Contact")]
    public class Contact
    {
        [Column(IsPrimaryKey = true)]
        public int ContactID;
        [Column]
        public string LastName;
    
    }
    // Sales Order Header Table.
    [Table(Name = "Sales.SalesOrderHeader")]
    public class SalesOrderHeader
    {
        [Column(IsPrimaryKey = true)]
        public int SalesOrderID;
    
        [Column]
        public string SalesOrderNumber;
    
        [Column]
        public int CustomerID;
    
    }
    // Sales Order Detail Table.
    [Table(Name = "Sales.SalesOrderDetail")]
    public class SalesOrderDetail
    {
        [Column(IsPrimaryKey = true)]
        public int SalesOrderDetailID;
    
        [Column]
        public int SalesOrderID;
    
        [Column]
        public int ProductID;
    
    }
    // Product Table.
    [Table(Name = "Production.Product")]
    public class Product
    {
        [Column(IsPrimaryKey = true)]
        public int ProductID;
    
        [Column]
        public string Name;
    
    }
    // Data Context Table.
    public partial class AdventureWorks : DataContext
    {
        public Table<Customer> Customer;
        public Table<Contact> Contact;
        public Table<SalesOrderHeader> SalesOrderHeader;
        public Table<SalesOrderDetail> SalesOrderDetail;
        public Table<Product> Product;
        public AdventureWorks(string connection) : base(connection) { }
    }
    
  7. 用下面的代码替换 CustomerRibbon_Load 事件处理程序方法。这段代码使用 LINQ 查询执行下列任务:

    • 使用 AdventureWorks 数据库中的 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
    
         Dim programFilesDir As String = Environment.GetFolderPath _
         (Environment.SpecialFolder.ProgramFiles)
         Dim db As New AdventureWorks(programFilesDir + _
             "\Microsoft SQL Server\" + "MSSQL.1\MSSQL\Data\AdventureWorks_Data.mdf")
    
         Dim customerQuery = From customers In db.Customer.Take(20), contact In db.Contact _
                             Where customers.CustomerID = contact.ContactID _
                 Select CustomerID = customers.CustomerID, LastName = _
                     contact.LastName
    
         ' Execute the query.
         For Each item In customerQuery
             Me.ComboBox1.Items.Add(New RibbonDropDownItem())
             Me.ComboBox1.Items.Last().Label = item.CustomerID.ToString() _
                 + "|" + item.LastName
         Next item
    
         Me.ComboBox1.Text = Me.ComboBox1.Items.First().Label
         PopulateSalesOrderInfo()
     End Sub
    
    
    private void CustomerRibbon_Load(object sender, RibbonUIEventArgs e)
    {
        string programFilesDir = Environment.GetFolderPath
            (Environment.SpecialFolder.ProgramFiles);
        AdventureWorks db = new AdventureWorks
            (programFilesDir + "\\Microsoft SQL Server\\" + 
        "MSSQL.1\\MSSQL\\Data\\AdventureWorks_Data.mdf");
        var customerQuery = from customers in db.Customer.Take(20)
                            join contacts in db.Contact on 
                                customers.CustomerID 
                            equals contacts.ContactID
                            select new { customers.CustomerID, 
                                contacts.LastName };
    
    
        // Execute the query.
        foreach (var item in customerQuery)
        {
            this.comboBox1.Items.Add(new RibbonDropDownItem());
            this.comboBox1.Items.Last().Label = 
                item.CustomerID.ToString() + "|" + item.LastName;
        }
        this.comboBox1.Text = this.comboBox1.Items.First().Label;
        PopulateSalesOrderInfo();
    }
    
  8. 向 CustomerRibbon 类中添加下面的代码。这段代码使用 LINQ 查询执行下列任务:

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

    • 为销售订单相关的产品向每个子菜单添加复选框。

    • 向每个复选框添加事件处理程序。

     Private Sub PopulateSalesOrderInfo()
    
         Dim tempArray As [String]() = comboBox1.Text.Split(New [Char]() {"|"c})
         Menu1.Items.Clear()
         Dim programFilesDir As String = Environment.GetFolderPath _
             (Environment.SpecialFolder.ProgramFiles)
         Dim db As New AdventureWorks(programFilesDir + _
             "\Microsoft SQL Server\" + "MSSQL.1\MSSQL\Data\AdventureWorks_Data.mdf")
    
    
         Dim orderQuery = From orders In db.SalesOrderHeader _
                          Where orders.CustomerID.ToString() = tempArray(0) _
                          Select SalesOrderID = _
                              orders.SalesOrderID, SalesOrderNumber = _
                                  orders.SalesOrderNumber
    
         For Each orderItem In orderQuery
             Dim Menu2 As New RibbonMenu()
             Menu2.Dynamic = True
             Menu1.Items.Add(Menu2)
             CType(Menu1.Items.Last(), RibbonMenu).Label = _
                 orderItem.SalesOrderNumber.ToString()
             CType(Menu1.Items.Last(), RibbonMenu).Tag = _
                 orderItem.SalesOrderID
    
             Dim productQuery = From orderDetail In db.SalesOrderDetail, _
                                    product In db.Product _
                                Where orderDetail.ProductID = _
                                    product.ProductID _
                                And orderDetail.SalesOrderID = _
                                orderItem.SalesOrderID _
             Select productName = product.Name
    
             For Each productItem In productQuery
                 CType(Menu1.Items.Last(), RibbonMenu).Items.Add _
                     (New RibbonCheckBox())
                 CType(CType(Menu1.Items.Last(),  _
    RibbonMenu).Items.Last(), RibbonCheckBox).Label = productItem
                 AddHandler (CType(CType(Menu1.Items.Last(),  _
    RibbonMenu).Items.Last(), RibbonCheckBox).Click), AddressOf CheckBox_Click
    
             Next productItem
         Next orderItem
     End Sub
    
    
    
    
    private void PopulateSalesOrderInfo()
    {
        String[] tempArray = comboBox1.Text.Split(new Char[] { '|' });
        menu1.Items.Clear();
        string programFilesDir = Environment.GetFolderPath
            (Environment.SpecialFolder.ProgramFiles);
        AdventureWorks db = new AdventureWorks
            (programFilesDir + "\\Microsoft SQL Server\\" +
                "MSSQL.1\\MSSQL\\Data\\AdventureWorks_Data.mdf");
    
        var orderQuery = from orders in db.SalesOrderHeader
                         where orders.CustomerID.ToString() == tempArray[0]
                         select new { orders.SalesOrderID, 
                             orders.SalesOrderNumber };
    
        foreach (var orderItem in orderQuery)
        {
            RibbonMenu menu2 = new RibbonMenu();
            menu2.Dynamic = true;
            menu1.Items.Add(menu2);
            ((RibbonMenu)menu1.Items.Last()).Label = 
                orderItem.SalesOrderNumber.ToString();
            ((RibbonMenu)menu1.Items.Last()).Tag = 
                orderItem.SalesOrderID;
    
            var productQuery = from orderDetail in db.SalesOrderDetail
                               join product in db.Product on 
                                   orderDetail.ProductID 
                               equals product.ProductID
                               where orderDetail.SalesOrderID == 
                                   orderItem.SalesOrderID
                               select new { product.Name };
    
            foreach (var productItem in productQuery)
            {
                ((RibbonMenu)menu1.Items.Last()).Items.Add
                    (new RibbonCheckBox());
                ((RibbonCheckBox)((RibbonMenu)menu1.Items.Last()).
                    Items.Last()).Label = productItem.Name;
                ((RibbonCheckBox)((RibbonMenu)menu1.Items.Last()).
                    Items.Last()).Click 
                    += new EventHandler<RibbonControlEventArgs>(checkBox_Click);
            }
    
        }
    }
    
  9. 在“解决方案资源管理器”中双击功能区代码文件。

    功能区设计器打开。

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

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

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

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

    • 调用 PopulateMailItem 帮助器方法。此方法填充新邮件的 Microsoft.Office.Interop.Outlook.MailItem.ToMicrosoft.Office.Interop.Outlook.MailItem.SubjectMicrosoft.Office.Interop.Outlook.MailItem.Body 字段。

    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);
    }
    
  12. 向 CustomerRibbon 类中添加下面的代码。这段代码将选定产品的名称添加到新邮件的 Microsoft.Office.Interop.Outlook.MailItem.Body 字段。

    Private Sub CheckBox_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 myCheckBox As RibbonCheckBox = CType(sender, RibbonCheckBox)
        myMailItem.Subject = "Following up on your order"
        myMailItem.Body = myMailItem.Body + ControlChars.Lf + "* " _
            + myCheckBox.Label
    End Sub
    
    private void checkBox_Click(object sender, RibbonControlEventArgs e)
    {
        Outlook.Application application = Globals.ThisAddIn.Application;
        Outlook.Inspector inspector = application.ActiveInspector();
        Outlook.MailItem myMailItem = (Outlook.MailItem)inspector.CurrentItem;
        RibbonCheckBox myCheckBox = (RibbonCheckBox)sender;
        myMailItem.Subject = "Following up on your order";
        myMailItem.Body = myMailItem.Body + "\n" + "* " + myCheckBox.Label;
    }
    
  13. 向 CustomerRibbon 类中添加下面的代码。这段代码执行下列任务:

    • 使用当前选定的客户的电子邮件地址填充新邮件的 Microsoft.Office.Interop.Outlook.MailItem.To 行。

    • 将文本添加到新邮件的 Microsoft.Office.Interop.Outlook.MailItem.SubjectMicrosoft.Office.Interop.Outlook.MailItem.Body 字段。

    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) + ","
        myMailItem.Body = myMailItem.Body + 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[1] + "@example.com";
        myMailItem.Subject = "Following up on your order";
        myMailItem.Body = "Hello " + tempArray[1] + ",";
        myMailItem.Body = myMailItem.Body + "\n" + 
            "We would like to get your " + 
                "feedback on the following products that you recently ordered: ";
    }
    

测试自定义组中的控件

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

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

测试自定义组中的控件

  1. 按 F5 运行项目。

    Outlook 即会启动。

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

    将进行以下操作:

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

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

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

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

    将进行以下操作:

    • “Products Purchased”(购买的产品)菜单将根据表示该客户每个销售订单的菜单进行更新。

    • 每个销售订单菜单根据表示客户购买的产品的复选框进行更新。

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

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

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

后续步骤

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

请参见

任务

如何:开始自定义功能区

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

如何:更改功能区上的选项卡的顺序

如何:自定义内置选项卡

如何:自定义 Microsoft Office 菜单

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

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

概念

在运行时访问功能区

功能区概述

功能区设计器

功能区对象模型概述

自定义 Outlook 功能区