使用库

Windows 功能区框架为开发人员提供了一个可靠且一致的模型,用于跨各种基于集合的控件管理动态内容。 通过调整和重新配置功能区 UI,这些动态控件使框架能够响应主机应用程序和功能区本身中的用户交互,并提供处理各种运行时环境的灵活性。

简介

功能区框架动态适应运行时条件、应用程序要求和最终用户输入的能力突出了框架丰富的 UI 功能,并为开发人员提供了满足广泛客户需求的灵活性。

本指南的重点是介绍框架支持的动态库控件,解释它们的差异,讨论何时和在哪里最好使用它们,并演示如何将其合并到功能区应用程序中。

库是功能上和图形丰富的列表框控件。 库的项集合可以按类别进行组织,以灵活的列和基于行的布局显示,以图像和文本表示,并且根据库的类型,支持实时预览。

库在功能上不同于其他动态功能区控件,原因如下:

  • 库实现 IUICollection 接口,该接口定义用于操作库项集合的各种方法。
  • 库可以在运行时更新,具体取决于功能区中直接发生的活动,例如当用户将命令添加到快速访问工具栏 (QAT) 时。
  • 库可以在运行时更新,具体取决于从运行时环境间接发生的活动,例如打印机驱动程序仅支持纵向页面布局时。
  • 库可以在运行时更新,具体取决于在主机应用程序中间接发生的活动,例如当用户选择文档中的项时。

功能区框架公开两种类型的库:项库和命令库。

项库

项库包含相关项的基于索引的集合,其中每个项由图像和/或字符串表示。 控件绑定到依赖于 由 UI_PKEY_SelectedItem 属性标识的索引值的单个 Command 处理程序。

项库支持实时预览,这意味着基于鼠标悬停或焦点显示命令结果,而无需提交或实际调用命令。

重要

框架不支持在应用程序菜单中托管项库。

 

命令库

命令库包含不同的非索引项的集合。 每个项都由通过命令 ID 绑定到命令处理程序的单个控件表示。 与独立控件一样,命令库中的每个项都将输入事件路由到关联的命令处理程序, 命令库本身不侦听事件。

命令库不支持实时预览。

功能区框架中有四个库控件: DropDownGallerySplitButtonGalleryInRibbonGalleryComboBox。 除 ComboBox 之外的所有项都可以实现为项库或命令库。

DropDownGallery 是显示包含互斥项或命令集合的下拉列表的按钮。

以下屏幕截图演示了适用于 Windows 7 的 Microsoft 画图 中的功能区下拉库控件。

适用于 Windows 7 的 Microsoft Paint 中下拉库控件的屏幕截图。

SplitButtonGallery

SplitButtonGallery 是一个复合控件,它在主按钮上公开其集合中的单个默认项或 Command,并在单击辅助按钮时显示的互斥下拉列表中显示其他项或命令。

以下屏幕截图演示了 windows 7 Microsoft 画图 中的功能区拆分按钮库控件。

适用于 Windows 7 的 Microsoft 画图中拆分按钮库控件的屏幕截图。

InRibbonGallery

InRibbonGallery 是一个库,用于在功能区中显示相关项或命令的集合。 如果库中的项过多,则会提供展开箭头,以在展开的窗格中显示集合的其余部分。

以下屏幕截图演示了适用于 Windows 7 的 Microsoft 画图 中的功能区内库控件。

Microsoft 画图功能区中功能区内库控件的屏幕截图。

ComboBox

ComboBox 是一个单列列表框,其中包含具有静态控件或编辑控件和下拉箭头的项集合。 当用户单击下拉箭头时,将显示控件的列表框部分。

以下屏幕截图演示了Windows Live 影音制作中的功能区组合框控件。

Microsoft 画图功能区中组合框控件的屏幕截图。

由于 ComboBox 是独占项库,因此它不支持 Command 项。 它也是唯一不支持命令空间的库控件。 (命令空间是在标记中声明并在项库或命令库底部列出的命令集合。)

