Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

Environment: VC6, W9x, W2K


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 no 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.


This code has not been tested for UNICODE builds, nor has it been tested on a network with UNC paths. If any bugs are found and fixed, please drop me a note at pja@telus.net


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


  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 nonclient area. It is a part of the control, not a seperate 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 widows style.
  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 do 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. 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")

    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, set it to FEC_FILE for files.

    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)
        DDX_FileEditCtrl(pDX, IDC_EDIT1, m_FileEditCtrl, FEC_FILE);

    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() 
        CFileEditDemoDlg *pDemo = (CFileEditDemoDlg *)GetParent();
        int width = 0;
        CString str;
        CDC *pDC = m_List.GetDC();
        int saved = pDC->SaveDC();
        // 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);
            CSize size(0, 0);
            size = pDC->GetTextExtent(str);
            width = width > size.cx ? width : size.cx;
        m_List.SetHorizontalExtent(width + 5);
       return TRUE;  // return TRUE unless you set the focus to a control
                     // EXCEPTION: OCX Property Pages should return FALSE

    When a user clicks on the browse button, the control will send a WM_NOTIFY message with a FEC_NM_PREBROWSE notification code to it's 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.

    The control will send another WM_NOTIFY message with a FEC_NM_POSTBROWSE notification code after the dialogs return and the controls window text has been updated. The NMHDR* pointer will once again point to a FEC_NOTIFY structure, but the LRESULT parameter will have no effect and can be ignored.

    typedef struct tagFEC_NOTIFY {
        NMHDR hdr;
        CFileEditCtrl* pFEC;       // pointer to control that sends 
                                   //    this notification
        tagFEC_NOTIFY (CFileEditCtrl *FEC, UINT code);
    #define FEC_NM_PREBROWSE  1    // notification code sent before dialogs 
                                   //    pop up
    #define FEC_NM_POSTBROWSE 2    // notification code sent after dialogs
                                   //    return

    User Functions

    These are the functions that are used to control the CFileEditCtrl class

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

    The class constructor has a parameter bAutoDelete that is FALSE by default. Setting bAutoDelete to TRUE causes the control class to delete itself in its PostNCDestroy() function. If this is done, there is no way of getting the files entered after the dialog has closed.


    The class destructor cleans up all the internal pointers.

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

    Create creates a CFileEditCtrl window in any window that does not have a template. For information on the dwFlags parameter see SetFlags() below. All other parameters are passed on to CWnd::CreateEx(). Returns TRUE on success, and FALSE on failure.

    DWORD CFileEditCtrl::GetFlags()

    GetFlags() returns a DWORD containing the bit flags. See SetFlags() below for an explanation of the flags.

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

    ModifyFlags() is used to modify the controls functionality. First the remove flags are removed, and then the add flags are added. Returns TRUE on success, and FALSE on failure. See SetFlags() below for an explanation of the flags.

    BOOL CFileEditCtrl::SetFlags(DWORD dwFlags)

    SetFlags() is used to set the controls functionality. SetFlags() returns TRUE on success, and FALSE on failure.



    FEC_FILE The 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 flag.
    FEC_MULTIPLE Used with FEC_FILE. The control will accept multiple files. Has the same effect as the OFN_ALLOWMULTISELECT flag.
    FEC_WILDCARDS Used with FEC_FILE. 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_NODEREFERENCELINKS Used with FEC_FILE. 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_FOLDER The 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_FILE flag.
    FEC_TRAILINGSLASH Used with FEC_FOLDER. The folder path entered in the control will have a trailing slash.
    FEC_BUTTONLEFT The ellipses button will be placed on the left side of the control.
    FEC_BUTTONTIP Enables the browse button tooltip. The tooltip text is set with the FEC_IDS_BUTTONTIP resource string
    FEC_CLIENTTIP Enables the client area tooltip. The tooltip text is set with the SetClientTipText() member function
    SetClientTipText(CString text)

    SetClientTipText() is used to set the text of the client area tooltip.

    BROWSEINFO* CFileEditCtrl::GetBrowseInfo() const

    GetBrowseInfo() returns a pointer to the internal BROWSEINFO structure. The return value is NULL if the control is set to find files. Use this pointer to modify the SHBrowseForFolder dialog.

    OPENFILENAME* CFileEditCtrl::GetOpenFileName() const

    GetOpenFileName() returns a pointer to the internal OPENFILENAME structure. The return value is NULL if the control is set to find folders. Use this pointer to modify the CFileDialog dialog.

    POSITION CFileEditCtrl::GetStartPosition()

    GetStartPosition() returns a MFC POSITION structure that is used as a starting point for the GetNextPathName() function. Returns NULL if there are no files entered in the control.

    CString CFileEditCtrl::GetNextPathName(POSITION &pos)

    GetNextPathName() returns a CString containing the full path name of the file entered in the control at the position referenced by the pos variable. GetNextPathName() updates pos to reference the next file entered in the control, or sets pos to NULL if there are no more files. Before calling GetNextPathName() for the first time, pos must be initialized by the GetStartPosition() function.

    void DDV_FileEditCtrl (CDataExchange *pDX, int nIDC)

    DDV_FileEditCtrl() is used to check that the entered file actually exists, and if it does, ensures that the user has entered either a file or a folder, depending on on the settings of the control. If you want your user to enter a nonexistant file or folder, do not use the DDV_FileEditCtrl() function.

    void DDX_FileEditCtrl (CDataExchange *pDX,
                           int nIDC,
                           CFileEditCtrl &rCFEC,
                           DWORD dwFlags)
    void DDX_FileEditCtrl (CDataExchange *pDX,
                           int nIDC,
                           CString& rStr,
                           DWORD dwFlags)

    These functions subclass the edit controls with the ID nIDC and pass the file data between the control and either the CFileEditCtrl referenced by rCFEC or the CString referenced by rStr. For information on dwFlags see SetFlags() above. The CString version does not accept the FEC_MULTIPLE flag (How can it return multiple files in one CString?).

    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 button on 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 button so its right side is at 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 height of client area
          lpncsp->rgrc[0].right -= 
               (lpncsp->rgrc[0].bottom - lpncsp->rgrc[0].top) * 8/10;
          // shrink button so its left side is at 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

    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

    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 nonclient 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 nonclient 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))
          m_bMouseCaptured = TRUE;

    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)
          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 posts a BN_CLICKED notification message to the control's parent window.

    void CFileEditCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
       CEdit::OnLButtonUp(nFlags, point);
       if (m_bMouseCaptured)
          m_bMouseCaptured = FALSE;
          if (m_nButtonState != BTN_UP)
          if (ScreenPointInButtonRect(point))

    Revision History

    November 11, 2000  - allowed the control to work with dialog templates
    November 22, 2000  - register 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 
                       - control can now be added to dialog template using 
                         an edit control
                       - browse button now drawn in nonclient area of 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
                       - 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 
                       - 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
                         recieves this notification message
                         used ON_CONTROL_REFLECT_EX macro instead of
    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
                       - 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
                         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 can not 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 treated as two single clicks
    June 23, 2001      - placed a declaration for the FECFolderProc global 
                         callback function into the header file
                       - fixed bug that occured 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
                         Involved : 
                            modifying the GetStartPosition(), 
                                      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
                       - 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

    Be sure to check here for the latest updates.


    Download demo project - 38 Kb


  • missing menu file

    Posted by Legacy on 09/13/2001 07:00am

    Originally posted by: Paresh Ghewala

    menuedit.h is not available.
    plz. include ASAP. as a comment
    or paste the containts of that file
    we will make it...


  • missing menuedit.h

    Posted by Legacy on 09/13/2001 07:00am

    Originally posted by: Nitro187

    There seems to be a missing file... menuedit.h

    Just curious on where it is. :)


  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date