Click here to Skip to main content
Licence 
First Posted 22 Aug 2000
Views 137,804
Bookmarked 35 times

Using the CFindReplaceDialog class

By | 9 Dec 2002 | Article
How to use the CFindReplaceDialog dialog for text searching

Introduction

This article is for MFC beginners and gives a general overview of using CFindReplaceDialog.

The problem we want to solve is how to allow the user to find any text string in their data. This article discusses the user interface, and not the actual searching algorithm.

One example of a good way of doing a long search is shown in RegEdit. It has a good UI for searches that may take a long time (cancel-search dialog).

Our example is designed for smaller data search. Note that when the time taken is noticable but not too long you can always use CWaitCursor.

We will demonstrate a way comparable with Wordpad search. First what will happen is a dialog will be displayed where user will enter a search string. It is not necessary to create our own Wordpad-like dialog in the resource editor because windows supports a standard one.

The API function FindText() creates a non-modal dialog which can communicate with its parent. (If you interested you can find details in help index under "common dialog boxes [Win32]/code examples/Finding text".)

We show how use CFindReplaceDialog which is an MFC class that calls FindText internally. Our FindDialog parent will be a tree-control (CMyTreeCtrl), but in general it can be any window type. This example ends at the FindWhatYouNeed() function call level.

In your handler (eg. in CMyTreeCtrl::OnFind()) you will create an instance of CFindReplaceDialog

ASSERT(m_pFindDialog == NULL);
m_pFindDialog = new CFindReplaceDialog();
m_pFindDialog->Create(TRUE, "Initial Text", NULL, FR_DOWN, this);

where m_pFindDialog is in my case class member of parent window CMyTreeCtrl

CFindReplaceDialog *m_pFindDialog;

This way it will display the find dialog in its default style. If you want you can change the dwFlags parameter of Create(). eg. FR_DOWN | FR_WHOLEWORD will check whole-word when the dialog starts. See "FINDREPLACE" help for all details.

When the dialog is opened the user can edit the text and change the checks (if you allow them to be shown). It's time to react to his actions.

CFindReplaceDialog sends a message to its parent. The ID of this message you can found by

UINT message = ::RegisterWindowMessage(FINDMSGSTRING);

Because ClassWizard knows nothing about this message we must add it manually. To MyTreeCtrl.h (parent window) add OnFindDialogMessage handler

protected:
     //{{AFX_MSG(C__LayerTreeCtrl)
     afx_msg void OnSomethingGeneratedByClassWizard();
     //}}AFX_MSG
     afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);

     DECLARE_MESSAGE_MAP()

To MyTreeCtrl.cpp (parent window)

const UINT CMyTreeCtrl::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);

BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
     //{{AFX_MSG_MAP(CMyTreeCtrl)
     ON_WM_SOMETHINGGENERATEDBYCLASSWIZARD()
     //}}AFX_MSG_MAP
     ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
END_MESSAGE_MAP()

where m_pFindDialogMessage is defined in MyTreeCtrl.h as

static const UINT m_pFindDialogMessage;

Of course can be simple (not class member) variable:

static UINT FindDialogMessage = ::RegisterWindowMessage(FINDMSGSTRING);

BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
     //{{AFX_MSG_MAP(CMyTreeCtrl)
     ON_WM_SOMETHING()
     //}}AFX_MSG_MAP
     ON_REGISTERED_MESSAGE(FindDialogMessage, OnFindDialogMessage)
END_MESSAGE_MAP()

As you can see I wrote my own entry out of AFX_MSG_MAP so ClassWizard knows nothing about/has no problems with.

Now we have to write message handler:

LRESULT CMyTreeCtrl::OnFindDialogMessage(WPARAM wParam, LPARAM lParam)
{
    ASSERT(m_pFindDialog != NULL);

    // If the FR_DIALOGTERM flag is set,
    // invalidate the handle identifying the dialog box.
    if (m_pFindDialog->IsTerminating())
    {
        m_pFindDialog = NULL;
        return 0;
    }

    // If the FR_FINDNEXT flag is set,
    // call the application-defined search routine
    // to search for the requested string.
    if(m_pFindDialog->FindNext())
    {
        //read data from dialog
        CString FindName = m_pFindDialog->GetFindString();
        bool bMatchCase = m_pFindDialog->MatchCase() == TRUE;
        bool bMatchWholeWord = m_pFindDialog->MatchWholeWord() == TRUE;
        bool bSearchDown = m_pFindDialog->SearchDown() == TRUE;

        //with given name do search
        FindWhatYouNeed(FindName, bMatchCase, bMatchWholeWord, bSearchDown);
    }

    return 0;
}

It is more user friendly when you remember data read from the dialog and use it to modify parameters of next Create() call. You can add new class members to parent window class. If you will not use m_pFindDialog as a class member you can get it dynamicaly by

CFindReplaceDialog *pFindDialog = CFindReplaceDialog::GetNotifier(lparam);

When your tree control does not have the TVS_SHOWSELALWAYS flag set after find dialog opening user will not see actual line selection. Because of this we will set this flag before dialog's creation (CMyTreeCtrl::OnFind()):

if(GetWindowLong(GetSafeHwnd(), GWL_STYLE) & TVS_SHOWSELALWAYS)
{
	m_bRemoveShowSelAlwaysAtFindDialogExit = false;
}
else
{
	SetWindowLong(GetSafeHwnd(), GWL_STYLE,
	              GetWindowLong(GetSafeHwnd(), GWL_STYLE) | TVS_SHOWSELALWAYS);
	m_bRemoveShowSelAlwaysAtFindDialogExit = true; //return back at dialog exit
}

and in dialog termination (CMyTreeCtrl::OnFindDialogMessage()):