下面的代码示例演示在 DropDownGallery 中声明三按钮命令空间所需的标记。

<DropDownGallery 
  CommandName="cmdSizeAndColor" 
  TextPosition="Hide" 
  Type="Commands"
  ItemHeight="32"
  ItemWidth="32">
  <DropDownGallery.MenuLayout>
    <FlowMenuLayout Rows="2" Columns="3" Gripper="None"/>
  </DropDownGallery.MenuLayout>
  <Button CommandName="cmdCommandSpace1"/>
  <Button CommandName="cmdCommandSpace2"/>
  <Button CommandName="cmdCommandSpace3"/>
</DropDownGallery>

以下屏幕截图演示了上述代码示例的三个按钮命令空间。

下拉列表中的三个按钮命令空间的屏幕截图。

本部分讨论功能区库的实现详细信息,并逐步介绍如何将它们合并到功能区应用程序中。

基本组件

本部分介绍一组属性和方法,这些属性和方法构成功能区框架中的动态内容的主干,并支持在运行时添加、删除、更新和以其他方式操作功能区库的内容和视觉布局。

IUICollection

库需要一组基本方法来访问和操作其集合中的单个项。

IEnumUnknown 接口定义这些方法,框架通过 IUICollection 接口中定义的其他方法补充其功能。 IUICollection 由框架为功能区标记中的每个库声明实现。

如果需要 IUICollection 接口未提供的其他功能,则可以将由主机应用程序实现并从 IEnumUnknown 派生的自定义集合对象替换为框架集合。

IUICollectionChangedEvent

要使应用程序响应库集合中的更改,它必须实现 IUICollectionChangedEvent 接口。 应用程序可以通过 IUICollectionChangedEvent::OnChanged 事件侦听器订阅来自 IUICollection 对象的通知。

当应用程序将框架提供的库集合替换为自定义集合时,应用程序应实现 IConnectionPointContainer 接口。 如果未实现 IConnectionPointContainer ,则应用程序无法通知框架自定义集合中需要对库控件进行动态更新的更改。

在未实现 IConnectionPointContainer 的情况下,只能通过 IUIFramework::InvalidateUICommand 和 IUICommandHandler::UpdateProperty 或调用 IUIFramework::SetUICommandProperty 来更新库控件。

IUISimplePropertySet

应用程序必须为库集合中的每个项或 Command 实现 IUISimplePropertySet。 但是,可以使用 IUISimplePropertySet::GetValue 请求的属性会有所不同。

通过 UI_PKEY_ItemsSource 属性键定义项并将其绑定到库,并使用 IUICollection 对象公开属性。

下表描述了项库中项的有效属性 (UI_COMMANDTYPE_COLLECTION) 。

注意

某些项属性(如 UI_PKEY_Label)可以在标记中定义。 有关详细信息,请参阅 属性键 参考文档。

 

控制

属性

ComboBox

UI_PKEY_LabelUI_PKEY_CategoryId

DropDownGallery

UI_PKEY_LabelUI_PKEY_ItemImage、UI_PKEY_CategoryId

InRibbonGallery

UI_PKEY_LabelUI_PKEY_ItemImage、UI_PKEY_CategoryId

SplitButtonGallery

UI_PKEY_LabelUI_PKEY_ItemImage、UI_PKEY_CategoryId

UI_PKEY_SelectedItem 是项库的属性。

 

下表介绍了 Command 库 (UI_COMMANDTYPE_COMMANDCOLLECTION) 的有效项属性。

控制 属性
DropDownGallery UI_PKEY_CommandIdUI_PKEY_CommandType、UI_PKEY_CategoryId
InRibbonGallery UI_PKEY_CommandIdUI_PKEY_CommandType、UI_PKEY_CategoryId
SplitButtonGallery UI_PKEY_CommandIdUI_PKEY_CommandType、UI_PKEY_CategoryId

 

类别用于组织库中的项和命令。 类别通过 UI_PKEY_Categories 属性键定义并绑定到库,并使用特定于类别的 IUICollection 对象公开属性。

类别没有 CommandType,也不支持用户交互。 例如,类别不能成为项库中的 SelectedItem,并且它们不会绑定到 Command 库中的命令。 与其他库项属性一样,可以通过调用 IUISimplePropertySet::GetValue 来检索UI_PKEY_Label和UI_PKEY_CategoryId等类别属性。

重要

当为没有关联类别的项请求UI_PKEY_CategoryId时,IUISimplePropertySet::GetValue 应返回UI_COLLECTION_INVALIDINDEX

 

在标记中声明控件

库,就像所有功能区控件一样,必须在标记中声明。 库在标记中标识为项库或命令库,并声明各种演示文稿详细信息。 与其他控件不同,库仅要求在标记中声明基控件或集合容器。 实际集合在运行时填充。 在标记中声明库时, Type 属性用于指定库是否为 Command 库的项库。

此处讨论的每个控件都有许多可用的可选布局属性。 这些属性为框架提供开发人员首选项,这些首选项直接影响控件在功能区中的填充和显示方式。 标记中适用的首选项与通过 大小定义和缩放策略自定义功能区中讨论的显示和布局模板和行为相关。

如果特定控件不允许直接在标记中使用布局首选项,或者未指定布局首选项,则框架会根据可用屏幕空间量定义特定于控件的显示约定。

以下示例演示如何将一组库合并到功能区中。

命令声明

应使用 CommandName 属性声明命令,该属性用于将控件或控件集与 Command 相关联。

还可以在此处指定用于在编译标记时将 Command 绑定到命令处理程序的 CommandId 属性。 如果未提供 ID,则框架会生成一个 ID。

<!-- ComboBox -->
<Command Name="cmdComboBoxGroup"
         Symbol="cmdComboBoxGroup"
         Comment="ComboBox Group"
         LabelTitle="ComboBox"/>
<Command Name="cmdComboBox"
         Symbol="cmdComboBox"
         Comment="ComboBox"
         LabelTitle="ComboBox"/>

<!-- DropDownGallery -->
<Command Name="cmdDropDownGalleryGroup"
         Symbol="cmdDropDownGalleryGroup"
         Comment="DropDownGallery Group"
         LabelTitle="DropDownGallery"/>
<Command Name="cmdDropDownGallery"
         Symbol="cmdDropDownGallery"
         Comment="DropDownGallery"
         LabelTitle="DropDownGallery"/>

<!-- InRibbonGallery -->
<Command Name="cmdInRibbonGalleryGroup"
         Symbol="cmdInRibbonGalleryGroup"
         Comment="InRibbonGallery Group"
         LabelTitle="InRibbonGallery"/>
<Command Name="cmdInRibbonGallery"
         Symbol="cmdInRibbonGallery"
         Comment="InRibbonGallery"
         LabelTitle="InRibbonGallery"

<!-- SplitButtonGallery -->
<Command Name="cmdSplitButtonGalleryGroup"
         Symbol="cmdSplitButtonGalleryGroup"
         Comment="SplitButtonGallery Group"
         LabelTitle="SplitButtonGallery"/>
<Command Name="cmdSplitButtonGallery"
         Symbol="cmdSplitButtonGallery"
         Comment="SplitButtonGallery"
         LabelTitle="SplitButtonGallery"

控件声明

本部分包含的示例演示各种库类型所需的基本控件标记。 它们演示如何声明库控件,并通过 CommandName 属性将它们与命令相关联。

以下示例演示 DropDownGallery 的控件声明,其中 Type 属性用于指定这是一个 Command 库。

