Работа с коллекциями

Платформа ленты Windows предоставляет разработчикам надежную и согласованную модель для управления динамическим содержимым в различных элементах управления на основе коллекций. Благодаря адаптации и перенастройке пользовательского интерфейса ленты эти динамические элементы управления позволяют платформе реагировать на взаимодействие с пользователем как в ведущем приложении, так и в самой ленте, а также обеспечивают гибкость для обработки различных сред времени выполнения.

Введение

Эта способность платформы ленты динамически адаптироваться к условиям времени выполнения, требованиям приложений и вводу данных конечным пользователем подчеркивает широкие возможности пользовательского интерфейса платформы и предоставляет разработчикам гибкость для удовлетворения широкого спектра потребностей клиентов.

В этом руководстве основное внимание уделяется описанию элементов управления динамической коллекции, поддерживаемых платформой, объясните их различия, обсудите, где и когда их лучше всего использовать, а также продемонстрировать, как их можно включить в приложение ленты.

Коллекции

Коллекции — это функциональные и графически многофункциональные элементы управления со списком. Коллекция элементов коллекции может быть упорядочена по категориям, отображаться в гибких макетах на основе столбцов и строк, представлена изображениями и текстом и в зависимости от типа коллекции, поддерживать динамический просмотр.

Коллекции функционально отличаются от других динамических элементов управления ленты по следующим причинам:

  • Коллекции реализуют интерфейс IUICollection , который определяет различные методы для управления коллекциями элементов коллекции.
  • Коллекции можно обновлять во время выполнения на основе действий, происходящих непосредственно на ленте, например, когда пользователь добавляет команду на панель быстрого доступа (QAT).
  • Коллекции можно обновлять во время выполнения на основе действий, которые косвенно происходят из среды выполнения, например, когда драйвер принтера поддерживает только книжные макеты страниц.
  • Коллекции можно обновлять во время выполнения на основе действий, которые косвенно происходят в ведущем приложении, например при выборе пользователем элемента в документе.

Платформа ленты предоставляет два типа коллекций: коллекции элементов и коллекции команд.

Коллекции элементов

Коллекции элементов содержат коллекцию связанных элементов на основе индекса, в которой каждый элемент представлен изображением, строкой или и тем, и другим. Элемент управления привязан к одному обработчику command, который зависит от значения индекса, определяемого свойством UI_PKEY_SelectedItem .

Коллекции элементов поддерживают динамический предварительный просмотр, что означает отображение результата команды на основе перехода мыши или фокуса без фиксации или фактического вызова команды.

Важно!

Платформа не поддерживает размещение коллекций элементов в меню приложения.

 

Коллекции команд

Коллекции команд содержат коллекцию отдельных неиндексированных элементов. Каждый элемент представлен одним элементом управления, привязанным к обработчику команд с помощью идентификатора команды. Как и в случае с автономными элементами управления, каждый элемент в коллекции команд направляет входные события в связанный обработчик команд. Сама коллекция команд не прослушивает события.

Коллекции команд не поддерживают динамический предварительный просмотр.

На платформе ленты существует четыре элемента управления коллекции: DropDownGallery, SplitButtonGallery, InRibbonGallery и ComboBox. Все, кроме ComboBox , можно реализовать как коллекцию элементов или коллекцию команд.

DropDownGallery — это кнопка, которая отображает раскрывающийся список, содержащий коллекцию взаимоисключающих элементов или команд.

На следующем снимке экрана показан элемент управления Раскрывающийся список ленты в Microsoft Paint для Windows 7.

Снимок экрана: раскрывающийся элемент управления коллекции в Microsoft Paint для Windows 7.

SplitButtonGallery

SplitButtonGallery — это составной элемент управления, который предоставляет один элемент по умолчанию или command из своей коллекции на основной кнопке и отображает другие элементы или команды во взаимоисключающем раскрывающемся списке, который отображается при нажатии дополнительной кнопки.

На следующем снимке экрана показан элемент управления Коллекция кнопок с разделением ленты в Microsoft Paint для Windows 7.

Снимок экрана: элемент управления коллекции разделенных кнопок в Microsoft Paint для Windows 7.

InRibbonGallery

InRibbonGallery — это коллекция, в которой на ленте отображается коллекция связанных элементов или команд. Если в коллекции слишком много элементов, отображается стрелка развертывания, чтобы отобразить остальную часть коллекции в развернутой области.

На следующем снимке экрана показан элемент управления "Лента в коллекции ленты" в Microsoft Paint для Windows 7.

Снимок экрана: элемент управления коллекции на ленте Microsoft Paint.

ComboBox

ComboBox — это список с одним столбцом, содержащий коллекцию элементов со статическим элементом управления или элементом управления "Изменить" и стрелкой раскрывающегося списка. Часть списка элемента управления отображается, когда пользователь щелкает стрелку раскрывающегося списка.

На следующем снимке экрана показан элемент управления "Поле со списком" на ленте из Киностудия Windows Live.

Снимок экрана: элемент управления со списком на ленте Microsoft Paint.

Так как 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 . Приложения могут подписываться на уведомления от объекта IUICollection через прослушиватель событий IUICollectionChangedEvent::OnChanged .

Когда приложение заменяет коллекцию коллекции, предоставленную платформой, пользовательской коллекцией, приложение должно реализовать интерфейс IConnectionPointContainer . Если IConnectionPointContainer не реализован, приложение не может уведомить платформу об изменениях в пользовательской коллекции, требующих динамических обновлений в элементе управления коллекции.

В тех случаях, когда IConnectionPointContainer не реализован, элемент управления коллекции можно обновить только с помощью IUIFramework::InvalidateUICommand и IUICommandHandler::UpdateProperty или путем вызова IUIFramework::SetUICommandProperty.

IUISimplePropertySet

Приложения должны реализовывать IUISimplePropertySet для каждого элемента или команды в коллекции. Однако свойства, которые можно запросить с помощью IUISimplePropertySet::GetValue , различаются.

Элементы определяются и привязываются к коллекции с помощью ключа свойства UI_PKEY_ItemsSource и предоставляют свойства с помощью объекта IUICollection .

Допустимые свойства элементов в коллекциях элементов (UI_COMMANDTYPE_COLLECTION) описаны в следующей таблице.

Примечание

Некоторые свойства элементов, например UI_PKEY_Label, можно определить в разметке. Дополнительные сведения см. в справочной документации по ключам свойств .

 

Control

Свойства

ComboBox

UI_PKEY_Label, UI_PKEY_CategoryId

DropDownGallery

UI_PKEY_Label, UI_PKEY_ItemImage , UI_PKEY_CategoryId

InRibbonGallery

UI_PKEY_Label, UI_PKEY_ItemImage , UI_PKEY_CategoryId

SplitButtonGallery

UI_PKEY_Label, UI_PKEY_ItemImageUI_PKEY_CategoryId

UI_PKEY_SelectedItem является свойством коллекции элементов.

 

Допустимые свойства элементов для коллекций команд (UI_COMMANDTYPE_COMMANDCOLLECTION) описаны в следующей таблице.

Control Свойства
DropDownGallery UI_PKEY_CommandId, UI_PKEY_CommandType , UI_PKEY_CategoryId
InRibbonGallery UI_PKEY_CommandId, UI_PKEY_CommandType , UI_PKEY_CategoryId
SplitButtonGallery UI_PKEY_CommandId, UI_PKEY_CommandTypeUI_PKEY_CategoryId

 

Категории используются для упорядочения элементов и команд в коллекциях. Категории определяются и привязываются к коллекции с помощью ключа свойства UI_PKEY_Categories и предоставляют свойства с объектом IUICollection для конкретной категории.

Категории не имеют CommandType и не поддерживают взаимодействие с пользователем. Например, категории не могут стать SelectedItem в коллекции элементов и не привязаны к Command в коллекции команд. Как и другие свойства элементов коллекции, свойства категорий, такие как UI_PKEY_Label и UI_PKEY_CategoryId , можно получить, вызвав IUISimplePropertySet::GetValue.

Важно!

IUISimplePropertySet::GetValue должен возвращать UI_COLLECTION_INVALIDINDEX при запросе UI_PKEY_CategoryId для элемента без связанной категории.

 

Объявление элементов управления в разметке

Коллекции, как и все элементы управления ленты, должны быть объявлены в разметке. Коллекция определяется в разметке как коллекция элементов или коллекция команд, и объявляются различные сведения о презентации. В отличие от других элементов управления, коллекции требуют объявления в разметке только базового элемента управления или контейнера коллекции. Фактические коллекции заполняются во время выполнения. Если коллекция объявлена в разметке, атрибут Type используется для указания того, является ли коллекция коллекцией элементов коллекции Команд.

Существует ряд необязательных атрибутов макета, доступных для каждого из элементов управления, которые рассматриваются здесь. Эти атрибуты предоставляют платформе предпочтения разработчика, которые непосредственно влияют на то, как элемент управления заполняется и отображается на ленте. Параметры, применимые в разметке, связаны с шаблонами отображения и макета и поведением, рассмотренным в разделе Настройка ленты с помощью определений размера и политик масштабирования.

Если определенный элемент управления не разрешает параметры макета непосредственно в разметке или параметры макета не указаны, платформа определяет соглашения о отображении элементов управления на основе объема доступного пространства на экране.

В следующих примерах показано, как включить набор коллекций в ленту.

Объявления команд

Команды должны объявляться с атрибутом CommandName , который используется для связывания элемента управления или набора элементов управления с командой.

Здесь также можно указать атрибут CommandId , используемый для привязки Command к обработчику command при компиляции разметки. Если идентификатор не указан, он создается платформой.

<!-- 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"

Объявления элементов управления

В этом разделе приведены примеры, демонстрирующие базовую разметку элемента управления, необходимую для различных типов коллекции. В них показано, как объявить элементы управления коллекции и связать их с Command с помощью атрибута CommandName .

В следующем примере показано объявление элемента управления для DropDownGallery , где атрибут Type используется для указания того, что это коллекция команд.

<!-- 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 .

Примечание

Несколько команд можно привязать к одному обработчику команд.

 

Обработчик команд служит двум целям:

  • IUICommandHandler::UpdateProperty отвечает на запросы на обновление свойства. Значения свойств command, таких как UI_PKEY_Enabled или UI_PKEY_Label, задаются с помощью вызовов IUIFramework::SetUICommandProperty или IUIFramework::InvalidateUICommand.
  • IUICommandHandler::Execute отвечает на события выполнения. Этот метод поддерживает следующие три состояния выполнения, заданные параметром UI_EXECUTIONVERB .
    • Состояние Выполнения выполняет или фиксирует любые команды, к которым привязан обработчик.
    • Состояние предварительного просмотра выполняет предварительный просмотр всех команд, к которым привязан обработчик. По сути, при этом команды выполняются без фиксации результата.
    • Состояние CancelPreview отменяет все предварительные команды. Это необходимо для поддержки обхода меню или списка, а также последовательного предварительного просмотра и отмены результатов по мере необходимости.

В следующем примере показан обработчик команд коллекции.

/*
 * 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;
  }
};

Привязка обработчика команд

После определения обработчика команд команда должна быть привязана к обработчику.

В следующем примере показано, как привязать коллекцию Command к определенному обработчику команд. В этом случае элементы управления 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.
  }
}

Инициализация коллекции

В следующем примере демонстрируется пользовательская реализация 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;
}

Свойства коллекции

Создание приложения ленты

Основные сведения о командах и элементах управления

Руководство по взаимодействию с пользователем ленты

Процесс проектирования ленты

Пример коллекции