单击MFC编辑框

4

我在一个MFC对话框中创建了一个只读的编辑框。我试图让用户在只读的编辑框内单击,然后打开一个文件对话框,并使用UpdateData将这个值放入文本框中。我捕捉到了ON_EN_SETFOCUS消息,但是按下文件对话框上的OK按钮会重新生成它,所以我陷入了一个无限循环。

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);

如果您有任何关于如何完成此操作的想法,我将不胜感激。

2
为什么不使用一个按钮?一个名为...的小型CButton用于浏览文件对话框非常普遍。 - AJG85
如果您想在对话框中增加一些可用性,请使用AJG85的建议。没有用户会单击只读CEdit框。 - CppChris
已经完成了,并且它可以工作,只是似乎有两个控件有点多余,想知道是否有更有效的方法来做到这一点? - James
这取决于您对“高效”的定义。实现当前设计将需要更多的消息处理程序和一些if语句检查,对GUI用户来说不太直观。 - AJG85
1
@James - Windows应用程序的用户希望看到那个'...'按钮,并且他们知道该怎么做(因为之前看到过)。他们不会预期点击方框会弹出文件打开对话框。 - Michael Kohne
我也支持对话框上的“...”按钮。 但是我不会为此事件调用“UpdateData”。 - Ajay
3个回答

3

好的,Lister先生,我想我会添加一个答案。

首先,我想强调的是,我可能只会在编辑框右侧添加一个名为“...”的按钮来启动文件对话框,因为这是最简单的解决方案,也是大多数Windows用户期望的。

另一种选择是扩展MFC控件。在决定扩展控件时,您需要选择大部分具有所需行为并具有虚析构函数的控件,以便于成为子类。由于您想要类似按钮的行为,CButton 可能是一个不错的选择。

您的类接口可能如下所示:

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
};

您可以根据控件是通过资源文件创建还是动态创建等需求,自定义控件的外观。基本思路是在按钮上显示当前所选文件名,同时将完整路径存储为成员以供其他用途,这样用户就不需要看到长路径和嵌套目录的混乱。
如果默认外观不符合您的喜好,您可以重写OnPaint并处理WM_PAINT消息,使用自定义字体、大小或为长文件标题添加省略号。您还可以使用文本度量和GetTextExtent来调整按钮大小以适应文件标题,或者当用户将鼠标悬停在按钮上时显示CToolTipCtrl,以便他们查看完整名称。MFC功能包中的CMFCButton具有工具提示功能,因此如果您继承它而不是CButton,则显示工具提示将非常简单,只需调用SetTooltip(m_path)即可。
如果你想变得非常高级,你可以使用一些uxtheme API或新的windows animation API

1
您可以在对话框类中重写PreTranslateMessage(),并以此确定是否点击了编辑控件:
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);
}

更新:您也可以(而且这可能是一个更好的主意)覆盖您的CEdit控件的CWnd::PreTranslateMessage()函数。这将需要从CEdit派生一个类。


1
除非你真的有必要,否则不建议覆盖PreTranslateMessage函数,但是这个方法是可行的。 - AJG85
@AJG85:为什么不推荐呢?只要遵循惯例,我看没有任何问题。 - Chris Dargis
PreTranslateMessage 可以在消息到达之前更改分派消息。尝试在此处处理所有内容是很诱人的,因为您可以在处理之前检查每个消息,但这可能会导致代码不易读取、容易出错且难以调试。每当您在 PreTranslateMessage 中检查 ids 或 hwnds 时,这就是您应该在其他地方处理消息的提示。正如您所说,在此情况下从 CEdit 派生并扩展其自定义行为可能更好。 - AJG85
@AJG85:我能理解你的观点,但是从CEdit派生时,PreTranslateMessage不是也会以同样的方式实现吗?即,CEdit控件没有OnClick通知/消息,因此需要重写PreTranslateMessage - Chris Dargis
取决于你想要多么巧妙;-) 例如,您可以从CButton派生,然后使用一些自定义绘制代码使其看起来像只读的CEdit,如果按钮功能更接近所需的功能。 - AJG85
1
我不想干涉与我无关的事情,但是@AJG85,你似乎想要添加自己的答案。 - Mr Lister

1

如果您正在使用VS2008 SP1或更高版本,则最简单的请求路径的方法是使用CMFCEditBrowseCtrl。它会显示一个带有按钮的编辑控件。使用它的步骤如下:

  • 将您的编辑控件类更改为CMFCEditBrowseCtrl
  • 调用EnableFileBrowseButton告诉它您要浏览文件,而不是文件夹(您可以设置过滤器和默认扩展名)
  • 当用户单击按钮时,会出现一个文件对话框,当您在其中单击“确定”时,所选路径将写入编辑控件。

我之前不知道这个功能包中的MFC类扩展。 - AJG85

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接