if (m_bRemoveShowSelAlwaysAtFindDialogExit)
	SetWindowLong(GetSafeHwnd(), GWL_STYLE,
	              GetWindowLong(GetSafeHwnd(), 
	                            GWL_STYLE) & (~((LONG)TVS_SHOWSELALWAYS)));

If you want to enable it so that when you open the find dialog another window will have focus, then the find dialog or our parent/tree must switch TVS_SHOWSELALWAYS "more dynamicaly".

Logic requires that we do not open more find dialogs. Because of this, replace

ASSERT(m_pFindDialog == NULL);

by better code (if/return) or follow next.

We will close the find dialog when it loses focus. This is not very user friendly (eg. new mail notification will close the dialog) but it's good for a short example.

To do this I derived own CMyFindReplaceDialog class from CFindReplaceDialog. (Do not forget call the new CMyFindReplaceDialog in CMyTreeCtrl::OnFind()). Then I mapped the WM_ACTIVATE message in MyFindReplaceDialog.h

afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);

in MyFindReplaceDialog.cpp add the message map entry:

ON_WM_ACTIVATE()

and finally:

void CmyLayerDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
{
    CFindReplaceDialog::OnActivate(nState, pWndOther, bMinimized );

    if(nState == WA_INACTIVE && IsTerminating() == false)
    {
        //close dialog
        PostMessage(WM_COMMAND, IDABORT); //DestroyWindow();
    }
}

The trick is not to call DestroyWindow(). The CFindReplaceDialog uses the protected class member m_szFindWhat in afxdlgs.h

TCHAR m_szFindWhat[128];

So do not exceed this size (or your text will be truncated).

In "FINDREPLACE" help you can find "The buffer should be at least 80 characters long." If you want (I do not know why) to replace this buffer by your own do not forget this point.

CFindReplaceDialog *pFindDialog = new CFindReplaceDialog();
pFindDialog->m_fr.lpstrFindWhat = MyBuffer;
pFindDialog->m_fr.wFindWhatLen =  _countof(MyBuffer);
pFindDialog->Create(TRUE, "Initial Text", NULL, FR_DOWN, this);

Do not forget MyBuffer must exist during whole find-dialog life so it can't be a local (stack) variable. (Easiest way is to derive CMyFindReplaceDialog from CFindReplaceDialog and create it as a class member.)

It is sometimes useful to make the find dialog "modal" (ie. make it impossible to switch to the parent until the dialog ends). It's easy: If we open it from a control in a dialog than add to the find-dialog Create() call:

GetParent()->EnableWindow(false);

and on IsTerminating()

GetParent()->EnableWindow(true);

(For safety you should take care of the case when Create() will be not successful.) If you have tree-control in a property page you must disable the whole sheet, not the actual page only:

GetParent()->GetParent()->EnableWindow(false);

and on IsTerminating()

GetParent()->GetParent()->EnableWindow(true);

This logic, with more if-s around, you can see in MFC CDialog::DoModal() in dlgcore.cpp or CPropertySheet::DoModal() in dlgprop.cpp.

Another, more complex example of using CFindReplaceDialog can be found in the MFC CRichEditView class. See the CRichEditView::OnEditFindReplace() function in the viewrich.cpp file.

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

About the Author

Tibor Blazko

Software Developer (Senior)

Slovakia Slovakia

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 PinmemberMichael Haephrati10:29 1 Feb '12  
GeneralEditBox and Button using SendMessage Pinmembermailtochandra2000@yahoo.com1:42 2 Feb '07  
GeneralFindNext PinsussAnonymous4:39 10 Jun '04  
QuestionHow do I close the dialog? PinsussAnonymous1:06 30 Apr '04  
AnswerRe: How do I close the dialog? PinmemberTibor Blazko1:29 30 Apr '04  
QuestionHow to SendMessage? PinmemberAdrian Gibbons9:46 21 Apr '04  
AnswerRe: How to SendMessage? PinmemberTibor Blazko19:02 21 Apr '04  
GeneralRe: How to SendMessage? PinmemberAdrian Gibbons23:54 21 Apr '04  
GeneralFind in a dialog box project based on CRcihEditCtrl PinmemberFilomela21:08 5 Apr '04  
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberTibor Blazko21:33 5 Apr '04  
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberFilomela20:46 7 Apr '04  
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberTibor Blazko18:58 21 Apr '04  
GeneralFind Dialog Box Pinmembersasan562:56 6 Sep '03  
GeneralRe: Find Dialog Box PinmemberLynxDK19:10 9 Sep '03  
GeneralRe: Find Dialog Box Pinmembersasan563:24 11 Sep '03  
GeneralRe: Find Dialog Box PinmemberLynxDK22:01 14 Sep '03  
GeneralRe: Find Dialog Box PinmemberTibor Blazko5:40 11 Sep '03  
GeneralFocus Problems while switching to another control PinmemberSiegmund18:56 2 Jan '03  
GeneralRe: Focus Problems while switching to another control PinmemberTibor Blazko19:07 2 Jan '03  
QuestionFunction in the article NOT defined! Can use it? Pinmemberzhdleonid20:27 5 Dec '02  
AnswerRe: Function in the article NOT defined! Can use it? PinmemberTibor Blazko20:43 5 Dec '02  
Generalmessages PinsussAnonymous10:24 2 Aug '02  
GeneralRe: messages PinmemberTibor Blazko19:20 4 Aug '02  
Generalcustomize PinmemberTibor Blazko3:20 6 Sep '01  
GeneralDestroyWindow() according to MSDN PinmemberPatrik Sjogren21:16 26 Feb '01  

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

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 10 Dec 2002
Article Copyright 2000 by Tibor Blazko
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid