65.9K
CodeProject 正在变化。 阅读更多。
Home

为 CHtmlViews 添加自定义搜索功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (14投票s)

2000年11月18日

viewsIcon

210585

downloadIcon

1880

可以用来创建一个类似 Visual C++ 的 CHtmlView 搜索组合框... 更新后,您可以高亮显示所有匹配的单词!

引言

是否曾经想创建一个像 Visual C++ 中那样全局工作的搜索组合框? 那么,这就是如何在您的 CHtmlView 中实现它的方法。 顺便说一句,这段代码可以用于任何事情,而不仅仅是一个搜索组合框!

搜索!

搜索功能基于由您的 CHtmlView 派生类拥有的三个函数

  • CMyHtmlView::FindText(CString searchText, long lFlags /* =2 */, 
    BOOL bNNF /* =FALSE (for internal use)*/)

    在文档中搜索指定的单词/文本,并高亮显示下一个匹配项。

    参数

    • searchText - 要搜索的文本(废话!)
    • lFlags - 传递给 IHTMLTxtRange::fidText 的搜索标志。 可以是 2(仅搜索整个单词)和 4(匹配大小写)的任意组合
    • bNNF - 内部使用。 如果你想知道它做什么,看看代码,我不记得了。
  • CMyHtmlView::FindText2(CString searchText, long lFlags /* =2 */, 
    CString matchStyle, CString searchID)

    在文档中搜索指定的单词/文本,并高亮显示所有找到的出现次数。

    参数

    • searchText - 要搜索的文本(再说一遍,废话!)
    • lFlags - 传递给 IHTMLTxtRange::findText 的搜索标志。 可以是 2(仅搜索整个单词)和 4(匹配大小写)的任意组合
    • matchStyle - 允许您自定义在文档中高亮显示找到的文本的方式。 这是分配给 <span> HTML 标记的 CSS 样式。 默认:深蓝色背景上的白色文本。
    • searchID - 如果您想一次高亮显示多个搜索结果,请使用此选项(您可以为每个不同的 searchID 使用不同的 matchStyle。 通常,您会将此设置为默认值。
  • CMyHtmlView::ClearSearchResults(CString searchID /* ="CHtmlView_Search" */)

    取消高亮显示来自 FindText2 的搜索结果。

    参数

    • searchID - 您在 FindText2 中使用的 searchID。 如果您使用了默认值,请在此处执行相同的操作。

以下是函数的代码

void CMyHtmlView::FindText(CString searchText, 
   long lFlags /* =2 */, BOOL bNNF /* =FALSE  (for internal use)*/)
{
    static CString sLastSearch;
    static BSTR lastBookmark = NULL;

    if(sLastSearch != searchText)
        lastBookmark = NULL;
    sLastSearch = searchText;    

    IHTMLDocument2 *lpHtmlDocument = NULL;
    LPDISPATCH lpDispatch = NULL;
    lpDispatch = GetHtmlDocument();
    ASSERT(lpDispatch);

    lpDispatch->QueryInterface(IID_IHTMLDocument2, 
                                (void**)&lpHtmlDocument);
    ASSERT(lpHtmlDocument);

    lpDispatch->Release();

    IHTMLElement *lpBodyElm;
    IHTMLBodyElement *lpBody;
    IHTMLTxtRange *lpTxtRange;

    lpHtmlDocument->get_body(&lpBodyElm);
    ASSERT(lpBodyElm);
    lpHtmlDocument->Release();
    lpBodyElm->QueryInterface(IID_IHTMLBodyElement,(void**)&lpBody);
    ASSERT(lpBody);
    lpBodyElm->Release();
    lpBody->createTextRange(&lpTxtRange);
    ASSERT(lpTxtRange);
    lpBody->Release();

    CComBSTR search(searchText.GetLength()+1,(LPCTSTR)searchText);
    bool bFound,bTest;
    long t;

    if(lastBookmark!=NULL)
    {
        lpTxtRange->moveToBookmark(lastBookmark,
                               (VARIANT_BOOL*)&bTest);
        if(!bTest)
        {
            lastBookmark=NULL;
            lpTxtRange->moveStart((BSTR)CComBSTR("Textedit"),1,&t);
            lpTxtRange->moveEnd((BSTR)CComBSTR("Textedit"),1,&t);
        } else
        {
            lpTxtRange->moveStart((BSTR)CComBSTR("Character"),1,&t);
            lpTxtRange->moveEnd((BSTR)CComBSTR("Textedit"),1,&t);
        }
    } else
    {
        lpTxtRange->moveStart((BSTR)CComBSTR("Textedit"),0,&t);
        lpTxtRange->moveEnd((BSTR)CComBSTR("Textedit"),1,&t);
    }
    lpTxtRange->findText((BSTR)search,0,
                    lFlags,(VARIANT_BOOL*)&bFound);

    if(!bFound)
    {
        if(lastBookmark==NULL && !bNNF)
        {
            CString message;
            message.Format("Cannot find the string: '%s'",searchText);
            AfxMessageBox(message);
        } else if(lastBookmark!=NULL)
        {
            lastBookmark = NULL;
            FindText(searchText,lFlags,TRUE);
        }
    } else
    {
        if(lpTxtRange->getBookmark(&lastBookmark)!=S_OK)
            lastBookmark=NULL;
        lpTxtRange->select();
        lpTxtRange->scrollIntoView(TRUE);
    }

    lpTxtRange->Release();
}
void CMyHtmlView::FindText2(CString searchText, 
   long lFlags /* =2 */, CString matchStyle, CString searchID)
{
    ClearSearchResults(searchID);

    IHTMLDocument2 *lpHtmlDocument = NULL;
    LPDISPATCH lpDispatch = NULL;
    lpDispatch = GetHtmlDocument();
    ASSERT(lpDispatch);

    lpDispatch->QueryInterface(IID_IHTMLDocument2, 
                               (void**)&lpHtmlDocument);
    ASSERT(lpHtmlDocument);

    lpDispatch->Release();

    IHTMLElement *lpBodyElm;
    IHTMLBodyElement *lpBody;
    IHTMLTxtRange *lpTxtRange;

    lpHtmlDocument->get_body(&lpBodyElm);
    ASSERT(lpBodyElm);
    lpHtmlDocument->Release();
    lpBodyElm->QueryInterface(IID_IHTMLBodyElement,(void**)&lpBody);
    ASSERT(lpBody);
    lpBodyElm->Release();
    lpBody->createTextRange(&lpTxtRange);
    ASSERT(lpTxtRange);
    lpBody->Release();

    CComBSTR html;
    CComBSTR newhtml;
    CComBSTR search(searchText.GetLength()+1,(LPCTSTR)searchText);

    long t;
    bool bFound;
    while(lpTxtRange->findText(search,0,lFlags,
                        (VARIANT_BOOL*)&bFound),bFound)
    {
        newhtml.Empty();
        lpTxtRange->get_htmlText(&html);
        newhtml.Append("<span id='");
        newhtml.Append((LPCTSTR)searchID);
        newhtml.Append("' style='");
        newhtml.Append((LPCTSTR)matchStyle);
        newhtml.Append("'>");
        if(searchText==" ")
            // doesn't work very well, but prevents (some) shit
            newhtml.Append(" ;"); 
        else 
            newhtml.AppendBSTR(html);
        newhtml.Append("</span>");
        lpTxtRange->pasteHTML(newhtml);
                
        lpTxtRange->moveStart((BSTR)CComBSTR("Character"),1,&t);
        lpTxtRange->moveEnd((BSTR)CComBSTR("Textedit"),1,&t);
    }

    lpTxtRange->Release();
}
void CMyHtmlView::ClearSearchResults(CString searchID)
{
    CComBSTR testid(searchID.GetLength()+1,searchID);
    CComBSTR testtag(5,"SPAN");
    IHTMLDocument2 *lpHtmlDocument = NULL;
    LPDISPATCH lpDispatch = NULL;
    lpDispatch = GetHtmlDocument();
    ASSERT(lpDispatch);

    lpDispatch->QueryInterface(IID_IHTMLDocument2, 
                                (void**)&lpHtmlDocument);
    ASSERT(lpHtmlDocument);
    lpDispatch->Release();

    IHTMLElementCollection *lpAllElements;
    lpHtmlDocument->get_all(&lpAllElements);
    ASSERT(lpAllElements);
    lpHtmlDocument->Release();

    IUnknown *lpUnk;
    IEnumVARIANT *lpNewEnum;
    if (SUCCEEDED(lpAllElements->get__newEnum(&lpUnk)) && lpUnk != NULL)
    {
        lpUnk->QueryInterface(IID_IEnumVARIANT,(void**)&lpNewEnum);
        ASSERT(lpNewEnum);
        VARIANT varElement;
        IHTMLElement *lpElement;

        while (lpNewEnum->Next(1, &varElement, NULL) == S_OK)
        {
            _ASSERTE(varElement.vt == VT_DISPATCH);
            varElement.pdispVal->QueryInterface
                   (IID_IHTMLElement,(void**)&lpElement);
            ASSERT(lpElement);
            if (lpElement)
            {
                CComBSTR id;
                CComBSTR tag;
                lpElement->get_id(&id);
                lpElement->get_tagName(&tag);
                if((id==testid)&&(tag==testtag))
                {
                    BSTR innerText;
                    lpElement->get_innerHTML(&innerText);
                    lpElement->put_outerHTML(innerText);
                }
            }
            VariantClear(&varElement);
        }
    }
}

定义

void FindText(CString searchText, long Flags = 2, 
    BOOL bNNF = FALSE /*for internal use*/);
void FindText2(CString searchText, long Flags = 2, 
    CString matchStyle = "color: white; background-color: darkblue", 
    CString searchID = "CHtmlView_Search");
void ClearSearchResults(CString searchID = "CHtmlView_Search");

以及要包含到您的 CHtmlView 派生类中的其他文件

#include <mshtml.h>
#include <atlbase.h>

用法

  • 如果您想要一次高亮显示一个匹配单词的搜索,请使用 FindText,并将要搜索的文本作为第一个参数,并将其他参数保留为默认值。
  • 如果您想要一个与上述相同但也可以在单词中搜索匹配文本的搜索,请使用与上述相同的函数,但将第二个参数设置为 2。(对于标准组合框搜索,使用此选项或上面的选项)
  • 如果您想要一个扫描整个文档并高亮显示所有找到的出现次数的搜索,请调用 FindText2,并将要搜索的文本作为第一个参数,并将第二个参数如上所述使用。

就是这样!

顺便说一句,如果您想使用这些函数的“高级”功能,您应该至少了解一点 CSS。

组合框在哪里?

有关如何实现组合框的信息,请查看演示项目的源代码。如果您对如何执行此操作有疑问,请在此处发布。

已知bug

当您在 FindText2 中搜索 " "(空格)时,该函数会将所有空格替换为不间断空格(否则它们会简单地消失)。 这会导致页面显示像 sh*t 一样,但这仍然比空格消失好。 :)

更新

在此最新更新中,我添加了两个更多函数,允许您进行同时选择所有结果的搜索。 以下是演示应用程序的屏幕截图

单项选择搜索

全选搜索。

致谢

COM 代码的灵感来自 Nic's Javascript Page 上的一些脚本。

历史

  • 2000 年 11 月 18 日:初始版本

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.