详解Office Add-in 清单文件

优质
小牛编辑
129浏览
2023-12-01

作者:陈希章 发表于2017年12月8日

前言

我们都知道,一个Office Add-in,最主要是由两个部分组成的:清单文件(manifest)和真正要用来执行的网站。

清单文件其实是一个标准的XML文件,它有固定的Schema。目前来说,最新版本的清单文件必须指定“http://schemas.microsoft.com/office/appforoffice/1.1”作为Schema,否则某些功能可能不能正常工作。当然,指定Schema这件事情你可能不太会需要手工去做,毕竟不管你是用Visual Studio的项目模板,还是用其他开发工具(例如Visual Studio Code),清单文件都是自动生成的,而且默认就已经指定了1.1这个版本。下面两篇文章介绍了如何在不同工具开始office Add-in的开发。

  • 在Visual Studio 中开发Office Add-in
  • 在Visual Studio Code中开发Office Add-in

一个典型的清单文件看起来是下面这样的

在Visual Studio中,有时候会用可视化界面,取代纯文本的XML编辑界面,例如下面这样

平胸而论,Visual Studio 提供了对于清单文件的最佳编辑体验,因为它会自动根据Schema提供智能感知,甚至如你刚才看到的那样,它甚至提供了可视化界面,我爱死这个功能了。

下面我会从三个方面分别对清单文件进行详细介绍

  1. 基本属性定义
  2. 通过清单文件自定义Ribbon和快捷菜单
  3. 通过清单文件是实现多语言支持

基本属性定义

清单文件中的根元素是OfficeApp,这里会指定几个namespace,但同时会有一个至关重要的属性:xsi:type,目前我们支持三种不同类型的Office Add-in,分别是

  1. ContentApp,这是内容应用,主要是在Excel和PowerPoint中能用。通过这类Add-in,可以为宿主程序添加自定义的内容元素,例如一个自定义地图之类的。
  2. TaskPaneApp,这是应用最广的类型。通过这类Add-in,可以为宿主程序添加自定义的功能,例如通过一个自定义菜单,执行某些操作。
  3. MailApp,这是专用于Outlook的Add-in。

除此之外,OfficeApp这个根元素,还需要包含如下的基本元素

  1. Id,唯一的编号(一个GUID)
  2. Version,这个版本信息在你更新时可能需要修改
  3. ProviderName,作者及公司信息
  4. DefaultLocale,默认的语言,格式是类似于en-US这样的。我在下面还会介绍多语言支持的功能
  5. DisplayName,显示名称
  6. Description,描述
  7. IconUrl,图标文件路径(32*32,PNG格式)
  8. HighResolutionIconUrl,高清图片文件路径
  9. SupportUrl,技术支持网址
  10. AppDomains,如果你的应用中,需要导航到其他网站(不同域),则需要在这里定义。
  11. Hosts,宿主形式。因为一个Add-in其实可以同时用于几个不同的宿主(例如Word,Excel等),所以这里可以定义多个Host。
    Document (Word)
    Database (Access)
    Mailbox (Outlook)
    Notebook (OneNote)
    Presentation (PowerPoint)
    Project (Project)
    Workbook (Excel)
    
  12. DefaultSettings,默认设置,这里最关键的属性有SourceLocation,这个是用来指定Add-in加载时默认显示的页面。另外,不同的Add-in可能还会有一些自己的DefaultSettings,例如ContentApp的话,还可以设置RequestedWidth和RequestedHeight这两个属性,以确定自定义内容默认的尺寸。
  13. Permissions,这是规定Add-in拥有的对于宿主和文档的访问权限,不同的Add-in有不同的Permission设置。
    ContentApp 和 TaskPaneApp
    
    <Permissions> [Restricted | ReadDocument | ReadAllDocument | WriteDocument | ReadWriteDocument]</Permissions>
    
    MailApp
    
    <Permissions>[Restricted | ReadItem | ReadWriteItem | ReadWriteMailbox]</Permissions>
    
    

值得一提的是,如果你用Visual Studio 项目模板生成的清单文件,你会发现IconUrl以及SourceLocation 等属性,会包含一个特定的地址 ~remoteurl,这个其实会在工具进行编译和发布时自动替换为你的网站的根地址。而在Visual Studio code等工具中,你可能需要精确地设置。

