Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / MFC
Article

Using the CFindReplaceDialog class

Rate me:
Please Sign up or sign in to vote.
4.37/5 (15 votes)
9 Dec 2002 178.2K   1.7K   37   29
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


Written By
Software Developer (Senior)
Slovakia Slovakia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
skyformat99@gmail.com21-Aug-13 19:45
skyformat99@gmail.com21-Aug-13 19:45 
GeneralMy vote of 5 Pin
Michael Haephrati1-Feb-12 10:29
professionalMichael Haephrati1-Feb-12 10:29 
GeneralEditBox and Button using SendMessage Pin
mailtochandra2000@yahoo.com2-Feb-07 1:42
mailtochandra2000@yahoo.com2-Feb-07 1:42 
GeneralFindNext Pin
Anonymous10-Jun-04 4:39
Anonymous10-Jun-04 4:39 
QuestionHow do I close the dialog? Pin
Anonymous30-Apr-04 1:06
Anonymous30-Apr-04 1:06 
AnswerRe: How do I close the dialog? Pin
Tibor Blazko30-Apr-04 1:29
Tibor Blazko30-Apr-04 1:29 
QuestionHow to SendMessage? Pin
Adrian Gibbons21-Apr-04 9:46
Adrian Gibbons21-Apr-04 9:46 
AnswerRe: How to SendMessage? Pin
Tibor Blazko21-Apr-04 19:02
Tibor Blazko21-Apr-04 19:02 
GeneralRe: How to SendMessage? Pin
Adrian Gibbons21-Apr-04 23:54
Adrian Gibbons21-Apr-04 23:54 
GeneralFind in a dialog box project based on CRcihEditCtrl Pin
Filomela5-Apr-04 21:08
Filomela5-Apr-04 21:08 
GeneralRe: Find in a dialog box project based on CRcihEditCtrl Pin
Tibor Blazko5-Apr-04 21:33
Tibor Blazko5-Apr-04 21:33 
GeneralRe: Find in a dialog box project based on CRcihEditCtrl Pin
Filomela7-Apr-04 20:46
Filomela7-Apr-04 20:46 
I agree, but I began my project using a dialog box and it is quite complex and I can't redo it using Doc/View architecture as you suggested.
GeneralRe: Find in a dialog box project based on CRcihEditCtrl Pin
Tibor Blazko21-Apr-04 18:58
Tibor Blazko21-Apr-04 18:58 
GeneralFind Dialog Box Pin
sasan566-Sep-03 2:56
sasan566-Sep-03 2:56 
GeneralRe: Find Dialog Box Pin
LynxDK9-Sep-03 19:10
LynxDK9-Sep-03 19:10 
GeneralRe: Find Dialog Box Pin
sasan5611-Sep-03 3:24
sasan5611-Sep-03 3:24 
GeneralRe: Find Dialog Box Pin
LynxDK14-Sep-03 22:01
LynxDK14-Sep-03 22:01 
GeneralRe: Find Dialog Box Pin
Tibor Blazko11-Sep-03 5:40
Tibor Blazko11-Sep-03 5:40 
GeneralFocus Problems while switching to another control Pin
Siegmund Frenzel2-Jan-03 18:56
Siegmund Frenzel2-Jan-03 18:56 
GeneralRe: Focus Problems while switching to another control Pin
Tibor Blazko2-Jan-03 19:07
Tibor Blazko2-Jan-03 19:07 
QuestionFunction in the article NOT defined! Can use it? Pin
zhdleonid5-Dec-02 20:27
zhdleonid5-Dec-02 20:27 
AnswerRe: Function in the article NOT defined! Can use it? Pin
5-Dec-02 20:43
suss5-Dec-02 20:43 
Generalmessages Pin
Jared Bratsky2-Aug-02 10:24
Jared Bratsky2-Aug-02 10:24 
GeneralRe: messages Pin
Tibor Blazko4-Aug-02 19:20
Tibor Blazko4-Aug-02 19:20 
Generalcustomize Pin
Tibor Blazko6-Sep-01 3:20
Tibor Blazko6-Sep-01 3:20 

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.