<!-- DropDownGallery -->
<Group CommandName="cmdDropDownGalleryGroup">
  <DropDownGallery CommandName="cmdDropDownGallery"
                   TextPosition="Hide"
                   Type="Commands"
                   ItemHeight="32"
                   ItemWidth="32">
    <DropDownGallery.MenuLayout>
      <FlowMenuLayout Rows="2"
                      Columns="3"
                      Gripper="None"/>
    </DropDownGallery.MenuLayout>
    <DropDownGallery.MenuGroups>
      <MenuGroup>
        <Button CommandName="cmdButton1"></Button>
        <Button CommandName="cmdButton2"></Button>
       </MenuGroup>
       <MenuGroup>
        <Button CommandName="cmdButton3"></Button>
      </MenuGroup>
    </DropDownGallery.MenuGroups>
  </DropDownGallery>
</Group>

以下示例演示 SplitButtonGallery 的控件声明。

<!-- SplitButtonGallery -->
<Group CommandName="cmdSplitButtonGalleryGroup">
  <SplitButtonGallery CommandName="cmdSplitButtonGallery">
    <SplitButtonGallery.MenuLayout>
      <FlowMenuLayout Rows="2"
                      Columns="3"
                      Gripper="None"/>
    </SplitButtonGallery.MenuLayout>
    <SplitButtonGallery.MenuGroups>
      <MenuGroup>
        <Button CommandName="cmdButton1"></Button>
        <Button CommandName="cmdButton2"></Button>
      </MenuGroup>
      <MenuGroup>
        <Button CommandName="cmdButton3"></Button>
      </MenuGroup>
    </SplitButtonGallery.MenuGroups>
  </SplitButtonGallery>
</Group>

以下示例演示 InRibbonGallery 的控件声明。

注意

由于 InRibbonGallery 旨在在不激活下拉菜单的情况下在功能区中显示其项集合的子集,因此它提供了许多可选属性,用于控制功能区初始化时的大小和项布局。 这些属性对 InRibbonGallery 是唯一的,在其他动态控件中不可用。

 

<!-- InRibbonGallery -->
<Group CommandName="cmdInRibbonGalleryGroup" SizeDefinition="OneInRibbonGallery">
  <InRibbonGallery CommandName="cmdInRibbonGallery"
                   MaxColumns="10"
                   MaxColumnsMedium="5"
                   MinColumnsLarge="5"
                   MinColumnsMedium="3"
                   Type="Items">
    <InRibbonGallery.MenuLayout>
      <VerticalMenuLayout Rows="2"
                          Gripper="Vertical"/>
    </InRibbonGallery.MenuLayout>
    <InRibbonGallery.MenuGroups>
      <MenuGroup>
        <Button CommandName="cmdButton1"></Button>
        <Button CommandName="cmdButton2"></Button>
      </MenuGroup>
      <MenuGroup>
        <Button CommandName="cmdButton3"></Button>
      </MenuGroup>
    </InRibbonGallery.MenuGroups>            
  </InRibbonGallery>
</Group>

以下示例演示 ComboBox 的控件声明。

<!-- ComboBox -->
<Group CommandName="cmdComboBoxGroup">
  <ComboBox CommandName="cmdComboBox">              
  </ComboBox>
</Group>

创建命令处理程序

对于每个命令,功能区框架都需要主机应用程序中相应的命令处理程序。 命令处理程序由功能区主机应用程序实现,派生自 IUICommandHandler 接口。

注意

多个命令可以绑定到单个命令处理程序。

 

命令处理程序有两个用途:

以下示例演示库命令处理程序。

/*
 * GALLERY COMMAND HANDLER IMPLEMENTATION
 */
