2010年2月15日,星期一

通过Web部件和现场控件以编程方式自定义功能区

在本系列文章中:

  1. 定制功能区–创建选项卡,组和控件
  2. 将功能区项目添加到现有选项卡/组中
  3. 功能区自定义-下拉控件,客户端对象模型和JavaScript页面组件
  4. 通过Web部件和现场控件以编程方式自定义功能区(本文)

与我之前的文章相比,本文是’t详细的演练–而是,它是一种技术和信息的汇编,应该可以帮助您为Web部件,自定义字段控件甚至其他任何类型的页面控件实现功能区自定义。在撰写本文时,我还没有’除了一些资源,我确实看到了很多关于此的文章’会提到。我很想在这里写详细的演练,但是a’这是因为a)微软最近针对其中一种情况发布了一些很好的信息,所以我’d希望指出这一点,b)要深入研究这里的所有场景,将需要数周的时间,而c)因为如果我不这样做’我不要再停止写丝带了’明年这个时候我仍然会在这个话题上,坦率地说,我有一个 这本书的一章将在完全不同的SharePoint 2010领域上写,我需要继续学习!所以我们’我会看每一个要求’讨论我认为您的关键技术是什么’d结合我的一些观察来使用。

请注意,由于到目前为止,有关该主题的文档普遍缺乏(而且我还没有’(在所有情况下都将一切都触底了),这里有几件事是猜测,而不是确凿的事实。一世’请澄清这些内容,并会尽力回到本文并在出现新信息时添加更新。 

在我们潜入之前,请记住,如果您’遵循功能区设计原则,您最有可能应该使用ContextualGroup(在 将功能区项目添加到现有选项卡/组中) –这是用于功能区元素的容器,这些功能区仅根据用户正在执行的操作才相关(显示为可见,但此处未激活):

语境小组

如果不是’注意,如果您只需要在现有选项卡或组上添加一些在某些情况下被激活的控件,则可能会更容易。在这种情况下,您只需向‘EnabledScript’控件的属性–我在的“通知/状态”演示中展示了这一点 自定义功能区(第1部分)–创建选项卡,组和控件. 的rest of this article focuses on how 您 might get a contextual group (see image above) to show in different scenarios.

 从Web部件添加功能区项目

在SharePoint 2010中,Web部件框架现在具有用于功能区自定义的特殊规定,这意味着需要为您处理几件事。 Microsoft现在以SharePoint开发人员文档博客上的文章形式发布了有关此方案的一些指导- 如何使用上下文选项卡创建Web部件 。那里’里面有很多黄金信息,但是我’我将在这里说明要点:

  • 使用服务器端代码,‘RegisterDataExtension’SPRibbon的方法可用于将XML传递到功能区框架。
    • 这是使用CustomAction元素的完全声明方法的替代方法。据我所知,这两种技术都只能用于特定于某个控件的功能区自定义(这与功能区自定义(例如,要使用CustomAction的特定类型的所有列表相对)相反)。
  • Your web part needs to implement 的new IWebPartPageComponentProvider 接口和它’s WebPartContextualInfo 属性
    • This allows 您 to specify information about 的associated ribbon customization(s) and 的ID of 的associated 页面组件 (which we discussed 上次)作为您的自定义位。 这使功能区框架能够‘link’功能区更改您的Web部件–意味着某些事情会为您照顾,例如仅当您的Web部件将焦点放在页面上时才触发命令(如果您通过使用‘getFocusedCommands’ in 您r 页面组件). Without this 您 would have to write 的JavaScriptcode to manually handle page events for controls emitted by 您r web part e.g. 的click event for a textbox.

从字段控件添加功能区项目

像网络部件一样,我 认为 (在此处推测)框架中有特殊的规定,用于从现场控件进行功能区定制。考虑到,就像Web部件一样,字段控件通常只在具有焦点时才显示其上下文功能区选项-因为所有字段控件都属于这种情况,所以我认为Microsoft可以为我们抽象一些处理方法对我来说很有意义。如果不, 将负责编写JavaScript来检测用户何时单击您的字段,以便您可以显示ContextualGroup。

仔细研究一下,我注意到所有SharePoint字段控件都具有4个新属性(因为它们是在一个基类上实现的, Microsoft.SharePoint.WebControls.FormComponent):

  • RibbonTabCommand
  • RibbonContextualGroupCommand
  • RibbonGroupCommand
  • 功能区命令

I’m surmising that these control properties would be set declaratively in 的hosting page and are designed to match up with commands specified in an accompanying 页面组件 - this would enable 您 to run client-side code similar to my sample 上次, perhaps to 初始化 data for 您r ribbon controls and so on. However, when I try to implement these commands, my 页面组件’s ‘handleCommand’方法从不接收这些命令。所以我’我做错了什么或这个理论不正确。在这种情况下,不必担心仍可以完全实现针对现场控制的功能区定制,这将需要做更多的工作。继续阅读。

使用服务器端代码显示功能区项目

超越任何思维‘framework support’对于我们的功能区定制针对的目标,我们始终可以编写服务器端或客户端代码以显示上下文组。实际上,我已经在其中显示了服务器端代码 将功能区项目添加到现有选项卡/组中(功能区定制第2部分):

protected override void OnPreRender(EventArgs e)
{
    SPRibbon currentRibbon = SPRibbon.GetCurrent(this.Page);
    currentRibbon.MakeTabAvailable("COB.SharePoint.Ribbon.ContextualTab");
    currentRibbon.MakeContextualGroupInitiallyVisible("COB.SharePoint.Ribbon.ContextualGroup", string.Empty);
    
    base.OnPreRender(e);
}

这可能仅在您的内容与上下文相关时才适用-ish –应用程序页面将是一个很好的例子。在此示例中,我们只关心用户在页面上,然后我们可以显示我们的选项。它没有’无论哪个控件具有焦点,实际上,我们的选项都可以在页面加载时默认显示。但是,如果您需要页面级‘contextuality’(我可能只是顺便发明了这个词-今天在给老板的一句话中使用它)然后很可能是您’当用户在页面上执行特定操作时,例如,希望使用JavaScript来显示您的上下文组。编辑某个字段。您’d然后寻找一些客户端代码来检测到这一点并做出相应的响应。   

使用客户端代码显示功能区项目

所以,最后一种情况-如果您需要显示来自某些东西的功能区项目怎么办’一个Web部件或现场控件(或者像我一样,’不能在现场控制框架中提供任何可能会或可能不会有帮助的东西),并且必须在客户端将其完全上下文化?好吧,我很失望’我到目前为止在这里画了另一个空白– I’d很乐意听到知道答案的任何人。万一你’重新进行功能区开发并且对此感兴趣,在这里’我的旅程摘要:

  • 在客户端OM中检查了SP.Ribbon名称空间
  • 在调试器和各种现成的JavaScript文件中度过了许多欢乐时光,以查找核心产品执行此操作的示例(例如日历,富文本编辑器,仅举几例)
  • 在CUI.Ribbon对象上找到了一些有趣的方法,例如showContextualGroup(‘foo’)和selectTabByCommand(‘bar’)
    • 请注意,在整个SharePoint根目录中,这些方法仅由RTE调用,而不在SharePoint代码库中全局调用
    • 注意,RTE代码从属性(RTE.RichTextEditorComponent。$ 3b()获得一个CUI.Ribbon实例。–N.B.大部分JS都是经过混淆或机器生成的*)
  • 试图使用SP.Ribbon.PageManager.get_instance()。get_ribbon()(在OOTB代码库中的其他位置使用)为我提供CUI.Ribbon实例,但是这给了我一个空值
  • 尝试使用‘_ribbon’页面级变量,但是它似乎不是CUI.Ribbon类型,因为调试器显示它没有方法I’m trying to call
  • 我尝试了其他几件事’ve forgotten now

不用说,我’d喜欢听到我的声音’我对此不见了。如果没有其他要求,希望MS会尽快发布更多信息,这将为如何处理此情况提供一些启示。

概要

这篇文章没有’声称拥有所有答案,但它可以作为“leg up" if 您’现在尝试构建任何这些方案。一世’我希望这方面缺乏深入的信息反映出RTM尚需时日,并且功能开发人员会不时地从SDK中获得一些喜爱。我在这里讨论的关键场景是显示来自Web部件和字段控件的自定义功能区元素,还显示了使用服务器或客户端代码显示自定义项的更一般的情况。

如果您可以在此处覆盖我的报道中的空白,请随时发表评论。

2010年2月5日,星期五

功能区自定义-下拉控件,客户端对象模型和JavaScript页面组件

在本系列文章中:

  1. 定制功能区–创建选项卡,组和控件
  2. 将功能区项目添加到现有选项卡/组中
  3. 功能区自定义-下拉控件,客户端对象模型和JavaScript页面组件(本文)
  4. 通过Web部件和现场控件以编程方式自定义功能区

一旦了解了如何将自定义项放置在功能区中的正确位置,您可能会发现不仅仅需要添加按钮并使用其他控件(例如下拉菜单,复选框,弹出锚点等)之外。这种功能区开发确实涉及其中,但是与SharePoint开发中的许多事情一样,一旦您’我第一次知道整体“template”对于以后的场合-希望我’此处显示的内容是您可能想做的许多高级事情的良好起点。关键是JavaScript“page component”除了声明性XML,通常还需要’在我以前的帖子中看到过。

[Beta旁注]在撰写本文时(2010年2月,仍处于测试阶段),’他们认为功能区开发可能是SP2010中较复杂的开发领域之一。也许Microsoft的一些出色文档可能会改变这一点,但是现在有许多难以理解的死角–目前,SDK中几乎没有涵盖这些内容(实际上几乎没有任何内容),因此,除非您有内部信息,否则’一路主要是鲜血,汗水和眼泪。一世’ve在产品小组的产品经理(伊丽莎白·奥尔森(Elisabeth Olson))中提到了这一点,听起来更多的MS指南即将推出,因此’s hope.

的sample

我的示例显示了功能区中自定义下拉菜单的使用– I’稍后会详细介绍,但我’此处显示的m可用于 许多 功能区中的控件,而不仅仅是下拉列表。所以如果你’重新想做不’专门涉及下拉菜单,我’d建议无论如何都要阅读,因为这种技术可能仍然是您将要使用的技术。

第一次单击时,它将使用客户端对象模型来获取当前Web中的列表集合,然后在下拉列表中将其显示为选项:

CustomDropdownInRibbon

CustomDropdownInRibbonExpanded

当选择了一个项目,一个简单的JavaScript发出警报与选择列表的名称,但现实生活中的实施当然会做一些与价值更为有用。这里的目的是说明如何使用除按钮之外的功能区控件,以及如何在它们后面编写代码–一旦你可以做到这一点,你’能够构建广泛的解决方案。  

要注意的一件事–可以完全以XML将项目添加到下拉列表或其他控件中。一世’m choosing to use a 的JavaScript页面组件 to illustrate what happens when 您 need “code-behind”例如以我的情况遍历网络中的所有列表。

什么’s required – summary

  1. 声明式XML,以配置功能区控件
  2. 的JavaScript“page component”,通常在外部.js文件中声明
  3. 向页面添加JavaScript(使用最适合功能区自定义范围的技术–Web部件/委托控件中的代码(添加到AdditionalPageHead等)中。这将:
    1. Link 的external .js file
    2. 确保已加载核心相关的.js文件,例如SP.js,CUI.js
    3. Call into 的initialization function within 我们的 页面组件 –这将向功能区框架注册我们的组件,并确保将我们的组件添加到页面中。

1.声明式XML

我使用了以下XML– here I’m实际上显示的是一个简短的摘录,仅适用于包含我的控件的组。真的’s just 的‘Controls’最有趣的部分,周围的环境取决于您是否要 创建一个新标签 要么 add 的items into an existing tab/group,有关这些详细信息,请参见我以前的文章。

Key points of note are 的Command, PopulateQueryCommand, and QueryCommandattributes on 的dropdown – these will 链接 into 我们的 的JavaScript页面组件:

<Group
  Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup"
  Description="Contains advanced ribbon controls"
  Title="Page component sample"
  Sequence="53"
  Template="Ribbon.Templates.COB.OneLargeExample">
  <控制项 Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Controls">
    <Label Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Label" 
           ForId="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown" 
           Command="LabelCommand"
           LabelText="Select list:"
           Sequence="16" 
           TemplateAlias="c1"/>
    <DropDown
      Id="COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown"
      Sequence="17"
      Command="COB.PageComponent.Command.DoAction"
      动态地填充="true"
      PopulateOnlyOnce="true"
      PopulateQueryCommand="COB.PageComponent.Command.PopulateDropDown"
      QueryCommand="COB.PageComponent.Command.QueryDoAction"
      Width="75px"
      TemplateAlias="c2" />
  </控制项>
</Group>

2. 的JavaScript页面组件

这是复杂的位,至少是第一次。我们正在有效地编写面向对象的JavaScript,其中包含为功能区控件提供支持的类。考虑这样的JavaScript‘template’ to use for 页面组件s, where 您’ll每次修改实际的实现位。一世’ve评论了一些关键点,建议进行滚动浏览,然后我们’ll浏览重点:

Type.registerNamespace('COB.SharePoint.Ribbon.PageComponent');
 
COB.SharePoint.Ribbon.PageComponent = function () {
    COB.SharePoint.Ribbon.PageComponent.initializeBase(this);
}
 
// 的初始化 function needs to be called by some 脚本 added to 的page elsewhere - in 的end, it does 的important work 
// of calling PageManager.addPageComponent()..
COB.SharePoint.Ribbon.PageComponent.initialize = function () {
    ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, COB.SharePoint.Ribbon.PageComponent.initializePageComponent), 'SP.Ribbon.js');
}
COB.SharePoint.Ribbon.PageComponent.initializePageComponent = function() {
    
    var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
    if (null !== ribbonPageManager) {
        ribbonPageManager.addPageComponent(COB.SharePoint.Ribbon.PageComponent.instance);
    }
}
 
COB.SharePoint.Ribbon.PageComponent.prototype = {
    init: function () { },
 
    getFocusedCommands: function () {
        return ['COB.PageComponent.Command.FieldControl.GroupCommand', 'COB.PageComponent.Command.FieldControl.TabCommand', 'COB.PageComponent.Command.FieldControl.ContextualGroupCommand', 'COB.PageComponent.Command.FieldControl.RibbonCommand'];
    },
 
    getGlobalCommands: function () {
        return ['COB.PageComponent.Command.DoAction', 'COB.PageComponent.Command.PopulateDropDown', 'COB.PageComponent.Command.QueryDoAction'];
    },
 
    canHandleCommand: function (commandId) {
        if ((commandId === 'COB.PageComponent.Command.DoAction') ||
            (commandId === 'COB.PageComponent.Command.PopulateDropDown') || (commandId === 'COB.PageComponent.Command.QueryDoAction')) {
            return true;        
        }
        else {
            return false;
        }
    },
 
    handleCommand: function (commandId, properties, sequence) {
        if (commandId === 'COB.PageComponent.Command.FieldControl.GroupCommand') {
            alert("COB.PageComponent.Command.FieldControl.GroupCommand fired");
        }
        if (commandId === 'COB.PageComponent.Command.FieldControl.TabCommand') {
            alert("COB.PageComponent.Command.FieldControl.TabCommand fired");
        }
        if (commandId === 'COB.PageComponent.Command.FieldControl.ContextualGroupCommand') {
            alert("COB.PageComponent.Command.FieldControl.ContextualGroupCommand fired");
        }
        if (commandId === 'COB.PageComponent.Command.FieldControl.RibbonCommand') {
            alert("COB.PageComponent.Command.FieldControl.RibbonCommand fired");
        }
        if (commandId === 'COB.PageComponent.Command.QueryDoAction') {
            // this command executes as soon as tab is requested, so do initialization here ready for if 我们的 dropdown gets requested..
            loadCurrentWebLists();
        }
        if (commandId === 'COB.PageComponent.Command.PopulateDropDown') {
            // actually build 的dropdown contents by setting 的PopulationXML 属性 to a value with 的expected format. We have to deal with possible 
            // timing issues/dependency on core SharePoint JS code with an ExecuteOrDelay..
            ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, getDropdownItemsXml), 'SP.js');
 
            properties.PopulationXML = getDropdownItemsXml();
        }
        if (commandId === 'COB.PageComponent.Command.DoAction') {
            // here we're using 的SourceControlId to detect 的selected item, but more normally each item would have a unique commandId (rather than 'DoAction'). 
            // However this isn't possible in this case since each item is a list in 的current web, and this can change..
            var selectedItem = properties.SourceControlId.toString();
            var listName = selectedItem.substring(selectedItem.lastIndexOf('.') + 1);
            alert("You selected 的list: " + listName);
        }
    },
 
    isFocusable: function () {
        return true;
    },
 
    receiveFocus: function () {
        return true;
    },
 
    yieldFocus: function () {
        return true;
    }
}
 
// **** BEGIN: helper code specific to this sample ****
 
// some global variables which we'll use with 的async processing..
var lists = null;
var querySucceeded = false;
 
// use 的Client Object Model to fetch 的lists in 的current site..        
function loadCurrentWebLists() {
    var clientContext = new SP.ClientContext.get_current();
    var web = clientContext.get_web();
    this.lists = web.get_lists();
 
    clientContext.load(lists);
    clientContext.executeQueryAsync(
           Function.createDelegate(this, this.onQuerySucceeded),
           Function.createDelegate(this, this.onQueryFailed));
}
 
function onQuerySucceeded() {
    querySucceeded = true;
}
 
function onQueryFailed(sender, args) {
    querySucceeded = false;
}
 
function getDropdownItemsXml() {
    var sb = new Sys.StringBuilder();
    sb.append('<Menu Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu\'>');
    sb.append('<MenuSection DisplayMode=\'Menu\' Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage\'>');
    sb.append('<Controls Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage.Controls\'>');
    if (querySucceeded)
    {
        var listEnumerator = lists.getEnumerator();
 
        while (listEnumerator.moveNext()) {
            var oList = listEnumerator.get_current();
            
            sb.append('<Button');
            sb.append(' Id=\'COB.SharePoint.Ribbon.WithPageComponent.PCNotificationGroup.Dropdown.Menu.Manage.');
            sb.append(oList.get_title());
            sb.append('\'');
            sb.append(' Command=\'');
            sb.append('COB.PageComponent.Command.DoAction');
            sb.append('\'');
            sb.append(' LabelText=\'');
            sb.append(SP.Utilities.HttpUtility.htmlEncode(oList.get_title()));
            sb.append('\'');
            sb.append('/>');
        }
    }
    sb.append('</Controls>');
    sb.append('</MenuSection>');
    sb.append('</Menu>');
    return sb.toString();
}
  
