Click here to Skip to main content
15,861,125 members
Articles / Desktop Programming / MFC
Article

File Searcher Edit Control with Browse Button

Rate me:
Please Sign up or sign in to vote.
4.90/5 (22 votes)
13 Dec 2002CPOL 415.3K   5K   112   96
A control that combines an edit control for entering a file or directory name, and a browse button that brings up a dialog for choosing a file or folder.

Sample Image - FileEditCtrl.jpg

Introduction

When I need a user to select a file or folder, I would have to create two controls, an edit control for entering the text, and a browse button that would bring up a dialog for actually choosing the file or folder. So I thought, why not combine the two controls into one. The CFileEditCtrl class is the result. The class definition and implementation are in the files FileEditCtrl.h and FileEditCtrl.cpp which are included in the demo project.

Testing

This code has not been tested for UNICODE builds. If any bugs are found and fixed, please drop me a note.

Acknowledgements

Michael Dunn for his article Introduction to COM - What It Is and How to Use It, for showing me how to handle shortcut (*.lnk and *.pif) files.

Me for my articles CPJAImage - Yet another image drawing class and Multiple Selection in a File Dialog.

Using an image on the button

If you want to place an image (HICON or HBITMAP), instead of the ellipses, on the button, you have to add the PJAImage.h and PJAImage.cpp files to your project. If you do not want to use an image, you do not have to add these files to your project, but you have to ensure that the PJAImage.h file is not included in the FileEditCtrl.h file, by commenting out the #include "PJAImage.h" (line 41) line in FileEditCtrl.h.

top

Features

  1. The control is derived from CEdit. All CEdit member functions are CFileEditCtrl member functions. It can be created with any of the ES_* edit control styles. It will respond just like any other edit control to any EM_* commands, and sends all EN_* notification messages.
  2. The ellipses button is drawn in the controls non-client area. It is a part of the control, not a separate button that has to be added onto the dialog template, or otherwise created or setup. It can be placed on either the left or the right side of the control.
  3. The control has its own DDX_FileEditCtrl and DDV_FileEditCtrl dialog data exchange functions. Setting up the control for use is very easy.
  4. Using the Create() member function, the control can be created in any window, not just dialogs or forms.
  5. The control can be used to browse for files or folders. And it has a member function that can be used to switch between the two. When the CFileDialog or SHBrowseForFolder dialogs are opened, they will be set to the directory currently entered in the control.
  6. The control accepts relative paths. Users can enter ..\..\anyfolder and the control will return the absolute path relative to the current working directory. Entering '.' will return the current working directory. If the FEC_MULTIPLE flag is set, the first file entered will be relative to the current directory, and all subsequent files will be relative to the first file, unless the absolute path is entered.
  7. The control accepts wildcards ( '*' and/or '?' ) in the file name. Just set the FEC_WILDCARDS flag.
  8. The control will automatically dereference shortcut (*.lnk) files. To disable this feature, just set the FEC_NODEREFERENCELINKS flag.
  9. The control accepts Drag and Drop files and folders. Just create it with the WS_EX_ACCEPTFILES extended Windows style. When a drop action occurs, the control sends a WM_NOTIFY message to its parent window.
  10. Member functions give access to the internal BROWSEINFO and OPENFILENAME structures, so if the default settings are not satisfactory, there is complete control over how the CFileDialog and SHBrowseForFolder dialogs are implemented.
  11. The control is resizable. The button keeps its proportionate size relative to the height of the control. As the control gets taller, the button gets bigger, and so does the dots on the button.
  12. The CFileDialog has the text on its default button changed from 'Open' to 'OK'.
  13. When the ellipses button is clicked, the control sends a WM_NOTIFY message to its parent window, giving the parent window a chance to stop the SHBrowseForFolder or CFileDialog from popping up. The <Ctrl><.> keystroke has the same action as a click on the button.
  14. The button can have an icon or bitmap displayed on it instead of the ellipses.
  15. The control can be drawn as a flat, hot to mouse control.
top

Using the Control

To use this control in your application, Add the FileEditCtrl.h and FileEditCtrl.cpp files to your project. Then it is recommended to add the text strings defined at the top of the FileEditCtrl.cpp file to your string table resource, using the FEC_IDS_* identifiers defined there.

// FEC_IDS_ALLFILES will be defined in resource.h if these strings
// are in a string table resource
#if !defined FEC_IDS_ALLFILES
    #define FEC_NORESOURCESTRINGS so this class knows how to handle these strings
    #define FEC_IDS_ALLFILES        _T("All Files (*.*)|*.*||")
    #define FEC_IDS_BUTTONTIP       _T("Browse")
    #define FEC_IDS_FILEDIALOGTITLE _T("Browse for File")
    #define FEC_IDS_SEPERATOR       _T(";")
    #define FEC_IDS_NOFILE          _T("Enter an existing file.")
    #define FEC_IDS_NOTEXIST        _T("%s does not exist.")
    #define FEC_IDS_NOTFILE         _T("%s is not a file.")
    #define FEC_IDS_NOTFOLDER       _T("%s is not a folder.")
    #define FEC_IDS_OKBUTTON        _T("OK")
#endif

To use the control on a dialog, using all the default settings, add an edit control to the dialog template, add a CString member variable to the dialog class, and in DoDataExchange() add the DDX_FileEditCtrl() and DDV_FileEditCtrl() functions. The default settings for the SHBrowseForFolder dialog has the BIF_RETURNONLYFSDIRS flag set. And the default settings for the CFileDialog dialog has the OFN_HIDEREADONLY, OFN_FILEMUSTEXIST and OFN_NOCHANGEDIR flags set, the file filter is set to the FEC_IDS_ALLFILES resource string, and the dialog caption is set to the FEC_IDS_FILEDIALOGTITLE resource string. If you want the control to be used for folders, set the flag in last parameter in DDX_FileEditCtrl() to FEC_FOLDER, and for files set it to FEC_FILEOPEN to use the File Open dialog, and FEC_FILESAVEAS to use the File Save As dialog.

If you want more control over the dialogs, such as choosing multiple files, then you have to add a CFileEditCtrl variable to your dialog class. In DoDataExchange(), add the second version of DDX_FileEditCtrl(). And then get a pointer to the OPENFILENAME or BROWSEINFO structures, using the GetOpenFileName() or GetBrowseInfo() functions, and set them accordingly. In the demo app, I have an edit control with an ID of IDC_EDIT1 and a CFileEditCtrl variable m_FileEditCtrl.

void CFileEditDemoDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CFileEditDemoDlg)
    ...
    //}}AFX_DATA_MAP

    DDX_FileEditCtrl(pDX, IDC_EDIT1, m_FileEditCtrl, FEC_FILEOPEN);
    ...
}

Because these functions are not supported by Class Wizard, they have to be placed outside the AFX_DATA_MAP code block. If you would like to add Class Wizard support, see MFC Technical Note 26 DDX and DDV routines and look under ClassWizard.

To retrieve the file names from the control, use the GetStartPosition() and GetNextPathName() member functions. In the demo app, I did this in the CDumpDialog::OnInitDialog() function, in order to fill the list box with the files entered by the user.

BOOL CDumpDialog::OnInitDialog() 
{
    CDialog::OnInitDialog();
    
    CFileEditDemoDlg *pDemo = (CFileEditDemoDlg *)GetParent();
    int width = 0;
    CString str;
    CDC *pDC = m_List.GetDC();
    int saved = pDC->SaveDC();
    pDC->SelectObject(GetFont());

    // call GetStartPosition() to get the position of the first file 
    // in the control
    POSITION pos = pDemo->m_fileeditctrl.GetStartPosition();
    while (pos)
    {
        // add the file paths to the list
        str = pDemo->m_fileeditctrl.GetNextPathName(pos);
        m_List.AddString(str);
        CSize size(0, 0);
        size = pDC->GetTextExtent(str);
        width = width > size.cx ? width : size.cx;
    }
    pDC->RestoreDC(saved);
    ReleaseDC(pDC);
    m_List.SetHorizontalExtent(width + 5);
    
    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}
top

FEC_NOTIFY structure

typedef struct tagFEC_NOTIFY {
    NMHDR hdr;
    CFileEditCtrl* pFEC; // pointer to control that sends this notification
    CString *pNewText; // pointer to the updated text
    tagFEC_NOTIFY (CFileEditCtrl *FEC, UINT code);
} FEC_NOTIFY;

// notification code sent before dialogs pop up
#define FEC_NM_PREBROWSE  1
// notification code sent after dialogs return
#define FEC_NM_POSTBROWSE 2
// notification code sent when user drops files onto control
#define FEC_NM_DROP       3

When a user clicks on the browse button, the control will send a WM_NOTIFY message with a FEC_NM_PREBROWSE notification code to its parent window before it brings up the SHBrowseForFolder or CFileDialog dialogs. The NMHDR* pointer will point to a FEC_NOTIFY structure. The pFEC member will point to the CFileEditCtrl that sent the message. You can use this pointer to modify the OPENFILENAME or BROWSEINFO structures. If you set the LRESULT parameter of the OnNotify handler to a nonzero value, you will stop the dialogs from executing.

If you want to use the browse button to start a dialog, or other functions, other than the CFileDialog or SHBrowseForFolder, simply call and handle your dialog in the FEC_NM_PREBROWSE handler. Be sure to set the LRESULT parameter to nonzero.

The control will send another WM_NOTIFY message with a FEC_NM_POSTBROWSE notification code after the browse dialog is closed but before the control's window text has been updated. The NMHDR* pointer will once again point to a FEC_NOTIFY structure. If the pNewText parameter is NULL, the dialog was cancelled. If it is not NULL, it will be a pointer to a CString containing the new text. If you set the LRESULT parameter of the OnNotify handler to a nonzero value, you will stop the control from updating.