class CGalleryCommandHandler
      : public CComObjectRootEx<CComMultiThreadModel>
      , public IUICommandHandler
{
public:
  BEGIN_COM_MAP(CGalleryCommandHandler)
    COM_INTERFACE_ENTRY(IUICommandHandler)
  END_COM_MAP()

  // Gallery command handler's Execute method
  STDMETHODIMP Execute(UINT nCmdID,
                       UI_EXECUTIONVERB verb, 
                       const PROPERTYKEY* key,
                       const PROPVARIANT* ppropvarValue,
                       IUISimplePropertySet* pCommandExecutionProperties)
  {
    HRESULT hr = S_OK;
        
    // Switch on manner of execution (Execute/Preview/CancelPreview)
    switch (verb)
    {
      case UI_EXECUTIONVERB_EXECUTE:
        if(nCmdID == cmdTextSizeGallery || 
           nCmdID == cmdTextSizeGallery2 || 
           nCmdID == cmdTextSizeGallery3)
        {
          if (pCommandExecutionProperties != NULL)
          {
            CItemProperties *pItem = 
              static_cast<CItemProperties *>(pCommandExecutionProperties);
            g_prevSelection = g_index = pItem->GetIndex();
            UpdateGallerySelectedItems();
            ::InvalidateRect(g_hWindowFrame, NULL, TRUE);
          }
          else
          {
            g_prevSelection = g_index = 0;
            UpdateGallerySelectedItems();
            ::InvalidateRect(g_hWindowFrame, NULL, TRUE);
          }
        }           
        break;
      case UI_EXECUTIONVERB_PREVIEW:
        CItemProperties *pItem = 
          static_cast<CItemProperties *>(pCommandExecutionProperties);
        g_index = pItem->GetIndex();
        ::InvalidateRect(g_hWindowFrame, NULL, TRUE);
        break;
      case UI_EXECUTIONVERB_CANCELPREVIEW:
        g_index = g_prevSelection;
        ::InvalidateRect(g_hWindowFrame, NULL, TRUE);
        break;
    }   
    return hr;
  }

  // Gallery command handler's UpdateProperty method
  STDMETHODIMP UpdateProperty(UINT nCmdID,
                              REFPROPERTYKEY key,
                              const PROPVARIANT* ppropvarCurrentValue,
                              PROPVARIANT* ppropvarNewValue)
  {
    UNREFERENCED_PARAMETER(ppropvarCurrentValue);

    HRESULT hr = E_NOTIMPL;         

    if (key == UI_PKEY_ItemsSource) // Gallery items requested
    {
      if (nCmdID == cmdTextSizeGallery || 
          nCmdID == cmdTextSizeGallery2 || 
          nCmdID == cmdTextSizeGallery3)
      {
        CComQIPtr<IUICollection> spCollection(ppropvarCurrentValue->punkVal);

        int count = _countof(g_labels);

        for (int i = 0; i < count; i++)
        {
          CComObject<CItemProperties> * pItem;
          CComObject<CItemProperties>::CreateInstance(&pItem);
                    
          pItem->AddRef();
          pItem->Initialize(i);

          spCollection->Add(pItem);
        }
        return S_OK;
      }
      if (nCmdID == cmdCommandGallery1)
      {
        CComQIPtr<IUICollection> spCollection(ppropvarCurrentValue->punkVal);

        int count = 12;
        int commands[] = {cmdButton1, 
                          cmdButton2, 
                          cmdBoolean1, 
                          cmdBoolean2, 
                          cmdButton1, 
                          cmdButton2, 
                          cmdBoolean1, 
                          cmdBoolean2, 
                          cmdButton1, 
                          cmdButton2, 
                          cmdBoolean1, 
                          cmdBoolean2};

        for (int i = 0; i < count; i++)
        {
          CComObject<CItemProperties> * pItem;
          CComObject<CItemProperties>::CreateInstance(&pItem);
                    
          pItem->AddRef();
          pItem->InitializeAsCommand(commands[i]);

          spCollection->Add(pItem);
        }
        return S_OK;
      }
    }        
    else if (key == UI_PKEY_SelectedItem) // Selected item requested
    {           
      hr = UIInitPropertyFromUInt32(UI_PKEY_SelectedItem, g_index, ppropvarNewValue);           
    }
    return hr;
  }
};

绑定命令处理程序

定义命令处理程序后,命令必须绑定到处理程序。

以下示例演示如何将库命令绑定到特定的命令处理程序。 在这种情况下, ComboBox 和库控件都绑定到各自的命令处理程序。

// Called for each Command in markup. 
// Application will return a Command handler for each Command.
STDMETHOD(OnCreateUICommand)(UINT32 nCmdID,
                             UI_COMMANDTYPE typeID,
                             IUICommandHandler** ppCommandHandler) 
{   
  // CommandType for ComboBox and galleries
  if (typeID == UI_COMMANDTYPE_COLLECTION || typeID == UI_COMMANDTYPE_COMMANDCOLLECTION) 
  {
    switch (nCmdID)
    {
      case cmdComboBox:
        CComObject<CComboBoxCommandHandler> * pComboBoxCommandHandler;
        CComObject<CComboBoxCommandHandler>::CreateInstance(&pComboBoxCommandHandler);
        return pComboBoxCommandHandler->QueryInterface(IID_PPV_ARGS(ppCommandHandler));
      default:
        CComObject<CGalleryCommandHandler> * pGalleryCommandHandler;
        CComObject<CGalleryCommandHandler>::CreateInstance(&pGalleryCommandHandler);
        return pGalleryCommandHandler->QueryInterface(IID_PPV_ARGS(ppCommandHandler));
    }
    return E_NOTIMPL; // Command is not implemented, so do not pass a handler back.
  }
}

初始化集合

以下示例演示项库和 Command 库的 IUISimplePropertySet 的自定义实现。

此示例中的 CItemProperties 类派生自 IUISimplePropertySet。 除了必需的方法 IUISimplePropertySet::GetValue 外,CItemProperties 类还实现一组用于初始化和索引跟踪的帮助程序函数。

//
//  PURPOSE:    Implementation of IUISimplePropertySet.
//
//  COMMENTS:
//              Three gallery-specific helper functions included. 
//

class CItemProperties
  : public CComObjectRootEx<CComMultiThreadModel>
  , public IUISimplePropertySet
{
  public:

  // COM map for QueryInterface of IUISimplePropertySet.
  BEGIN_COM_MAP(CItemProperties)
    COM_INTERFACE_ENTRY(IUISimplePropertySet)
  END_COM_MAP()

  // Required method that enables property key values to be 
  // retrieved on gallery collection items.
  STDMETHOD(GetValue)(REFPROPERTYKEY key, PROPVARIANT *ppropvar)
  {
    HRESULT hr;

    // No category is associated with this item.
    if (key == UI_PKEY_CategoryId)
    {
      return UIInitiPropertyFromUInt32(UI_PKEY_CategoryId, 
                                       UI_COLLECTION_INVALIDINDEX, 
                                       pprovar);
    }

    // A Command gallery.
    // _isCommandGallery is set on initialization.
    if (_isCommandGallery)
    {           
      if(key == UI_PKEY_CommandId && _isCommandGallery)
      {
        // Return a pointer to the CommandId of the item.
        return InitPropVariantFromUInt32(_cmdID, ppropvar);
      }         
    }
    // An item gallery.
    else
    {
      if (key == UI_PKEY_Label)
      {
        // Return a pointer to the item label string.
        return UIInitPropertyFromString(UI_PKEY_Label, ppropvar);
      }
      else if(key == UI_PKEY_ItemImage)
      {
        // Return a pointer to the item image.
        return UIInitPropertyFromImage(UI_PKEY_ItemImage, ppropvar);
      }         
    }
    return E_NOTIMPL;
  }

  // Initialize an item in an item gallery collection at the specified index.
  void Initialize(int index)
  {
    _index = index;
    _cmdID = 0;
    _isCommandGallery = false;
  }

  // Initialize a Command in a Command gallery.
  void InitializeAsCommand(__in UINT cmdID)
  {
    _index = 0;
    _cmdID = cmdID;
    _isCommandGallery = true;
  }

  // Gets the index of the selected item in an item gallery.
  int GetIndex()
  {
    return _index;
  }

private:
  int _index;
  int _cmdID;
  bool _isCommandGallery;   
};