// **** END: helper code specific to this sample ****
 
COB.SharePoint.Ribbon.PageComponent.registerClass('COB.SharePoint.Ribbon.PageComponent', CUI.Page.PageComponent);
COB.SharePoint.Ribbon.PageComponent.instance = new COB.SharePoint.Ribbon.PageComponent();
 
NotifyScriptLoadedAndExecuteWaitingJobs("COB.SharePoint.Ribbon.PageComponent.js");
 
  • 的‘initialize’函数通常负责调用‘addPageComponent’在功能区PageManager上(但不是在SP.Ribbon.js加载之前)
  • 的commands referenced in 的JS are those specified in 的control XML e.g. for my dropdown
    • 的‘getFocusedCommands’函数返回一个命令数组,当控件具有焦点时应执行
    • 的‘getGlobalCommands’函数返回应执行的命令数组 而不管 专注
    • 我们需要列出可以在‘canHandleCommand’ function, and 为以下各项提供实际的实现‘handleCommand’
  • 请注意以下有关使用的各种命令的要点:
    • PopulateQueryCommand–用于构建控件中的项目列表。这是我的地方’m使用客户端对象模型(ECMAScript版本)来获取当前网站的列表。
    • QueryCommand–当父容器(例如标签)被激活时调用。记住功能区就是关于“script on demand”(延迟加载),所以我’我选择这里作为进行实际客户端OM请求初始化工作的更好位置– more on this later.
    • 命令–当用户实际选择项目时调用
  • 重要–这些命令适用于除下拉菜单以外的许多功能区控件,例如FlyoutAnchor,SplitButton,ToggleButton,TextBox,Checkbox,Spinner等。这就是即使此信息也很重要的原因’不是专门的下拉控件’re working with.
  • 的key to populating controls which take collections is to use 的‘properties’ object passed to handleCommand, using either: ul>
  • properties.PopulationXML
  • properties.PopulationJSON
  • I’m使用properties.PopulationXML提供应该显示在我的下拉菜单中的项目,所需格式为:
       1: <Menu Id="">
       2:   <MenuSection Id="">
       3:     <控制项 Id="">
       4:       <Button Command="" Id="" LabelText="" />
       5:       ..a 'Button' element here for each item in 的collection..
       6:     </控制项>
       7:   </MenuSection>
       8: </Menu>
    我没有’尚未看到有关如何使用.PopulationJSON属性的示例,所以请不要’不知道要在JSON中使用的确切名称。
  • 将客户端OM与功能区JS框架结合起来有一些有趣的方面–有效地使用了异步模型,这意味着在功能区框架需要它时,方法调用的结果可能尚未准备好(毕竟它发生在不同的请求上)。一世’在本文结尾处的示例中,我将解释如何处理此问题。

3.页面级JavaScript

的final element is 的的JavaScriptyou need to add to 的page to call into 的页面组件. In my example I’我很高兴将此JavaScript添加到我网站的每个页面中(因为’是我的功能区自定义范围),因此我使用了 委托控制 在AdditionalPageHead中添加一个自定义用户控件,其主体如下所示: 

<SharePoint:ScriptLink Name="sp.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink1" />
<SharePoint:ScriptLink Name="CUI.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink3" />
<SharePoint:ScriptLink Name="/_layouts/COB.SharePoint.Demos.Ribbon/COB.SharePoint.Ribbon.PageComponent.js" LoadAfterUI="true" OnDemand="false" Localizable="false" runat="server" ID="ScriptLink2" />
    <脚本 type="text/javascript">
     
        //<![CDATA[
            function initCOBRibbon() {
                COB.SharePoint.Ribbon.PageComponent.initialize();
            }
     
            ExecuteOrDelayUntilScriptLoaded(initCOBRibbon, 'COB.SharePoint.Ribbon.PageComponent.js');
    //    
    //]]>
</脚本>

的important things here are that we ensure required system JS files are loaded with 的ScriptLink tag, do 的same for 我们的 JS file, then call 的.initialize() function of 我们的 页面组件.

因此,功能区中用于复杂控件的那些组件!通过根据需要定制此信息/示例代码,应该可以进行各种功能区自定义(请记住‘handleCommand’ is 的key implementation hook), and I definitely 认为 that starting from such a 模板 is 的way to go.