Flow chart in case the above paragraphs were not clear enough

The control also sends a WM_NOTIFY message with the FEC_NM_DROP notification code after the user drags and drops a file or folder onto the control, but before the control updates its text. Once again, setting the LRESULT parameter of the OnNotify handler to a nonzero value will stop the control from updating.

top

User Functions

These are the public member functions that are used to control the CFileEditCtrl class.

CFileEditCtrl::CFileEditCtrl(BOOL bAutoDelete /* = FALSE */)

CFileEditCtrl constructor

Initializes all the internal variables.

Parameters:

  • bAutoDelete [in] - Auto delete flag

Returns:

Nothing.

Note:

If bAutoDelete is TRUE, this class object will be deleted when its window is destroyed (in CFileEditCtrl::PostNCDestroy). The only time this should be used is when the control is created dynamically in the DDX_FileEditCtrl(CDataExchange*,int,CString&,DWORD) function.

User Functions
CFileEditCtrl::~CFileEditCtrl()

CFileEditCtrl destructor

Cleans up internal data variables.

Parameters:

None

Returns:

Nothing.

User Functions
BOOL CFileEditCtrl::Create(DWORD dwFlags,
                           DWORD dwExStyle,
                           LPCTSTR lpszWindowName,
                           DWORD dwStyle,
                           const RECT& rect,
                           CWnd* pParentWnd,
                           UINT nID)

CFileEditCtrl::Create

Creates the CFileEditCtrl in any window.

Parameters:

dwFlags[in]- CFileEditCtrl flags (FEC_*)
dwExStyle[in] - Windows extended styles (WS_EX_*)
lpszWindowName[in]- The initial text in the control
dwStyle[in]- Windows and Edit control styles (WS_* and ES_*)
rect[in]- The position and size of the control
pParentWnd[in]- Pointer to the control's parent window
nID[in]- The control's ID

Returns:

TRUE if the control was successfully created. FALSE if not.

Note:

See SetFlags() below for descriptions of the flags used.

User Functions
DWORD CFileEditCtrl::GetFlags()

CFileEditCtrl::GetFlags

Retrieves the current flags.

Parameters:

None.

Returns:

The current flags.

Note:

See SetFlags() below for descriptions of the flags used. Because some flags can be changed via GetOpenFileName(), always use this function to get the current state of the flags.

User Functions
BOOL CFileEditCtrl::ModifyFlags(DWORD remove,
                                DWORD add)

CFileEditCtrl::ModifyFlags

Modifies the control flags.

Parameters:

remove[in]- The flags to remove
add[in]- The flags to add

Returns:

TRUE if the flags are successfully modified. FALSE if not.

Note:

See SetFlags() below for descriptions of the flags used.

User Functions
BOOL CFileEditCtrl::SetFlags(DWORD dwFlags)

CFileEditCtrl::SetFlags

  • Sets all the internal flags.
  • Initializes and sets up the OPENFILENAME or BROWSEINFO structures.
  • Forces the control to be redrawn if the button position changes.

Parameters:

  • dwFlags [in] - The flags to set.

Returns:

TRUE if successful. FALSE if not.

Note:

The following table describes the flags used in the dwFlags parameter.

dwFlags

Purpose

FEC_FILEOPENThe control is set to accept files. When the ellipses button is clicked, the control starts the Windows Common File Open dialog. This flag cannot be used with the FEC_FOLDER nor FEC_FILESAVEAS flags.
FEC_FILESAVEASThe control is set to accept files. When the ellipses button is clicked, the control starts the Windows Common File Save As dialog. This flag cannot be used with the FEC_FOLDER nor FEC_FILEOPEN flags.
FEC_MULTIPLEUsed with FEC_FILEOPEN. The control will accept multiple files. Has the same effect as the OFN_ALLOWMULTISELECT flag.
FEC_MULTIPLEFILESCombination of FEC_FILEOPEN and FEC_MULTIPLE
FEC_WILDCARDSUsed with FEC_FILEOPEN. The control will accept and resolve any wildcards ('*' and/or '?') in the file name. If the FEC_MULTIPLE flag is set, GetNextPathName() will return all the files that match. If FEC_MULTIPLE is not set, GetNextPathName() will return only the first match.
FEC_NODEREFERENCELINKSUsed with FEC_FILEOPEN. GetNextPathName() will return the path name of any shortcut (*.lnk) files entered. If this flag is not set, GetNextPathName() will return the path name of the file the shortcut points to. Has the same effect as the OFN_NODEREFERENCELINKS flag.
FEC_FOLDERThe control is set to accept folders. When the ellipses button is clicked, the control starts the SHBrowseForFolder dialog. This flag cannot be used with the FEC_FILEOPEN nor FEC_FILESAVEAS flag.
FEC_TRAILINGSLASHUsed with FEC_FOLDER. The folder path entered in the control will have a trailing slash.
FEC_BUTTONLEFTThe ellipses button will be placed on the left side of the control.
FEC_BUTTONTIPEnables the browse button tooltip. The tooltip text is set with the FEC_IDS_BUTTONTIP resource string.
FEC_CLIENTTIPEnables the client area tooltip. The tooltip text is set with the SetClientTipText() member function.
FEC_FLATSets the control to be drawn as a flat, hot to mouse control.
FEC_GRAYSCALEUsed with FEC_FLAT. The dots or image on the button will be drawn in grayscale when the control is flat. They are drawn in color when the control is active/hot.
User Functions
void CFileEditCtrl::SetClientTipText(CString text)

CFileEditCtrl::SetClientTipText

Sets the text to be used by the client area tooltip.

Parameters:

  • text [in] - The text to set

Returns:

Nothing.

User Functions
BROWSEINFO* CFileEditCtrl::GetBrowseInfo() const

CFileEditCtrl::GetBrowseInfo

Retrieve a pointer to the BROWSEINFO structure.

Parameters:

None.

Returns:

A pointer to the BROWSEINFO structure if the FEC_FOLDER flag was set. NULL otherwise.

Note:

If the default SHBrowseForFolder settings do not fit your use, use the pointer returned by this function to set up the SHBrowseForFolder using your own settings.

User Functions
OPENFILENAME* CFileEditCtrl::GetOpenFileName() const

CFileEditCtrl::GetOpenFileName

Retrieves a pointer to the OPENFILENAME structure.

Parameters:

None.

Returns:

A pointer to the OPENFILENAME structure if the FEC_FILE flag was set. NULL otherwise.

Note:

If the default CFileDialog settings do not fit your use, use the pointer returned by this function to set up the CFileDialog using your own settings.

User Functions
POSITION CFileEditCtrl::GetStartPosition()

CFileEditCtrl::GetStartPosition

Gets the starting position for the GetNextPathName() function.

Parameters:

None.

Returns:

A MFC POSITION structure that points to the first file in the control.

User Functions
CString CFileEditCtrl::GetNextPathName(POSITION &pos)

CFileEditCtrl::GetNextPathName

Returns the file name at the specified position in the buffer.

Parameters:

pos[in]- The position of the file name to retrieve
 [out]- The position of the next file name

Returns:

The complete path name of the file or folder.

Note:

The starting position is retrieved using the GetStartPosition() function. pos will be set to NULL when there are no more files.

User Functions
int CFileEditCtrl::SetButtonWidth(int width/* = -1*/)

CFileEditCtrl::SetButtonWidth

Sets the width, in pixels, of the browse button.

Parameters:

width [in] - The new width of the button

Returns:

The previous width of the button.

Note:

Setting the width to -1 causes the control to use the default width which is calculated to be 80% of its height.

User Functions
int CFileEditCtrl::GetButtonWidth()

CFileEditCtrl::GetButtonWidth

Retrieves the width, in pixels, of the browse button.

Parameters:

None.

Returns:

The width of the browse button.

User Functions
void CFileEditCtrl::SetButtonImage(HANDLE hImage,
                                   DWORD PJAIFlags,
                                   COLORREF Transparent/* = CLR_DEFAULT*/)

CFileEditCtrl::SetButtonImage

Sets the image to be used on the browse button.

Parameters:

hImage[in]- Handle of the image (can be a HBITMAP or HICON)
PJAIFlags[in]- The image flags (see the note below)
Transparent[in]- The transparent color on the bitmap. If CLR_DEFAULT is used, the color of the top left pixel is used as the transparent color.

Returns:

TRUE if a new image is set. FALSE if not.

Note:

The PJAImage.h file must be included (see lines 37 to 41 of FileEditCtrl.h) in order for this function to work.

Flags used in the PJAIFlags parameter:

PJAI_BITMAPThe given handle is an HBITMAP
PJAI_ICONThe given handle is an HICON
PJAI_AUTODELETEThe given handle will be deleted and the memory freed when a new image is set or the CFileEditCtrl object is deleted. If this flag is not set, the user of this class is responsible for freeing the image handle when it is no longer needed
PJAI_STRETCHEDDraws the image so that it fills the button.
PJAI_TRANSPARENTDraws the bitmap image transparently. This flag has no effect if the image is an icon, as icons are transparent by default.
User Functions

Data Exchange Functions

void DDV_FileEditCtrl (CDataExchange *pDX,
                       int nIDC)

DDV_FileEditCtrl

Verifies that the files or folders entered actually exist.

Parameters:

pDX[in]- Pointer to the CDataExchange object
nIDC[in]- The control's resource ID

Returns:

Nothing.

Note:

If the file or folder is invalid, pops up a message box informing the user, then sets the focus to the offending CFileEditCtrl.

User Functions
void DDX_FileEditCtrl (CDataExchange *pDX,
                       int nIDC,
                       CFileEditCtrl &rCFEC,
                       DWORD dwFlags)

DDX_FileEditCtrl

Subclasses the control with the given ID. Transfers the data between the window text and the supplied CFileEditCtrl.

Parameters:

pDX[in]- Pointer to the CDataExchange object
nIDC[in]- The control's resource ID
rCFEC[in]- The CFileEditCtrl object that is to control this window
dwFlags[in]- The flags used to setup this control

Returns:

Nothing.

Note:

See SetFlags() above for descriptions of the flags used.

User Functions top

The Button

In order to get the button to work, I first had to override the OnNcCalcSize() function. This is the function that is used to calculate the size and position of a Windows client area. In my override, I called CEdit::OnNcCalcSize() to get the default size and position of the client area, then I adjusted the size of the client area and calculated the size and position of the button. The CRect m_rcButtonRect member variable is used to store this information.

void CFileEditCtrl::OnNcCalcSize(BOOL bCalcValidRects, 
                            NCCALCSIZE_PARAMS FAR* lpncsp) 
{
    // calculate the size of the client area and the button.
    CEdit::OnNcCalcSize(bCalcValidRects, lpncsp);
    // set button area equal to client area of edit control
    m_rcButtonRect = lpncsp->rgrc[0];
    if (m_bButtonLeft) 
    // draw the button on the left side of the control
    {
        // shrink left side of client area
        // by 80% of the height of client area
        lpncsp->rgrc[0].left += 
          (lpncsp->rgrc[0].bottom - lpncsp->rgrc[0].top) * 8/10;
        // shrink the button so its right side
        // is at the left side of client area
        m_rcButtonRect.right = lpncsp->rgrc[0].left;
    }
    else // draw the button on the right side of the control
    {
        // shrink right side of client area
        // by 80% of the height of client area
        lpncsp->rgrc[0].right -= 
          (lpncsp->rgrc[0].bottom - lpncsp->rgrc[0].top) * 8/10;
        // shrink the button so its left side
        // is at the right side of client area
        m_rcButtonRect.left = lpncsp->rgrc[0].right;
    }
    if (bCalcValidRects)
        // convert button coordinates from
        // parent client coordinates to control 
        //window coordinates
        m_rcButtonRect.OffsetRect(-lpncsp->rgrc[1].left, 
                                   -lpncsp->rgrc[1].top);
    m_rcButtonRect.NormalizeRect();
}

The only time OnNcCalcSize() is called is when the windows frame has changed, so to force a call to OnNcCalcSize(), I had to call SetWindowPos() from SetFlags(), using the SWP_FRAMECHANGED flag.

BOOL FileEditCtrl::SetFlags(DWORD dwFlags)
{
    ...
        // Force a call to CFileEditCtrl::OnNcCalcSize() 
        // to calculate button size
        SetWindowPos(NULL,0,0,0,0,
                     SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|
                     SWP_NOZORDER|SWP_NOACTIVATE);
    ...
}

I then needed to paint the button on the control, so I wrote the DrawButton() function. Because the button is not in the client area of the window, DrawButton() has to be called from OnNcPaint().

void CFileEditCtrl::OnNcPaint() 
{
    CEdit::OnNcPaint();        // draws the border around the control
    DrawButton (m_nButtonState);    // draw the button in its current state
}

The next thing was to get mouse messages for the button. Because the button is not in the client area, it would not get client area mouse messages, and because it is not a border, it would not get non-client mouse messages. To solve this problem, I had to override OnNcHitTest() and get it to return HT_BORDER when the mouse cursor was over the button.

UINT CFileEditCtrl::OnNcHitTest(CPoint point) 
{
    ...
    UINT where = CEdit::OnNcHitTest(point);
    if (where == HTNOWHERE && ScreenPointInButtonRect(point))
        where = HTBORDER;
    return where;
}

Now a mouse press on the button would generate a WM_NCLBUTTONDOWN message, so I had to override OnNcLButtonDown(). In OnNcLButtonDown(), I would capture the mouse using SetCapture() and call DrawButton() to draw the button as down. Because once the mouse is captured, it no longer generates non-client mouse messages, I would have to respond to WM_LBUTTONUP and WM_MOUSEMOVE messages in order to keep track of the mouse. Because CEdit::OnLButtonDown also captures the mouse, I could not use GetCapture to see if the button had captured the mouse, so I added the BOOL m_bMouseCaptured variable to keep track of it.

void CFileEditCtrl::OnNcLButtonDown(UINT nHitTest, CPoint point) 
{
    CEdit::OnNcLButtonDown(nHitTest, point);
    ...
    if (ScreenPointInButtonRect(point))
    {
        SetCapture();
        m_bMouseCaptured = TRUE;
        SetFocus();
        DrawButton(BTN_DOWN);
    }
}

By overriding OnMouseMove(), I could keep track of the captured mouse, and draw the button as down if the mouse cursor was over the button, or as up if it was not.

void CFileEditCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
    CEdit::OnMouseMove(nFlags, point);
    ...
    if (m_bMouseCaptured)
    {
        ClientToScreen(point);
        if (ScreenPointInButtonRect(point))
        {
            if (m_nButtonState != BTN_DOWN)
                DrawButton (BTN_DOWN);
        }
        else if (m_nButtonState != BTN_UP)
            DrawButton (BTN_UP);
    }
}

In the override of OnLButtonUp, the mouse capture is released, the m_bMouseCaptured flag is cleared, and if the mouse cursor is over the button, the ButtonClicked() function is called. The ButtonClicked() function opens the appropriate dialog and sends a WM_NOTIFY notification message to the control's parent window.

void CFileEditCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CEdit::OnLButtonUp(nFlags, point);
    ...
    if (m_bMouseCaptured)
    {
        ReleaseCapture();
        m_bMouseCaptured = FALSE;
        if (m_nButtonState != BTN_UP)
            DrawButton(BTN_UP);
        ClientToScreen(point);
        if (ScreenPointInButtonRect(point))
            ButtonClicked();
    }
}
top

Revision History

  • Created October 2000
  • November 11, 2000 - allowed the control to work with dialog templates
  • November 22, 2000 - registers the control's window class, can now be added to dialog as custom control
  • January 4, 2001
    • near total rewrite of the control, now derived from CEdit
    • control can now be added to dialog template using an edit control
    • browse button now drawn in non-client area of the control
  • January 5, 2001 - removed OnKillFocus(), replaced with OnDestroy()
  • January 15, 2001
    • added DDX_ and DDV_ support
    • modified GetStartPosition() and GetNextPathName()
    • modified how FECOpenFile() updates the control text when multiple files are selected
    • added FillBuffers()
    • added support for relative paths
    • added OnChange handler
    • added drag and drop support
  • January 26, 2001 - fixed bug where SHBrowseForFolder does not like trailing slash
  • January 27, 2001 - fixed bug where if control is initialized with text, FillBuffers was not called.
  • January 28, 2001
    • removed GetFindFolder() and SetFindFolder(); replaced with GetFlags() and SetFlags()
    • modified the DDX_ and DDV_ functions to accept these flags
    • modified the Create() function to accept these flags
    • allowed option for returned folder to contain trailing slash
    • allowed browse button to be on the left side of the control
    • added ScreenPointInButtonRect() to better tell if mouse cursor is over the button
    • modified how OnDropFiles() updates the control text when multiple files are dropped
  • February 25, 2001 - fixed EN_CHANGE notification bug. Now parent window receives this notification message, used ON_CONTROL_REFLECT_EX macro instead of ON_CONTROL_REFLECT
  • April 12, 2001 - added OnSize handler, fixed button drawing problem when control size changed
  • April 21, 2001 - added a tooltip for the browse button
  • May 12, 2001
    • removed OnDestroy, replaced with PostNCDestroy
    • added tooltip support to client area
    • modified the FECBrowseForFolder and FECFolderProc functions
    • added a one pixel neutral area between the client area and browse button when the button is on the right hand side of the control. (looks better IMO)
  • May 29, 2001
    • PL -- removed the filename from the m_pCFileDialog->m_ofn.lpstrInitialDir variable, so when browsing back for file, we open the correct folder.
    • used smaller (exact size) arrays for file, extension and path components.
    • some cosmetic changes.
  • May 29, 2001 - FECFolderProc now checks for UNC path. SHBrowseForFolder cannot be initialized with UNC
  • June 2, 2001 - modified ButtonClicked function. Now sends a WM_NOTIFY message to parent window before showing dialog, allows parent window to cancel action by setting result to nonzero. Also sends WM_NOTIFY message to parent window after dialog closes with successful return
  • June 9, 2001 - added OnNcLButtonDblClk handler. Double click on button now treated as two single clicks
  • June 23, 2001
    • placed a declaration for the FECFolderProc global callback function into the header file
    • fixed bug that occurred when removing the filename from the m_pCFileDialog->m_ofn.lpstrInitialDir variable when there was no file to remove.
  • August 2, 2001 - replaced SetWindowText() with OnSetText() message handler. Now correctly handles WM_SETTEXT messages
  • August 12, 2001
    • added GetValidFolder() function and modified FECOpenFile() function. We now start browsing in the correct folder -- it finally works!!! {:o)
    • modified SetFlags() so the button could be moved by setting the FEC_BUTTONLEFT flag
    • removed the m_bCreatingControl variable
    • removed the call to SetWindowPos() from the Create() and DDX_FileEditCtrl() functions. Now done in SetFlags() function
  • August 14, 2001 - modified FECOpenFile(). Now sets the file name in CFileDialog to first file name in FileEditCtrl
  • August 18, 2001 - Set the tooltip font to the same font used in the CFileEditCtrl
  • September 2, 2001
    • added the ModifyFlags() function and changed how the flags are handled
    • modified the GetFlags() function
    • added the FEC_MULTIPLE and FEC_MULTIPLEFILES flags
    • added support for wildcards ( '*' and '?') in filenames

      Involved:

      • modifying the GetStartPosition(), GetNextPathName(), SetFlags(), and FillBuffers() functions
      • adding the ExpandWildCards() function
      • replacing the m_lpstrFiles variable with the m_Files array
      • adding the FEC_WILDCARDS flag.
  • September 3, 2001
    • added ability to dereference shortcut files (*.lnk)
    • added the FEC_NODEREFERENCELINKS flag.
    • added the DereferenceLink() function.
  • September 5, 2001 - fixed the Create() function - now destroys the control if the SetFlags() function fails
  • September 8, 2001
    • added the AddFiles() function to be better able to handle shortcut (*.lnk) files
    • modified the OnDropFiles() function to be better able to handle shortcut (*.lnk) files
  • September 12, 2001
    • PR -- added #include <shlobj.h> to the FileEditCtrl.h header file
    • UNICODE fixes, added _T() macro in Create() function and in TRACE() calls.
    • PR states that the code now works perfectly with UNICODE builds and UNC paths. {:o) {:o) {:o) {:o) {:o) {:o)
  • September 18, 2001 - added ability to use icons or bitmaps on the browse button

    Involved:

    • adding SetButtonImage() function.
    • modifying the DrawButton() function
    • adding the FECButtonImage class.
  • September 20, 2001
    • fixed resource leak in FECButtonImage class
    • cleaned up the FECButtonImage class code
    • added ability to resize the browse button

      Involved:

      • adding m_nButtonWidth variable
      • adding SetButtonWidth() and GetButtonWidth() functions
      • modifying OnNcCalcSize() and DrawButton() functions
  • September 24, 2001 - fixed bug in GetNextPathName() and ExpandWildCards() where multiple files could not start from the current drive. i.e. \folder\file.ext as second file would give an error
  • September 26, 2001 - fixed bug in GetNextPathName() to allow incomplete relative paths (i.e. ..\..\) when browsing for files.
  • October 5, 2001 - PR -- Added #include <afxcmn.h> to the CFileEditCtrl.h header file
  • October 14, 2001
    • rewrote the FECButtonImage::DrawImage() function, it now handles disabled transparent bitmaps better (the transparent color can be any color, no longer just light colors), and now also handles pattern and bitmap background brushes
    • various other touch ups (comments mostly)
  • November 20, 2001 - added ability to dereference *.pif (shortcut to MS-DOS) files
  • November 26, 2001 - added ability to be flat, hot to mouse (FEC_FLAT flag)

    Involved:

    • adding OnKillFocus(), OnLButtonDown() and OnNCMouseMove() message handlers
    • adding Redraw() and SetReadOnly() member functions
    • modifying OnEnable(), OnMouseMove(), OnNCPaint() and OnSetFocus() message handlers
    • modifying DrawButton() and SetFlags() member functions
    • modifying the CFECButtonImage::DrawImage() function
  • December 1, 2001 - clean up code from November 26, 2001
  • December 5, 2001 - added the FEC_GRAYSCALE flag. Flat buttons can be drawn in full color or grayscale.
  • December 8, 2001 - greatly improved the button drawing code.
  • December 18, 2001
    • removed the CFECButtonImage class, replaced with the CPJAImage class.
    • rewrote the SetButtonImage() function.
    • rewrote the DrawButton() function.
  • January 7, 2002 - Remon -- added the FEC_NM_DROP notification. The control now sends a WM_NOTIFY message to its parent window after a file or folder has been dropped onto it.
  • January 14, 2002 - fixed a bug that prevented the tooltips from displaying properly.
  • February 18, 2002 - replaced SetReadOnly() with OnSetReadOnly(), the control now properly handles the EM_SETREADONLY message.
  • March 27, 2002 - fixed border drawing when control is drawn flat. Now the control does not appear to change size when it switches from edit mode to readonly or disabled.
  • March 28, 2002
    • moved the FEC_NM_POSTBROWSE notification. Now it is sent after the dialog closes, but before the edit control's text is updated. The parent window can now return a nonzero value to stop the edit control from updating.

      Involved:

      • modifying the ButtonClicked(), FECBrowseForFolder() and FECOpenFile() functions
      • adding the pNewText pointer to the FEC_NOTIFY structure
    • modified FEC_NM_DROP notification. The parent window can now return a nonzero value to stop the edit control from updating.
  • April 1, 2002 - Removed the CStringArray used to store file names, now using a linked list (CStringList)

    Involved:

    • modifying the AddFile(), GetStartPosition(), GetNextPathName and FillBuffers() functions
  • April 2, 2002 - Added the ability for the CFECFileDialog to dynamically manage the memory needed by the lpstrFile member of the OPENFILENAME structure. (Thanks to Philippe Lhoste)

    Involved:

    • adding the DoModal(), OnFileNameChange() and Reset() functions to the CFECFileDialog class
    • modifying the FECOpenFile() function.
  • November 23, 2002 - The April 2, 2002 changes did not work with WinNT and above. No idea why not. I fixed it by adding the GetStartPosition() and GetNextPathName() functions and a destructor to the CFECFileDialog class. I also removed the Reset() function, and modified the DoModal() and FECOpenFile() functions.
  • December 4, 2002
    • Added the FEC_FILEOPEN and FEC_FILESAVEAS flags. FEC_FILEOPEN replaces the FEC_FILE flag. The FEC_FILESAVEAS flag causes the browse button to open the 'File Save As' dialog.
    • Improved the drawing code for flat control
