Click here to Skip to main content
12,353,226 members (71,244 online)
Click here to Skip to main content
Add your own
alternative version

Stats

166.1K views
1.6K downloads
39 bookmarked
Posted

Adding a custom search feature to CHtmlViews

, 22 Nov 2000
Rate this:
Please Sign up or sign in to vote.
Could be used to create a Visual C++ like search combo for CHtmlViews...<br>Update: Now you can highlight all matching words!

Introduction

Ever wanted to make a globally working search combo like in Visual C++? Well, here's how to make it work in your CHtmlView. BTW, this code can be used for anything, not just for a search combo!

Search!

The search feature is based on three functions owned by your CHtmlView-derived class:

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

    Searches through the document for the specified word/text and highlights the next match.

    Parameters:

    • searchText -Text to search for (duh!)
    • lFlags - Search flags passed to IHTMLTxtRange::fidText. Can be any combination of 2 (search for whole words only) and 4 (match case)
    • bNNF - Used internally. If you want to know what this does, look at the code, I don't remember.
  • CMyHtmlView::FindText2(CString searchText, long lFlags /* =2 */, CString matchStyle, CString searchID)

    Searches through the document for the specified word/text and highlights all found occurrences.

    Parameters:

    • searchText - Text to search for (again, duh!)
    • lFlags - Search flags passed to IHTMLTxtRange::findText. Can be any combination of 2 (search for whole words only) and 4 (match case)
    • matchStyle - Allows you to customize the way the found text is highlighted in the document. This is a CSS style assigned to the <span> HTML tag. Default: white text on dark blue background.
    • searchID - Use this if you want to highlight multiple search results at one time (you could use a different matchStyle for each different searchID. Normally you would leave this to default.
  • CMyHtmlView::ClearSearchResults(CString searchID /* ="CHtmlView_Search" */)

    Un-highlights the search results from FindText2.

    Parameters:

    • searchID - the searchID you used in FindText2. If you used the default value, do the same here.

And here comes the code for the functions:

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) sh*t
            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);
        }
    }
}

The definition:

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");

And the additional files to include to your CHtmlView derived class:

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

Usage

  • Is you want a search that highlights one matching word at a time, use FindText with the text you want to search for as the first parameter, and the other params left to default.
  • If you want a search that does the same as above, but that also searches for matching text within words, use the same function as above, but with the second parameter set to 2. (Use this one or the above one for standard combo-box search)
  • If you want a search that scans through the whole document and highlights all found occurrences, call FindText2 with the text you want to search for as the first parameter, and the second parameter used as described above.

That's it.

BTW, if you want to use the "advanced" features of these functions, you should know at least a little CSS.

Where's the combo?

For information on how to implement the combo-box, check the source code of the demo project. If you have questions on how to do this, post them here or mail them to me: devix@devix.cjb.net.

Known bugs

When you search for " " (space) in FindText2, the function replaces all spaces with non-breaking spaces (otherwise they simply disappear). This causes the page to display like sh*t, but it's still better than the spaces disappearing Smile | :)

Update

In this latest update, I have added two more functions that allows you to make a search that selects all off the result at the same time. Here are the screenshots of the demo-app:

Single selection search

Select-all search

Credits

COM code inspired by some script on Nic's Javscript Page.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    Share

    About the Author

    Marc Richarme
    Engineer Nokia
    Denmark Denmark
    My programming experience started a long time ago in
    QBasic (on a 25MHz 486).
    I'm now mainly using Java, C++, C, MFC, Perl and PHP, but have used quite a number of other languages as well for various projects.

    You may also be interested in...

    Comments and Discussions

     
    GeneralIHTMLElement *lpElement not released Pin
    chocm17-Oct-09 19:02
    memberchocm17-Oct-09 19:02 
    Questionforever loop in findText Pin
    obayan31-Mar-06 13:59
    memberobayan31-Mar-06 13:59 
    GeneralWindow flickering Pin
    Kronuz16-Jun-05 14:32
    memberKronuz16-Jun-05 14:32 
    Question2 bugs!? Pin
    beginner23qweqe16-May-05 11:20
    memberbeginner23qweqe16-May-05 11:20 
    Questionhow to do this in c# Pin
    dotnetdevotee24-Mar-05 1:39
    memberdotnetdevotee24-Mar-05 1:39 
    AnswerRe: how to do this in c# Pin
    Anonymous4-Aug-05 21:30
    sussAnonymous4-Aug-05 21:30 
    QuestionHow to add jscript in IHTMLDocument and to execute it? (GOOD) Pin
    a_jordanov9-Oct-04 1:16
    sussa_jordanov9-Oct-04 1:16 
    AnswerRe: How to add jscript in IHTMLDocument and to execute it? (GOOD) Pin
    alexiworld10-Oct-04 19:35
    memberalexiworld10-Oct-04 19:35 
    AnswerRe: How to add jscript in IHTMLDocument and to execute it? (GOOD) Pin
    Alexander Fedorov9-Apr-08 5:39
    memberAlexander Fedorov9-Apr-08 5:39 
    QuestionHow to add jscript in IHTMLDocument and to execute it? Pin
    a_jordanov9-Oct-04 1:11
    sussa_jordanov9-Oct-04 1:11 
    GeneralRegular Expression Pin
    sixcans27-Jun-04 16:26
    membersixcans27-Jun-04 16:26 
    Generalabove code is not working properly Pin
    senmah31-May-04 23:04
    susssenmah31-May-04 23:04 
    GeneralRe: above code is not working properly Pin
    jotterst17-Dec-09 6:38
    memberjotterst17-Dec-09 6:38 
    Generalrelease version Pin
    includeh1013-Apr-04 6:49
    memberincludeh1013-Apr-04 6:49 
    GeneralRe: release version Pin
    bruno cortona8-Jun-04 20:53
    memberbruno cortona8-Jun-04 20:53 
    Generalquery Pin
    mailMonty3-Mar-04 2:41
    membermailMonty3-Mar-04 2:41 
    Generalsolution Pin
    mailMonty11-Mar-04 23:05
    membermailMonty11-Mar-04 23:05 
    QuestionHow to skip hidden text? Pin
    Edwin Evans2-Nov-03 8:35
    memberEdwin Evans2-Nov-03 8:35 
    GeneralThis with frames Pin
    inc995-Oct-03 5:05
    memberinc995-Oct-03 5:05 
    GeneralHighlight the search parameter with some extra words Pin
    pallavibondre1-Oct-03 7:54
    memberpallavibondre1-Oct-03 7:54 
    GeneralThe &quot;right&quot; way to do it. Pin
    Shomoiog30-Jul-03 3:37
    memberShomoiog30-Jul-03 3:37 
    GeneralRe: The &quot;right&quot; way to do it. Pin
    Neville Franks11-Aug-03 11:48
    memberNeville Franks11-Aug-03 11:48 
    GeneralRe: The "right" way to do it. Pin
    Yu Hailiang11-Apr-04 18:03
    memberYu Hailiang11-Apr-04 18:03 
    GeneralRe: The &quot;right&quot; way to do it. Pin
    James R. Twine23-Aug-04 10:05
    memberJames R. Twine23-Aug-04 10:05 
    GeneralRe: The "right" way to do it. Pin
    peterchen27-Jul-10 2:49
    memberpeterchen27-Jul-10 2:49 
    GeneralSearch BackWards Pin
    rashvi27-Jun-03 1:47
    memberrashvi27-Jun-03 1:47 
    GeneralRe: Search BackWards Pin
    Mr Delphi Developer19-May-09 10:19
    memberMr Delphi Developer19-May-09 10:19 
    Generalhighlight Pin
    Anonymous28-Oct-02 15:44
    sussAnonymous28-Oct-02 15:44 
    GeneralCHtmlView and Swing Pin
    Sky17-Jul-02 22:44
    memberSky17-Jul-02 22:44 
    GeneralThis With Frames Pin
    The_InCall12-Jul-02 8:57
    sussThe_InCall12-Jul-02 8:57 
    QuestionHow to Get this Working With Frames Pin
    andyk808-Jan-02 14:54
    memberandyk808-Jan-02 14:54 
    Generalcombine it with... Pin
    sedonya7-Nov-01 8:20
    membersedonya7-Nov-01 8:20 
    GeneralTo get document size of Html page Pin
    Bablu29-Jan-01 3:02
    memberBablu29-Jan-01 3:02 
    GeneralRe: To get document size of Html page Pin
    *Marc Richarme*29-Jan-01 7:25
    member*Marc Richarme*29-Jan-01 7:25 
    GeneralRe: To get document size of Html page Pin
    Marc Richarme29-Jan-01 12:55
    memberMarc Richarme29-Jan-01 12:55 
    GeneralRe: To get document size of Html page Pin
    Bablu30-Jan-01 3:40
    memberBablu30-Jan-01 3:40 
    GeneralRe: To get document size of Html page Pin
    Marc Richarme31-Jan-01 9:32
    memberMarc Richarme31-Jan-01 9:32 
    GeneralHow can I get the Source ? Pin
    Oren Farber11-Oct-01 5:51
    memberOren Farber11-Oct-01 5:51 
    GeneralRe: How can I get the Source ? Pin
    Marc Richarme11-Oct-01 7:21
    memberMarc Richarme11-Oct-01 7:21 
    GeneralProblem under W2K Pin
    Wayne Tanner24-Nov-00 10:28
    memberWayne Tanner24-Nov-00 10:28 
    GeneralRe: Problem under W2K Pin
    song8225-Jun-02 19:17
    membersong8225-Jun-02 19:17 
    GeneralRe: Problem under W2K Pin
    includeh1013-Apr-04 7:30
    memberincludeh1013-Apr-04 7:30 
    GeneralDon't Make Promises... Pin
    Brian Hart22-Nov-00 16:23
    memberBrian Hart22-Nov-00 16:23 
    GeneralRe: Don't Make Promises... Pin
    Marc Richarme23-Nov-00 5:48
    memberMarc Richarme23-Nov-00 5:48 
    GeneralGraphic Pin
    Brian Hart22-Nov-00 16:18
    memberBrian Hart22-Nov-00 16:18 
    GeneralNice example Pin
    Obliterator20-Nov-00 6:40
    memberObliterator20-Nov-00 6:40 
    GeneralRe: Nice example Pin
    Marc Richarme21-Nov-00 12:51
    memberMarc Richarme21-Nov-00 12:51 
    GeneralRe: Nice example Pin
    Mike Stevenson21-Nov-00 13:05
    memberMike Stevenson21-Nov-00 13:05 
    GeneralRe: Nice example Pin
    Marc Richarme21-Nov-00 13:21
    memberMarc Richarme21-Nov-00 13:21 
    GeneralRe: Nice example Pin
    David Wulff21-Nov-00 14:35
    memberDavid Wulff21-Nov-00 14:35 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    | Advertise | Privacy | Terms of Use | Mobile
    Web02 | 2.8.160621.1 | Last Updated 23 Nov 2000
    Article Copyright 2000 by Marc Richarme
    Everything else Copyright © CodeProject, 1999-2016
    Layout: fixed | fluid