通过清单文件自定义Ribbon

作为TaskPaneApp,最常见的做法是在启动后位宿主程序添加一个工具栏按钮,然后用户点击按钮的话,执行某个操作(打开内容面板和执行某个Javascript函数)。我在这一节主要介绍的是自定义Ribbon的方式。

有意思的是,要定义工具栏和清单,在清单文件中,我们称之为VersionOverrides。一个最简单的Ribbon定义如下

如果想要实现当文档打开时,就自动加载某个TaskPaneApp,则无需进行VersionOverrides,直接保留DefaultSettings即可。这样,用户在选择插入该Add-in的时候,不会去改变宿主程序的菜单(Ribbon和Context Menu),而是自动打开任务面板,而且最关键的是,这种情况下,下次再次打开文档的时候,也会自动打开这个任务面板。

<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
    <Hosts>
      <Host xsi:type="Workbook">
        <DesktopFormFactor>
          <GetStarted>
            <Title resid="Contoso.GetStarted.Title"/>
            <LearnMoreUrl resid="Contoso.GetStarted.LearnMoreUrl"/>
          </GetStarted>
            <!--函数文件,是定义可以直接被调用的Javascript函数所在的位置-->
          <FunctionFile resid="Contoso.DesktopFunctionFile.Url" />

          <!-- 扩展定义 -->
          <ExtensionPoint xsi:type="PrimaryCommandSurface">
            <!-- 如果是扩展现有的Tab,使用 OfficeTab .如果是创建新的Tab,则使用 CustomTab -->
            <OfficeTab>
              <!-- 这个id必须唯一,可以结合公司的名称. -->
              <Group>
                <Label resid="Contoso.Group1Label" />
                <Icon>
                  <bt:Image size="16" resid="Contoso.tpicon_16x16" />
                  <bt:Image size="32" resid="Contoso.tpicon_32x32" />
                  <bt:Image size="80" resid="Contoso.tpicon_80x80" />
                </Icon>

                <Control xsi:type="Button">
                  <Label resid="Contoso.TaskpaneButton.Label" />
                  <Supertip>
                    <Title resid="Contoso.TaskpaneButton.Label" />
                    <Description resid="Contoso.TaskpaneButton.Tooltip" />
                  </Supertip>
                  <Icon>
                    <bt:Image size="16" resid="Contoso.tpicon_16x16" />
                    <bt:Image size="32" resid="Contoso.tpicon_32x32" />
                    <bt:Image size="80" resid="Contoso.tpicon_80x80" />
                  </Icon>

                  <!-- 下面这个方式是打开一个内容面板 -->
                  <Action xsi:type="ShowTaskpane">
                    <TaskpaneId>ButtonId1</TaskpaneId>
                    <SourceLocation resid="Contoso.Taskpane.Url" />
                  </Action>
                  <!-- 下面这个方式是执行一个Javascript函数-->
                  <Action xsi:type="ExecuteFunction">
                    <FunctionName>SubmitDataToServer</FunctionName>
                  </Action>
                </Control>
              </Group>
            </OfficeTab>
          </ExtensionPoint>
        </DesktopFormFactor>
      </Host>
    </Hosts>
    
    <!--目前规定所有的定义必须用资源的形式来做,避免重复定义 -->
    <Resources>
      <bt:Images>
        <bt:Image DefaultValue="~remoteAppUrl/Images/Button16x16.png" />
        <bt:Image DefaultValue="~remoteAppUrl/Images/Button32x32.png" />
        <bt:Image DefaultValue="~remoteAppUrl/Images/Button80x80.png" />
      </bt:Images>
      <bt:Urls>
        <bt:Url DefaultValue="~remoteAppUrl/Functions/FunctionFile.html" />
        <bt:Url DefaultValue="~remoteAppUrl/Home.html" />
        <bt:Url DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" />
      </bt:Urls>
      <!-- ShortStrings 最长可以125. -->
      <bt:ShortStrings>
        <bt:String DefaultValue="Show Taskpane" />
        <bt:String DefaultValue="Commands Group" />
        <bt:String DefaultValue="Get started with your sample add-in!" />
      </bt:ShortStrings>
      <!-- LongStrings 最长可以250. -->
      <bt:LongStrings>
        <bt:String DefaultValue="Click to Show a Taskpane">
        </bt:String>
        <bt:String DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." />
      </bt:LongStrings>
    </Resources>
  </VersionOverrides>

通过清单文件自定义快捷菜单(Context Menu)

除了Office Ribbon的自定义之外,目前也支持通过清单文件对快捷菜单进行自定义,例如下面这个例子,是给单元格的快捷菜单增加一个按钮。这个按钮,同样可以有两种操作:打开一个内容面板,还是直接执行一个Javascript函数。

<ExtensionPoint xsi:type="ContextMenu">
    <OfficeMenu>
        <!-- Define a control that shows a task pane. -->
        <Control xsi:type="Button">
            <Label resid="Contoso.TaskpaneButton.Label" />
            <Supertip>
                <Title resid="Contoso.TaskpaneButton.Label" />
                <Description resid="Contoso.TaskpaneButton.Tooltip" />
            </Supertip>
            <Icon>
                <bt:Image size="16" resid="Contoso.tpicon_16x16" />
                <bt:Image size="32" resid="Contoso.tpicon_32x32" />
                <bt:Image size="80" resid="Contoso.tpicon_80x80" />
            </Icon>
            <Action xsi:type="ShowTaskpane">
                <SourceLocation resid="Contoso.Taskpane.Url" />
            </Action>
        </Control>
    </OfficeMenu>
</ExtensionPoint>

关于在内容面板中开发以及自定义Javascript函数的开发,我会通过另外一个专题文章来讲解。

通过清单文件实现多语言支持

Office Web Add-in的愿景是希望开发人员一次编写,处处运行——不光是在不同设备都能体验一致地工作,而且在全球都能使用。那么问题来了?如何实现这样的美好愿望呢?这个问题同样分为两个方面:通过清单文件来无代码实现UI层面的多语言支持,以及在Javascript代码中根据当前的环境实现自定义多语言支持。

后者相对简单,而且更多的是依赖于开发人员的自定义实现,这里列出来两个非常重要的属性:

  • Office.context.displayLanguage,这个属性能获取到当前Office宿主程序的显示语言。代码范例如下
    function sayHelloWithDisplayLanguage() {
        var myLanguage = Office.context.displayLanguage;
        switch (myLanguage) {
            case 'en-US':
                write('Hello!');
                break;
            case 'fr-FR':
                write('Bonjour!');
                break;
        }
    }
    
    // Function that writes to a div with id='message' on the page.
    function write(message) {
        document.getElementById('message').innerText += message; 
    }
    
  • Office.context.contentLanguage,这个属性我觉得很酷,它是能检测当前文档内容的语言,例如是一篇中文的Word文档,还是一个英文的Excel表格。
    function sayHelloWithContentLanguage() {
        var myLanguage = Office.context.contentLanguage;
        switch (myLanguage) {
            case 'en-US':
                write('Hello!');
                break;
            case 'fr-FR':
                write('Bonjour!');
                break;
        }
    }
    
    // Function that writes to a div with id='message' on the page.
    function write(message) {
        document.getElementById('message').innerText += message; 
    }
    

接下来要看一下的是在清单文件中如果定义一些UI层面的多语言支持。目前有如下的属性是支持多语言的。

  1. Description,这是Add-in的描述,定义方式如下
    <Description DefaultValue="ExcelWebAddIn2">
        <Override Locale="zh-CN" Value="我的插件描述说明......"/>
    </Description>
    
  2. DisplayName,这是Add-in的显示名称,定义方式如下
    <DisplayName DefaultValue="ExcelWebAddIn2">
        <Override Locale="zh-CN" Value="我的第二个插件"/>
    </DisplayName>
    
  3. IconUrl,这是Add-in的图标
    <IconUrl DefaultValue="~remoteAppUrl/Images/Button32x32.png">
        <Override Locale="zh-CN" Value="~remoteAppUrl/Images/zh-Button32x32.png"/>
    </IconUrl>
    
  4. HighResolutionIconUrl,这是Add-in的高清图标,定义方式如下
    <HighResolutionIconUrl DefaultValue="~remoteAppUrl/Images/Button32x32.png">
        <Override Locale="zh-CN" Value="~remoteAppUrl/Images/zh-Button32x32.png"/>
    </IconUrl>
    
  5. Resources,所有针对界面扩展(例如工具栏或者快捷菜单的按钮相关的文字,路径,图片等),定义方式大多如下
    <bt:String DefaultValue="Click to Show a Taskpane">
        <bt:Override Locale="zh-CN" Value="显示一个内容面板"/>
    </bt:String>
    
  6. SourceLocation
        <SourceLocation DefaultValue="~remoteAppUrl/Home.html">
        <Override Locale="zh-CN" Value="~remoteAppUrl/zh-Home.html"/>
        </SourceLocation>
    

