Share via


Working with Virtualized Items

This topic describes how to use functionality provided by the ItemContainer and VirtualizedItem control patterns to find and retrieve information about virtualized items.

Controls that contain a large number of child items can use virtualization to efficiently manage the items. With virtualization, the control maintains full information in memory for only a subset of items at any given time. Typically, the subset includes only those items that are currently visible to the user. Full information about the remaining virtualized items is kept in storage and is loaded into memory, or realized, as the control needs it, for example, as new items become visible to the user.

Controls that use virtualization represent a challenge because only realized items are fully available as Microsoft UI Automation elements in the UI Automation tree. Virtualized items do not exist in the tree, so information about them is not available to clients. To retrieve information about virtualized items, clients need a way to force UI Automation to pass the request to realize the items to the control. After the items have been realized, UI Automation can create UI Automation elements for them. UI Automation includes two control patterns to enable clients to work with virtualized items: ItemContainer and VirtualizedItem.

Any control that can contain virtualized items must support the ItemContainer control pattern. Further, any item that can be virtualized must support the VirtualizedItem control pattern. The functionality that is exposed by the ItemContainer and VirtualizedItem control patterns is accessible to clients through the IUIAutomationItemContainerPattern and IUIAutomationVirtualizedItemPattern interfaces.

Clients can use the IUIAutomationItemContainerPattern::FindItemByProperty method to search for child items in the container based on the value of a particular property. The method can also retrieve the first item in the container or the item that follows the specified item. If a matching child item is found, the FindItemByProperty retrieves an IUIAutomationElement interface for the item. However, if the child item is virtualized, the IUIAutomationElement interface is a placeholder. The UIA_E_ELEMENTNOTAVAILABLE error occurs when the client attempts to use the IUIAutomationElement interface to retrieve property values or call methods that are not yet available. Which properties or methods are available through a placeholder depends on the control implementation. The only requirement for a placeholder is to support the IUIAutomationVirtualizedItemPattern interface.

The UIA_E_ELEMENTNOTAVAILABLE error is an indication to the client that an item may be virtualized. The client should respond by retrieving the IUIAutomationVirtualizedItemPattern interface for the item, and then realizing the item by calling the IUIAutomationVirtualizedItemPattern::Realize method. If this succeeds, the IUIAutomationElement interface is fully functional with all appropriate properties available.

Depending on the control implementation, calling Realize may cause the control to scroll the item into view. However, a client should not rely on the item scrolling into view or made visible. To ensure that the item is visible, the client can use the IUIAutomationScrollItemPattern::ScrollIntoView method.

The following example searches a container for an item that has the specified name and retrieves the IUIAutomationElement interface for the item. The example looks in the UI Automation subtree first. If the item is not there, the example uses the container IUIAutomationItemContainerPattern interface to find the item, and then uses the item IUIAutomationVirtualizedItemPattern interface to realize the item.

HRESULT GetContainerItem (BSTR bstrItemName, IUIAutomationElement *pContainerElement, 
                          IUIAutomationElement **ppItemElement)                                  
{
    HRESULT hr;
    VARIANT varNameStr;
    IUIAutomationCondition *pNamePropertyCond = NULL;
    IUIAutomationElement *pFoundElement = NULL;
    IUIAutomationItemContainerPattern *pItemContainer = NULL;

    // Make sure the pointers are valid.
    if (ppItemElement == NULL || pContainerElement == NULL || bstrItemName == NULL)
        return E_INVALIDARG;
    
    *ppItemElement = NULL;

    // Try to find the item in the UI Automation tree. This attempt will fail if the
    // item is virtualized. Note: g_pAutomation is a global pointer to the IUIAutomation interface.
    varNameStr.vt = VT_BSTR;
    varNameStr.bstrVal = SysAllocString(bstrItemName);
    hr = g_pAutomation->CreatePropertyCondition(UIA_NamePropertyId, varNameStr, &pNamePropertyCond);
    if (pNamePropertyCond == NULL)
        goto cleanup;

    hr = pContainerElement->FindFirst(TreeScope_Subtree, pNamePropertyCond, &pFoundElement);
    if (pFoundElement == NULL) { 

        // The item is not in the UI Automation tree, so it may be virtualized. Try
        // using the ItemContainer control pattern to find the item.
        hr = pContainerElement->GetCurrentPatternAs(UIA_ItemContainerPatternId, 
                                                    __uuidof(IUIAutomationItemContainerPattern), 
                                                    (void**)&pItemContainer);
        if (pItemContainer == NULL)
            goto cleanup;

        hr = pItemContainer->FindItemByProperty(NULL, UIA_NamePropertyId, varNameStr, &pFoundElement);
        if (pFoundElement == NULL) // container has no item with the specified name
            goto cleanup;
    }

    // Attempt to get the name property. The attempt will fail with 
    // UIA_E_ELEMENTNOTAVAILABLE if the item is virtualized. 
    BSTR bstrName; 
    hr = pFoundElement->get_CurrentName(&bstrName);
    if (hr == UIA_E_ELEMENTNOTAVAILABLE) 
    {
        // The item might be virtualized. Use the VirtualizedItem control pattern to 
        // realize the item. 
        IUIAutomationVirtualizedItemPattern *pVirtualizedItem;
        hr = pFoundElement->GetCurrentPatternAs(UIA_VirtualizedItemPatternId, 
                    __uuidof(IUIAutomationVirtualizedItemPattern), (void**)&pVirtualizedItem);
        if (pVirtualizedItem == NULL)
            goto cleanup;

        hr = pVirtualizedItem->Realize();
        pVirtualizedItem->Release();

        if (hr != S_OK)
            goto cleanup;

        // Try to get the name again. 
        hr = pFoundElement->get_CurrentName(&bstrName);
    }    

    if (SUCCEEDED(hr))
    {
        // Make sure the item is the one we're looking for.
        if (wcscmp(bstrName, bstrItemName) == 0)
        {
            *ppItemElement = pFoundElement;
            pFoundElement = NULL;
        }
    }


cleanup:
        VariantClear(&varNameStr);

        if (pNamePropertyCond != NULL) pNamePropertyCond->Release();
        if (pFoundElement != NULL) pFoundElement->Release();
        if (pItemContainer != NULL) pItemContainer->Release();

        return hr;
}