附录-在功能区中使用客户端对象模型的注意事项

使用功能区时,很快就会发现,如果客户端对象模型没有’不存在,事情就会 许多 棘手的–它们是满足许多需求的自然选择。尽管如此,仍然存在一些挑战–考虑一个控件(例如下拉菜单)将拥有它’的项目集合尽可能晚地填充‘PopulateDynamically’设置为true(通常是个好主意),即当实际上单击下拉列表选择一个项目时! 这是因为色带是围绕“script on demand” model (you’ll often see “SOD”Microsoft中的引用’s JavaScript) –这样可以确保仅将所需的JavaScript下载到客户端,而不会下载更多。这解决了在SharePoint 2007 WCM网站上,我们将为匿名用户隐藏core.js文件的问题,因为该文件很大,而这些用户不需要。 无论如何,当单击下拉列表时,此时功能区框架将调用‘handleCommand’使用为指定的命令‘PopulateQueryCommand’值。如果您在此处运行客户端OM代码’不好,因为你赢了’无法获得结果,然后由于异步模型–结果将在很长时间后提供给回调函数‘handleCommand’已经完成,所以最终结果是您的控件将为空。

因此,您需要进行实际处理 之前 的‘PopulateQueryCommand’ is called. You 可以 选择在页面加载后立即执行操作,但是在大多数情况下,这可能效率不高–如果用户不这样做怎么办’不会在此页面加载时接近功能区控件吗?在这种情况下,我们将需要进行一些客户端处理 and an extra request to 的server 完全没有必要–在人流量大的页面上,这可能是个坏消息。没有任何文档’目前尚无法确定,但似乎‘QueryCommand’是放置此类客户端OM代码的好地方–当父容器(例如标签)显示为可见时(在此处,’现在用户可以使用我们的控件了)。在我的代码中,我在此处运行了实际的客户端OM查询,并将结果存储在页面级变量中-然后对其进行选择并对其进行迭代‘PopulateQueryCommand’。在脚本运行此命令以填充控件时,查询已执行并且数据已准备就绪– happy days. I’我会很感兴趣地看到这方面发生了什么,看看这是否是预期的模式,或者,如果我’我完全错了。

概要

Complex ribbon customizations are likely to require a 的JavaScript页面组件 - in general 页面组件s are somewhat complex (partly because of 的current lack of documentation perhaps), but once 您 have a suitable 模板, subsequent implementations should be easier. If 您 need to use Client Object Model code, beware of 的“异步/生命周期问题”在此处进行了讨论,并确保您的代码具有可用于功能区框架的数据。