关于所有目前支持的语言列表,请参考

Language Culture NameDisplay NameCulture CodeISO 639x Value
af-ZAAfrikaans - South Africa0x0436AFK
sq-ALAlbanian - Albania0x041CSQI
ar-DZArabic - Algeria0x1401ARG
ar-BHArabic - Bahrain0x3C01ARH
ar-EGArabic - Egypt0x0C01ARE
ar-IQArabic - Iraq0x0801ARI
ar-JOArabic - Jordan0x2C01ARJ
ar-KWArabic - Kuwait0x3401ARK
ar-LBArabic - Lebanon0x3001ARB
ar-LYArabic - Libya0x1001ARL
ar-MAArabic - Morocco0x1801ARM
ar-OMArabic - Oman0x2001ARO
ar-QAArabic - Qatar0x4001ARQ
ar-SAArabic - Saudi Arabia0x0401ARA
ar-SYArabic - Syria0x2801ARS
ar-TNArabic - Tunisia0x1C01ART
ar-AEArabic - United Arab Emirates0x3801ARU
ar-YEArabic - Yemen0x2401ARY
hy-AMArmenian - Armenia0x042B
Cy-az-AZAzeri (Cyrillic) - Azerbaijan0x082C
Lt-az-AZAzeri (Latin) - Azerbaijan0x042C
eu-ESBasque - Basque0x042DEUQ
be-BYBelarusian - Belarus0x0423BEL
bg-BGBulgarian - Bulgaria0x0402BGR
ca-ESCatalan - Catalan0x0403CAT
zh-CNChinese - China0x0804CHS
zh-HKChinese - Hong Kong SAR0x0C04ZHH
zh-MOChinese - Macau SAR0x1404
zh-SGChinese - Singapore0x1004ZHI
zh-TWChinese - Taiwan0x0404CHT
zh-CHSChinese (Simplified)0x0004
zh-CHTChinese (Traditional)0x7C04
hr-HRCroatian - Croatia0x041AHRV
cs-CZCzech - Czech Republic0x0405CSY
da-DKDanish - Denmark0x0406DAN
div-MVDhivehi - Maldives0x0465
nl-BEDutch - Belgium0x0813NLB
nl-NLDutch - The Netherlands0x0413
en-AUEnglish - Australia0x0C09ENA
en-BZEnglish - Belize0x2809ENL
en-CAEnglish - Canada0x1009ENC
en-CBEnglish - Caribbean0x2409
en-IEEnglish - Ireland0x1809ENI
en-JMEnglish - Jamaica0x2009ENJ
en-NZEnglish - New Zealand0x1409ENZ
en-PHEnglish - Philippines0x3409
en-ZAEnglish - South Africa0x1C09ENS
en-TTEnglish - Trinidad and Tobago0x2C09ENT
en-GBEnglish - United Kingdom0x0809ENG
en-USEnglish - United States0x0409ENU
en-ZWEnglish - Zimbabwe0x3009
et-EEEstonian - Estonia0x0425ETI
fo-FOFaroese - Faroe Islands0x0438FOS
fa-IRFarsi - Iran0x0429FAR
fi-FIFinnish - Finland0x040BFIN
fr-BEFrench - Belgium0x080CFRB
fr-CAFrench - Canada0x0C0CFRC
fr-FRFrench - France0x040C
fr-LUFrench - Luxembourg0x140CFRL
fr-MCFrench - Monaco0x180C
fr-CHFrench - Switzerland0x100CFRS
gl-ESGalician - Galician0x0456
ka-GEGeorgian - Georgia0x0437
de-ATGerman - Austria0x0C07DEA
de-DEGerman - Germany0x0407
de-LIGerman - Liechtenstein0x1407DEC
de-LUGerman - Luxembourg0x1007DEL
de-CHGerman - Switzerland0x0807DES
el-GRGreek - Greece0x0408ELL
gu-INGujarati - India0x0447
he-ILHebrew - Israel0x040DHEB
hi-INHindi - India0x0439HIN
hu-HUHungarian - Hungary0x040EHUN
is-ISIcelandic - Iceland0x040FISL
id-IDIndonesian - Indonesia0x0421
it-ITItalian - Italy0x0410
it-CHItalian - Switzerland0x0810ITS
ja-JPJapanese - Japan0x0411JPN
kn-INKannada - India0x044B
kk-KZKazakh - Kazakhstan0x043F
kok-INKonkani - India0x0457
ko-KRKorean - Korea0x0412KOR
ky-KZKyrgyz - Kazakhstan0x0440
lv-LVLatvian - Latvia0x0426LVI
lt-LTLithuanian - Lithuania0x0427LTH
mk-MKMacedonian (FYROM)0x042FMKD
ms-BNMalay - Brunei0x083E
ms-MYMalay - Malaysia0x043E
mr-INMarathi - India0x044E
mn-MNMongolian - Mongolia0x0450
nb-NONorwegian (Bokm?l) - Norway0x0414
nn-NONorwegian (Nynorsk) - Norway0x0814
pl-PLPolish - Poland0x0415PLK
pt-BRPortuguese - Brazil0x0416PTB
pt-PTPortuguese - Portugal0x0816
pa-INPunjabi - India0x0446
ro-RORomanian - Romania0x0418ROM
ru-RURussian - Russia0x0419RUS
sa-INSanskrit - India0x044F
Cy-sr-SPSerbian (Cyrillic) - Serbia0x0C1A
Lt-sr-SPSerbian (Latin) - Serbia0x081A
sk-SKSlovak - Slovakia0x041BSKY
sl-SISlovenian - Slovenia0x0424SLV
es-ARSpanish - Argentina0x2C0AESS
es-BOSpanish - Bolivia0x400AESB
es-CLSpanish - Chile0x340AESL
es-COSpanish - Colombia0x240AESO
es-CRSpanish - Costa Rica0x140AESC
es-DOSpanish - Dominican Republic0x1C0AESD
es-ECSpanish - Ecuador0x300AESF
es-SVSpanish - El Salvador0x440AESE
es-GTSpanish - Guatemala0x100AESG
es-HNSpanish - Honduras0x480AESH
es-MXSpanish - Mexico0x080AESM
es-NISpanish - Nicaragua0x4C0AESI
es-PASpanish - Panama0x180AESA
es-PYSpanish - Paraguay0x3C0AESZ
es-PESpanish - Peru0x280AESR
es-PRSpanish - Puerto Rico0x500AES
es-ESSpanish - Spain0x0C0A
es-UYSpanish - Uruguay0x380AESY
es-VESpanish - Venezuela0x200AESV
sw-KESwahili - Kenya0x0441
sv-FISwedish - Finland0x081DSVF
sv-SESwedish - Sweden0x041D
syr-SYSyriac - Syria0x045A
ta-INTamil - India0x0449
tt-RUTatar - Russia0x0444
te-INTelugu - India0x044A
th-THThai - Thailand0x041ETHA
tr-TRTurkish - Turkey0x041FTRK
uk-UAUkrainian - Ukraine0x0422UKR
ur-PKUrdu - Pakistan0x0420URD
Cy-uz-UZUzbek (Cyrillic) - Uzbekistan0x0843
Lt-uz-UZUzbek (Latin) - Uzbekistan0x0443
vi-VNVietnamese - Vietnam0x042AVIT

关于Office Add-in的本地化支持,官方文档在 https://docs.microsoft.com/en-us/office/dev/add-ins/develop/localization。

其他注意事项

  1. 确保add-in ID是唯一的,这是一个GUID。如果使用Visual Studio开发的话,可以在工具菜单中,找到Create GUID的一个小工具,但也可以通过其他一些方式生成。

  2. 所有的Url都必须是https的。

  3. 所有的图片(例如用在命令按钮上面的图片),都必须是允许缓存,也就是说服务器不能在Header里面添加on-cache/no-store 这样的值。

  4. 如果add-in需要发布到Office Store,则必须提供SupportUrl这个属性。