4

I've created a read-only edit box in an MFC dialog box. I'm trying to have it so a user clicks in the edit box, which is read-only, it opens a file dialog, and then puts this value into the text box using UpdateData. I'm catching the ON_EN_SETFOCUS message but pressing OK on the file dialog respawns it, so I get caught in an infinite loop.

UpdateData(TRUE);
CFileDialog fileDialog(TRUE,NULL, NULL,OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, _T("Text Files(*.txt)|*.txt||"));
if( fileDialog.DoModal() == IDOK )
{
    configFile=fileDialog.GetPathName(); //Note to self, this includes filename, getPathName includes filename and path.

}
else
{
    return;
}

UpdateData(FALSE);

If you've got any ideas on how this should be done, I would be very grateful.

A.H.
  • 63,967
  • 15
  • 92
  • 126
James
  • 1,764
  • 5
  • 31
  • 49
  • 2
    Why not use a button? A small `CButton` named `...` to browse with a file dialog is pretty common. – AJG85 Jul 19 '12 at 16:26
  • Use the proposal by AJG85 if you want some usability in your dialog. No user will ever click on a read-only CEdit box. – CppChris Jul 19 '12 at 16:41
  • Done that, and it work, just seems extraneous to have two controls, and was wondering if there was a more efficient way of doing it? – James Jul 19 '12 at 16:42
  • That depends what you mean by efficient. It will take more message handlers and some if statement checks and be less intuitive to the user of the GUI to implement your current design. – AJG85 Jul 19 '12 at 16:47
  • 1
    @James - users of Windows apps EXPECT to see that '...' button and they know what to do with it (having seen it many times before). They won't expect that clicking on the box will bring up the file open dialog. – Michael Kohne Jul 19 '12 at 17:12
  • I also vote for `...` button on dialog box. But I wouldn't call `UpdateData` for this event. – Ajay Jul 20 '12 at 08:06

3 Answers3

3

Alright Mr. Lister I guess I'll add an answer.

First off I would preface this with I would probably simply add a button name "..." to launch the file dialog to the right of the edit box for opening the file dialog as that's the simplest solution and what most windows users will expect.

Another option however is to extend an MFC control. When deciding to extend a control you want to pick one that mostly has the desired behavior and that has a virtual destructor which lends itself to being a subclass. Since you want button like behavior CButton may be a good choice.

Your class interface might look something like this:

class CPathButton : public CButton
{
public:
    enum { ID /*= IDC_BUTTON1*/ };

    const CString GetPath() const;
    const CString GetFileName() const;
    const CString GetDirectory() const;
    const CString GetExtension() const;
    // other useful methods for setting file filters etc

protected:
    // add ON_CONTROL(BN_CLICKED, ID, &OnClick) or ON_BN_CLICKED(ID, &OnClick)
    DECLARE_MESSAGE_MAP()

    // CFileDialog fdlg.DoModal(), m_path = fdlg.GetPathName(), SetWindowText(fdlg.GetFileTitle()), etc
    afx_msg void OnClick();

    // additional message handlers etc

private:
    CString m_path; // save full path for after dialog is closed
};

You can add as much or as little customization as you want depending on if the control will be created dynamically, via the resource file, or whatever. The basic idea being that you display the currently selected file name on the button while storing the full path for other uses as a member so the user doesn't need to see the clutter of a long path with nested directories.

If you don't like the way it looks by default you can override OnPaint and handle WM_PAINT messages and use a custom font, size, or add ellipsis for a long file title. You could also handle re-sizing the button to fit the file title by using text metrics and GetTextExtent to ensure the name fits or simply display a CToolTipCtrl when they hover the mouse over the button so they can see the full name. The CMFCButton from the MFC feature pack in VS2008+ has tool tip functionality built in so if you inherit from that instead of CButton displaying a tool tip would be as simple as calling SetTooltip(m_path)

If you want to get really fancy you could use some of the uxtheme API or new windows animation API.

AJG85
  • 15,849
  • 13
  • 42
  • 50
1

You can override PreTranslateMessage() in your dialog class, and determine if the edit control was clicked that way:

CEdit m_CEditCtrl;
// ...

BOOL YourDialogClass::PreTranslateMessage(MSG *pMsg)
{
    if((pMsg->wParam == VK_LBUTTON) && (m_CEditCtrl.m_hWnd == pMsg->hwnd))
    {
       // open your file dialog
       return TRUE; // Return that the message was translated and doesn't need to be dispatched
    }
    return CDialog::PreTranslateMessage(pMsg);
}

Update: You can also (and it may be a better idea) to override your CEdit control's CWnd::PreTranslateMessage() function. This would require deriving a class from CEdit.

Chris Dargis
  • 5,891
  • 4
  • 39
  • 63
  • 1
    It's not recommended to override PreTranslateMessage unless you really have to but this would work. – AJG85 Jul 19 '12 at 16:59
  • @AJG85: Why is it not recommended? As long as the conventions are followed, I don't see any problem. – Chris Dargis Jul 19 '12 at 17:01
  • PreTranslateMessage can alter dispatch messages before they arrive. It's tempting to handle everything there as you can check every and all messages before processed but this can lead to less readable code that is error prone and harder to debug. Anytime you are checking ids or hwnds in PreTranslateMessage that's your cue you should be handling the message elsewhere. As you said deriving from CEdit and extending it with the custom behavior may be better in this case. – AJG85 Jul 19 '12 at 17:31
  • @AJG85: I can see your point, but wouldn't `PreTranslateMessage` be implemented the same way when deriving from CEdit? i.e., there is no OnClick notification/message for a CEdit control, hence the need to override `PreTranslateMessage`. – Chris Dargis Jul 19 '12 at 17:53
  • Depends how tricky you want to get ;-) You could for example derive from `CButton` and then use a bit of custom draw code to make it look like a read only `CEdit` if button functionality is closer to what is desired. – AJG85 Jul 19 '12 at 18:01
  • 1
    I don't want to interfere in matters not my concern, but @AJG85 you sound like you would want to add your own answer. – Mr Lister Jul 19 '12 at 18:11
1

If you are using VS2008 SP1 or above, the easiest way to ask for a path is with CMFCEditBrowseCtrl. It displays an edit control with a button. The steps to use it are:

  • Change your edit control's class to CMFCEditBrowseCtrl
  • Call EnableFileBrowseButton to tell it that you want to browse for files, not folders (you can set a filter and default extension)
  • When the user clicks the button, a file dialog appears, and when you click OK in it, the selected path is written in the edit control.
MikMik
  • 3,426
  • 2
  • 23
  • 41
  • +1 for the best answer. I wasn't aware of this MFC class extension in the feature pack. – AJG85 Jul 23 '12 at 16:51