top

Be sure to check here for the latest updates.

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    President
    Canada Canada
    Father of two, brother of two, child of two.
    Spouse to one, uncle to many, friend to lots.
    Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
    Likes walks with the wife, board games, card games, travel, and camping in the summer.
    High school graduate, college drop-out.
    Hobby programmer who knows C++ with MFC and the STL.
    Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
    Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.

    Comments and Discussions

     
    GeneralRe: Possible bug? Pin
    Philippe Lhoste1-Apr-02 21:33
    Philippe Lhoste1-Apr-02 21:33 
    GeneralRe: Possible bug? Pin
    PJ Arends2-Apr-02 8:46
    professionalPJ Arends2-Apr-02 8:46 
    GeneralRe: Possible bug? Pin
    Philippe Lhoste2-Apr-02 23:07
    Philippe Lhoste2-Apr-02 23:07 
    GeneralYuk! - ShBrowseForFolder Pin
    Andrew Phillips15-Jan-02 14:11
    Andrew Phillips15-Jan-02 14:11 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    PJ Arends15-Jan-02 14:49
    professionalPJ Arends15-Jan-02 14:49 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips16-Jan-02 13:27
    Andrew Phillips16-Jan-02 13:27 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Christian Graus16-Jan-02 13:41
    protectorChristian Graus16-Jan-02 13:41 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips17-Jan-02 15:05
    Andrew Phillips17-Jan-02 15:05 
    > I was expecting to be told it was unreliable or leaked resources.

    Software that leads the user to make mistakes is just as bad. The only time I ever used it the users of my software caused major problems by often depositing files in the wrong place. It can be hard for the user to use it to find a directory and there is little feedback about the directory they select.

    > ... I don't think anything you've listed makes SHBrowseForFolder 'evil', ...

    According to my dictionary one defn for 'evil' is "very unpleasant or troublesome", which exactly describes how I find SHBrowseForFolder.

    > It just does less than the browse for file dialog does.

    It's not just that it does less. The way it works is confusing and irritating.

    Imagine you have a 200 Gbyte server drive with millions of files and thousands of directories and you need to select one directory among hundreds in a directory 6 levels deep and it's management policy to use the same directory sub-structure all over the disk. You get to level 6 and the sub-directory is not there and you think you may have misclicked at one of the previous levels. So you spend ages just scrolling in this pissy little box that must be about 100x100 pixels when you have 3 monitors and 5 million pixels of screen space, trying to work out whether "tmp_" is before or after all the other "tmp*" directories and all you can see is the name of a few directories at the same level.


    Andrew Phillips
    andrew @ expertcomsoft.com
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Christian Graus17-Jan-02 15:43
    protectorChristian Graus17-Jan-02 15:43 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips5-Sep-02 3:50
    Andrew Phillips5-Sep-02 3:50 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    PJ Arends16-Jan-02 23:28
    professionalPJ Arends16-Jan-02 23:28 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips5-Sep-02 4:07
    Andrew Phillips5-Sep-02 4:07 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    PJ Arends5-Sep-02 6:42
    professionalPJ Arends5-Sep-02 6:42 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Bill Leibold17-Jan-02 17:31
    Bill Leibold17-Jan-02 17:31 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips5-Sep-02 4:23
    Andrew Phillips5-Sep-02 4:23 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Philippe Lhoste15-Mar-02 1:20
    Philippe Lhoste15-Mar-02 1:20 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    PJ Arends23-Mar-02 16:22
    professionalPJ Arends23-Mar-02 16:22 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Matt Newman15-Jan-02 15:20
    Matt Newman15-Jan-02 15:20 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    ColinDavies15-Jan-02 15:21
    ColinDavies15-Jan-02 15:21 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips5-Sep-02 4:18
    Andrew Phillips5-Sep-02 4:18 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Christian Graus15-Jan-02 15:33
    protectorChristian Graus15-Jan-02 15:33 
    GeneralRe: Yuk! - ShBrowseForFolder Pin
    Andrew Phillips16-Jan-02 13:33
    Andrew Phillips16-Jan-02 13:33 
    GeneralLittle improvment ... Pin
    6-Jan-02 5:13
    suss6-Jan-02 5:13 
    GeneralAnother one.. Pin
    6-Jan-02 7:14
    suss6-Jan-02 7:14 
    GeneralRe: Another one.. Pin
    PJ Arends7-Jan-02 15:20
    professionalPJ Arends7-Jan-02 15: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.