处理收集事件

以下示例演示 IUICollectionChangedEvent 实现。

class CQATChangedEvent
  : public CComObjectRootEx<CComSingleThreadModel>
  , public IUICollectionChangedEvent
{
  public:

  HRESULT FinalConstruct()
  {
    _pSite = NULL;
    return S_OK;
  }

  void Initialize(__in CQATSite* pSite)
  {
    if (pSite != NULL)
    {
      _pSite = pSite;
    }
  }

  void Uninitialize()
  {
    _pSite = NULL;
  }

  BEGIN_COM_MAP(CQATChangedEvent)
    COM_INTERFACE_ENTRY(IUICollectionChangedEvent)
  END_COM_MAP()

  // IUICollectionChangedEvent interface
  STDMETHOD(OnChanged)(UI_COLLECTIONCHANGE action, 
                       UINT32 oldIndex, 
                       IUnknown *pOldItem, 
                       UINT32 newIndex, 
                       IUnknown *pNewItem)
  {
    if (_pSite)
    {
      _pSite->OnCollectionChanged(action, oldIndex, pOldItem, newIndex, pNewItem);
    }
    return S_OK;
  }

  protected:
  virtual ~CQATChangedEvent(){}

  private:
  CQATSite* _pSite; // Weak ref to avoid circular refcounts
};

HRESULT CQATHandler::EnsureCollectionEventListener(__in IUICollection* pUICollection)
{
  // Check if listener already exists.
  if (_spQATChangedEvent)
  {
    return S_OK;
  }

  HRESULT hr = E_FAIL;

  // Create an IUICollectionChangedEvent listener.
  hr = CreateInstanceWithRefCountOne(&_spQATChangedEvent);
    
  if (SUCCEEDED(hr))
  {
    CComPtr<IUnknown> spUnknown;
    _spQATChangedEvent->QueryInterface(IID_PPV_ARGS(&spUnknown));

    // Create a connection between the collection connection point and the sink.
    AtlAdvise(pUICollection, spUnknown, __uuidof(IUICollectionChangedEvent), &_dwCookie);
    _spQATChangedEvent->Initialize(this);
  }
  return hr;
}

HRESULT CQATHandler::OnCollectionChanged(
             UI_COLLECTIONCHANGE action, 
          UINT32 oldIndex, 
             IUnknown *pOldItem, 
          UINT32 newIndex, 
          IUnknown *pNewItem)
{
    UNREFERENCED_PARAMETER(oldIndex);
    UNREFERENCED_PARAMETER(newIndex);

    switch (action)
    {
      case UI_COLLECTIONCHANGE_INSERT:
      {
        CComQIPtr<IUISimplePropertySet> spProperties(pNewItem);
                
        PROPVARIANT var;
        if (SUCCEEDED(spProperties->GetValue(UI_PKEY_CommandId, &var)))
        {
          UINT tcid;
          if (SUCCEEDED(UIPropertyToUInt32(UI_PKEY_CommandId, var, &tcid)))
          {
            FireETWEvent(tcid, L"Added to QAT");
            PropVariantClear(&var);
          }
        }
      }
      break;
      case UI_COLLECTIONCHANGE_REMOVE:
      {
        CComQIPtr<IUISimplePropertySet> spProperties(pOldItem);
                
        PROPVARIANT var;
        if (SUCCEEDED(spProperties->GetValue(UI_PKEY_CommandId, &var)))
        {
          UINT tcid;
          if (SUCCEEDED(UIPropertyToUInt32(UI_PKEY_CommandId, var, &tcid)))
          {
            FireETWEvent(tcid, L"Removed from QAT");
            PropVariantClear(&var);
          }
        }
      }
      break;
    default:
  }
  return S_OK;
}

集合属性

创建功能区应用程序

了解命令和控件

功能区用户体验指南

功能区设计过程

库示例