需求
在MFC中,有时候可能需要创建派生自自定义对话框类的对话框类(这句话很复杂,慢慢理解),也就是说创建了一个自定义对话框A,再创建一个自定义对话框B,如果B用到了A的很多方法,就需要让B派生自A。但是,给对话框添加类时,基类选项里并没有A类,只有默认的CDialogEx等类。下面,我们就来探讨一下手动把B类设置为A类的派生类的方法。
假如,我们要模仿Word中的查找和替换功能,有两个类,一个是查找对话框类,类名为CFindTextDialog,一个是替换对话框类,类名是CReplaceDialog。因为替换对话框有很多功能和查找对话框一样,所以应该让CReplaceDialog成为CFindTextDialog类的派生类。步骤如下:
创建CFindTextDialog类
先创建CFindTextDialog的资源和对话框类。创建对话框类时基类要选CDialogEx。如图所示:
CFindTextDialog.h:
#pragma once
// CFindTextDialog 对话框
class CFindTextDialog : public CDialogEx
{
DECLARE_DYNAMIC(CFindTextDialog)
public:
CFindTextDialog(CRichEditCtrl& RichEdit, CWnd* pParent = nullptr); // 标准构造函数
virtual ~CFindTextDialog();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_FINDDIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
FINDTEXTEXW m_ft;
CRichEditCtrl& m_RichEdit;
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedFindNext();
virtual void PostNcDestroy();
afx_msg void OnBnClickedCasesensitive();
};
CFindTextDialog.cpp:
// CFindTextDialog.cpp: 实现文件
//
#include "pch.h"
#include "Word.h"
#include "CFindTextDialog.h"
#include "afxdialogex.h"
// CFindTextDialog 对话框
IMPLEMENT_DYNAMIC(CFindTextDialog, CDialogEx)
CFindTextDialog::CFindTextDialog(CRichEditCtrl& RichEdit,
CWnd* pParent)
: CDialogEx(IDD_FINDTEXTDIALOG, pParent), m_RichEdit(RichEdit)
{
}
CFindTextDialog::~CFindTextDialog()
{
}
void CFindTextDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CFindTextDialog, CDialogEx)
ON_BN_CLICKED(IDC_FINDNEXT, &CFindTextDialog::OnBnClickedFindNext)
ON_BN_CLICKED(IDC_CASESENSITIVE, &CFindTextDialog::OnBnClickedCasesensitive)
END_MESSAGE_MAP()
// CFindTextDialog 消息处理程序
void CFindTextDialog::OnBnClickedFindNext()
{
m_RichEdit.GetSel(m_ft.chrg);
m_ft.chrg.cpMax = -1;
CString strText;
GetDlgItem(IDC_TEXTTOFIND)->GetWindowTextW(strText);
m_ft.lpstrText = strText;
if (m_RichEdit.GetSelText() == strText)
m_ft.chrg.cpMin += 1;
if (((CButton*)GetDlgItem(IDC_CASESENSITIVE))->
GetCheck())
m_RichEdit.FindTextW(FR_DOWN | FR_MATCHCASE, &m_ft);
else
m_RichEdit.FindTextW(FR_DOWN, &m_ft);
if (m_ft.chrgText.cpMax != -1)
m_RichEdit.SetSel(m_ft.chrgText);
else
MessageBoxW(L"未找到指定文本!", L"查找", MB_ICONINFORMATION | MB_OK);
m_RichEdit.SetFocus();
}
void CFindTextDialog::PostNcDestroy()
{
CDialogEx::PostNcDestroy();
delete this;
}
void CFindTextDialog::OnBnClickedCasesensitive()
{
m_RichEdit.SetFocus();
}
创建CReplaceDialog类
创建CReplaceDialog的资源。添加对话框类时,基类没有CFindTextDialog选项,所以我们先选CDialogEx。具体步骤省略。
修改代码
首先,要把CReplaceDialog.h中添加#include"CFindTextDialog.h"
,并把class CReplaceDialog : public CDialogEx
改为class CReplaceDialog : public CFindTextDialog
。
然后,将CReplaceDialog.cpp中的IMPLEMENT_DYNAMIC(CReplaceDialog, CDialogEx)
改为IMPLEMENT_DYNAMIC(CReplaceDialog, CFindTextDialog)
,BEGIN_MESSAGE_MAP(CReplaceDialog, CDialogEx)
改为BEGIN_MESSAGE_MAP(CReplaceDialog, CFindTextDialog)
。
然后,我们发现自动生成的构造函数有问题,CDialogEx(IDD_REPLACEDIALOG,pParent)
提示“不允许使用间接非虚拟基类”,解决办法是用CFindTextDialog的构造函数,但是那样创建出来的对话框也是CFindTextDialog的资源,所以我们必须修改CFindTextDialog类的构造函数,添加一个默认参数ID。
CFindTextDialog修改后的构造函数:
//声明
CFindTextDialog(CRichEditCtrl& RichEdit, CWnd* pParent = nullptr, UINT ID = IDD_FINDDIALOG);
//定义
CFindTextDialog::CFindTextDialog(CRichEditCtrl& RichEdit,
CWnd* pParent, UINT ID)
: CDialogEx(ID, pParent), m_RichEdit(RichEdit)
{
}
接下来,把CReplaceDialog的构造函数改成这样:
CReplaceDialog::CReplaceDialog(CRichEditCtrl& RichEdit, CWnd* pParent /*=nullptr*/)
: CFindTextDialog(RichEdit, pParent, IDD_REPLACEDIALOG)
{
}
这样,CReplaceDialog就可以直接调用CFindTextDialog的函数了。
添加消息处理程序
虽然我们手动设置了派生类,但CReplaceDialog执行时对应控件事件还没有处理。如果控件是新添加的,我们就可以直接使用添加事件处理程序的方法,但如果是CFindTextDialog里就有的,我们就需要做一下处理了。有以下两种方法:
1. 直接使用代码(不推荐)
可以直接在CReplaceDialog的MESSAGE_MAP中添加代码。如,按钮按下对应代码是:
ON_BN_CLICKED(IDC_FINDNEXT, &CReplaceDialog::OnBnClickedFindNext)
//这里的OnBnClickedFindNext是派生过来的CFindTextDialog中的函数
但这样有一大缺陷,就是如果有些复杂的消息,我们不知道对应的宏,就没法手动添加了。所以,我们需要更方便的方法。
2.修改自动添加的代码(推荐)
我们可以直接用平常添加消息处理程序的方法,VS会自动创建消息映射,我们直接把自动生成的函数声明和定义删掉,调用基类的函数就行了。