Click here to Skip to main content
Click here to Skip to main content

Using the CFindReplaceDialog class

, 9 Dec 2002
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

Tibor Blazko
Software Developer (Senior)
Slovakia Slovakia
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberskyformat99@gmail.com21-Aug-13 19:45 
GeneralMy vote of 5 PinmemberMichael Haephrati1-Feb-12 10:29 
GeneralEditBox and Button using SendMessage Pinmembermailtochandra2000@yahoo.com2-Feb-07 1:42 
GeneralFindNext PinsussAnonymous10-Jun-04 4:39 
QuestionHow do I close the dialog? PinsussAnonymous30-Apr-04 1:06 
AnswerRe: How do I close the dialog? PinmemberTibor Blazko30-Apr-04 1:29 
QuestionHow to SendMessage? PinmemberAdrian Gibbons21-Apr-04 9:46 
AnswerRe: How to SendMessage? PinmemberTibor Blazko21-Apr-04 19:02 
GeneralRe: How to SendMessage? PinmemberAdrian Gibbons21-Apr-04 23:54 
GeneralFind in a dialog box project based on CRcihEditCtrl PinmemberFilomela5-Apr-04 21:08 
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberTibor Blazko5-Apr-04 21:33 
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberFilomela7-Apr-04 20:46 
GeneralRe: Find in a dialog box project based on CRcihEditCtrl PinmemberTibor Blazko21-Apr-04 18:58 
GeneralFind Dialog Box Pinmembersasan566-Sep-03 2:56 
GeneralRe: Find Dialog Box PinmemberLynxDK9-Sep-03 19:10 
GeneralRe: Find Dialog Box Pinmembersasan5611-Sep-03 3:24 
GeneralRe: Find Dialog Box PinmemberLynxDK14-Sep-03 22:01 
GeneralRe: Find Dialog Box PinmemberTibor Blazko11-Sep-03 5:40 
GeneralFocus Problems while switching to another control PinmemberSiegmund2-Jan-03 18:56 
GeneralRe: Focus Problems while switching to another control PinmemberTibor Blazko2-Jan-03 19:07 
QuestionFunction in the article NOT defined! Can use it? Pinmemberzhdleonid5-Dec-02 20:27 
AnswerRe: Function in the article NOT defined! Can use it? PinmemberTibor Blazko5-Dec-02 20:43 
Generalmessages PinsussAnonymous2-Aug-02 10:24 
GeneralRe: messages PinmemberTibor Blazko4-Aug-02 19:20 
Generalcustomize PinmemberTibor Blazko6-Sep-01 3:20 

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.

| Advertise | Privacy | Mobile
Web04 | 2.8.141029.1 | Last Updated 10 Dec 2002
Article Copyright 2000 by Tibor Blazko
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid