6

首先,我要感谢所有花时间查看此线程并尝试提供帮助的人。

我在 Internet 上搜索过,但找不到选择标签文本与字符串变量文本相同的树视图节点的示例。

在 MSDN 上,我找到了消息 TVM_GETISEARCHSTRING,但我不知道它是否可以用来解决我的问题。即使可以,我仍然不知道如何使用它。

我有一个字符串变量,它保存数据库中的文本。当程序加载时,树视图应该有一个选择了相同文本的节点。

请帮助提供一些说明或代码片段,因为我什至不知道如何开始编码。

我在 MS Visual Studio Express 2008、Windows XP、C++ 中使用纯 WIN32 API 工作。

就这样,再次感谢所有试图提供帮助的人。非常非常感谢!

编辑:

这两个答案对我都有好处,但我不知道如何标记它们,似乎在这个网站上只能接受一个答案。

我不能忽视他们俩为帮助我所做的所有工作,所以我写这篇文章是为了报答乔纳森,至少正式声明他的解决方案对我来说也是可以接受的,只是蒂姆的解决方案更适合我的编码风格。我也会赞成这两个答案。

4

2 回答 2

6

不幸的是,没有记录按项目标签搜索树视图的方法。

TVM_GETISEARCHSTRING消息返回用户在树中键入的搜索字符串(增量搜索模式),但它不会触发搜索或让您提供自己的搜索字符串。

唯一的方法是手动迭代树节点并自己比较标签。下面是一个示例函数,请注意它是递归的,每个子级别将使用大约半 KB 的堆栈。

HTREEITEM TreeView_FindLabel(HWND hWnd, HTREEITEM hItemParent, LPCWSTR pszLabel)
{
    TVITEM tvi;
    wchar_t wchLabel[256];

    for (tvi.hItem = TreeView_GetChild(hWnd, hItemParent); tvi.hItem;
        tvi.hItem = TreeView_GetNextSibling(hWnd, tvi.hItem))
    {
        tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
        tvi.pszText = wchLabel;
        tvi.cchTextMax = _countof(wchLabel);
        if (TreeView_GetItem(hWnd, &tvi))
        {
            if (_wcsicmp(tvi.pszText, pszLabel) == 0)
                return tvi.hItem;
            if (tvi.cChildren)
            {
                HTREEITEM hChildSearch = TreeView_FindLabel(hWnd, tvi.hItem, pszLabel);
                if (hChildSearch) return hChildSearch;
            }
        }
    }
    return 0;
}

这不是搜索树的特别快速的方法。如果您需要进行大量搜索,最好使用 astd::map自己跟踪标签和树项。例如,

std::map<std::wstring, HTREEITEM> mapTreeItems;

// whenever you add an item
HTREEITEM hItem = ListView_InsertItem(...);
mapTreeItems[strLabel] = hItem;

然后,您可以使用地图按标签查找树项目。每当从树中删除项目时,您只需要记住更新地图并删除标签。

于 2013-07-21T20:25:27.190 回答
6

树视图控件不提供用于搜索标签的 API。您将不得不手动遍历这些项目并将它们与您的字符串进行比较。

如果树视图的深度超过一层,则必须决定如何遍历项目(深度优先或广度优先)。如果有多个具有相同标签的项目,这些策略可能会返回不同的项目。

一个实现可能看起来像这样:

// Helper function to return the label of a treeview item
std::wstring GetItemText( HWND hwndTV, HTREEITEM htItem )
{
    static const size_t maxLen = 128;
    WCHAR buffer[ maxLen + 1 ];

    TVITEMW tvi = { 0 };
    tvi.hItem = htItem;         // Treeview item to query
    tvi.mask = TVIF_TEXT;       // Request text only
    tvi.cchTextMax = maxLen;
    tvi.pszText = &buffer[ 0 ];
    if ( TreeView_GetItem( hwndTV, &tvi ) )
    {
        return std::wstring( tvi.pszText );
    }
    else
    {
        return std::wstring();
    }
}

这是实际遍历发生的地方。递归调用该函数,直到无法搜索更多项目或找到匹配项。此实现使用区分大小写的比较 ( wstring::operator==( const wstring& ))。如果您需要不同的谓词,则必须根据需要修改实现。

HTREEITEM FindItemDepthFirstImpl( HWND hwndTV, HTREEITEM htStart, const std::wstring& itemText )
{
    HTREEITEM htItemMatch = NULL;

    HTREEITEM htItemCurrent = htStart;
    // Iterate over items until there are no more items or we found a match
    while ( htItemCurrent != NULL && htItemMatch == NULL )
    {
        if ( GetItemText( hwndTV, htItemCurrent ) == itemText )
        {
            htItemMatch = htItemCurrent;
        }
        else
        {
            // Traverse into child items
            htItemMatch = FindItemDepthFirstImpl( hwndTV, TreeView_GetChild( hwndTV, htItemCurrent ), itemText );
        }
        htItemCurrent = TreeView_GetNextSibling( hwndTV, htItemCurrent );
    }

    return htItemMatch;
}

以下函数包装递归并将根元素作为起点。这是您将在代码中调用的函数。HTREEITEM如果找到,它将返回一个,NULL否则。

HTREEITEM FindItem( HWND hwndTV, const std::wstring& itemText )
{
    HTREEITEM htiRoot = TreeView_GetRoot( hwndTV );
    return FindItemDepthFirstImpl( hwndTV, htiRoot, itemText );
}
于 2013-07-21T20:42:50.713 回答