两个相互独立的对话框(非父子关系)之间的通信(消息响应、数值传递)

最近因项目需要在学习两个窗口之间的消息传递。

假设有相互独立的A、B两个对话框,现在点击A界面的Button控件之后,弹出B对话框。点击对话框B上按键之后在对话框A中响应自定义的消息。

1、工程创建完成之后资源视图会有一个对话框,更改其ID为IDD_DlgA。再加入一个对话框,style选popup,ID为IDD_DlgB。两个对话框的caption分别为A、B。

2、对话框A的头文件名和源文件名分别为:CDlgADlg.h、CDlgADlg.cpp(注意和CDlgA.h、CDlgA.cpp的区分)。手动给对话框B添加类,类名为CDlgB。

3、在对话框A中添加一个Button控件,为其添加变量为OkBtn;在对话框B中添加一个Button控件,为其添加变量为SendMessageToDlgABtn。

4、在A对话框的源文件中包含"DlgB.h"(至于为什么是在源文件中包含,而不是在头文件中包含,下文中会有解释),并在源文件任何函数之外定义一个对话框B的对象:CDlgB DlgB;。

然后在A对话框的OnInitDialog函数中添加如下代码:DlgB.Create(IDD_DlgB,this);

5、给对话框A中的Button控件添加事件处理程序,在其中添加如下代码:

void CDlgADlg::OnBnClickedOkBtn()
{
    // TODO: 在此添加控件通知处理程序代码
    POINT point;
    ::GetCursorPos(&point); //得到鼠标点击的位置
    DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B														       
    DlgB.ShowWindow(SW_SHOW);//显示对话框B
}

这样就能在鼠标点击的位置弹出对话框B了

6、在对话框A中添加消息响应函数。

6.1、因为消息的实现是在对话框A中,所以我们在其头文件中添加如下宏定义:

#define TestMessage WM_USER+100

6.2、在资源视图IDD_DlgA中点击鼠标右键进入类向导

图中位置2处添加的就是宏定义的符号名:TestMessage。

6.3、添加完处理程序之后,编辑代码:

afx_msg LRESULT CDlgADlg::OnTestmessage(WPARAM wParam, LPARAM lParam)

{

    MessageBox(_T("在对话框A中已处理对话框B控件的消息!"));

    return 0;

}

这样消息的处理就算完成了,接下来就是如何响应到这条消息,及消息的发送,这也是难点所在。

7、消息发送

自然而然的我们就想到了使用 SendMessage()这个函数。该函数的原型为:

LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam)

参数:

hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST:则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。

Msg:指定被发送的消息。

wParam:指定附加的消息特定信息。

IParam:指定附加的消息特定信息。

返回值:返回值指定消息处理的结果,依赖于所发送的消息。

我们先说说第二个参数,这个参数就是之前在对话框A中自定义消息的变量。而现在发送消息是在对话框B中实现的,故而,在对话框B的源文件中应该包含对话框A的头文件CDlgADlg.h。

现在我们就结合上文提到的不将"DlgB.h"包含到对话框A的头文件中来说说为什么也不将CDlgADlg.h包含于对话框B的头文件中,因为这样会导致CDlgADlg.h包含自身、CDlgB.h也包含自身。以致出现如下错误:

第三、四个参数是形参,用于传值。

下面重点说说第一个参数hWnd。它是第二个参数所在窗口的句柄,如果没有将第一个参数填写的相应的窗口句柄,消息是无法传递过去的。问题的关键所在就是如何在对话框B中得到对话框A的窗口句柄。

刚开始,我是想在对话框B中直接获取对话框A的窗口句柄,在网上搜了很多方法:

1)、this->m_hWnd或者this->GetSafeHwnd(),这个是获取当前窗口的句柄的,不适用我的程序。

2)、::FindWindow(NULL,WindowsName) ,这个需要知道窗口的名称,但是它不具一般性,虽然可能能够解决当前的问题,但是以后要是窗口没有名称怎么办(当窗口的border为none时,就不能设置名称),所以我也没有深入尝试这种方法。

3)、this->GetParent()->m_hWnd,这个是获取当前对话框的父对话框的句柄的,如果将对话框B的style设置为child,那么消息能够顺利的发送出去,处理函数也能顺利执行,但是由于实际的需要,我的对话框B不是对话框A的子对话框。故而这种方法也不适用。

无果,我开始转换思维,想着利用上面的第一种方法,在对话框A的按键处理程序中获取窗口A的句柄,并将其传到对话框B的程序中,这也不失为一种好方法。能让句柄传过去的关键就是找一个在两个源文件中的共同变量,这样才好用。

刚开始,我发现两个对话框的源文件中都包含了"DlgA.h",于是我就在"DlgA.h"文件中定义了一个全局变量 HWND DlgA_hWnd;然而这样会出现重定义:

然后我参考网上的资料,在对话框B的类中声明一个静态变量:static HWND m_DlgA_hWnd;,然后在对话框A的按键消息处理函数中修改代码:

void CDlgADlg::OnBnClickedOkBtn()
{
	// TODO: 在此添加控件通知处理程序代码

	//获取窗口A的句柄,并将其存至对话框B的静态成员m_DlgA_hWnd中
	DlgB.m_DlgA_hWnd=this->m_hWnd;
	POINT point;
	::GetCursorPos(&point); //得到鼠标点击的位置
	DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
	DlgB.ShowWindow(SW_SHOW);//显示对话框B
}

然后在对话框B中的按钮处理程序中添加如下代码:

void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
	// TODO: 在此添加控件通知处理程序代码
	::SendMessage(CDlgB::m_DlgA_hWnd,TestMessage,0,0);
}

编译程序。便出现如下的错误:

在网上也查找了原因:1)、只有声明而没有实现文件,2)、链接时实现文件没有链接进去。

然而如果我声明的不是静态成员变量,则不会出现此问题(得在类外定义对象,不能使用CDlgB::m_DlgA_hWnd)。

这个问题的原因我还没有找到,希望大家不吝赐教,谢谢大家。

之后,我查到另外一种方法:在要获取窗口句柄的源文件中定义一个全局变量,在使用的源文件中的任何函数之外利用extern 声明一下这个全局变量。这样就行了,具体代码如下:

在DlgADlg.cpp中:

HWND DlgA_hWnd;
void CDlgADlg::OnBnClickedOkBtn()
{
	// TODO: 在此添加控件通知处理程序代码

	//获取窗口A的句柄,并将其存至DlgA_hWnd中
	DlgA_hWnd=this->m_hWnd;

	POINT point;
	::GetCursorPos(&point); //得到鼠标点击的位置
	DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
	DlgB.ShowWindow(SW_SHOW);//显示对话框B
}

在DlgB.cpp中:

extern HWND DlgA_hWnd;
void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
	// TODO: 在此添加控件通知处理程序代码
	::SendMessage(DlgA_hWnd,TestMessage,0,0);
}

由于我也是刚接触MFC,有些地方的理解可能有些偏差,甚至是错误的,恳请大家指正,谢谢大家!

下面附上整个工程的代码,一起交流学习:

对话框A:

DlgADlg.h


// DlgADlg.h : 头文件

#pragma once
#include "afxwin.h"

#define TestMessage WM_USER+100

// CDlgADlg 对话框
class CDlgADlg : public CDialog
{
// 构造
public:
	CDlgADlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
	enum { IDD = IDD_DlgA };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CButton OkBtn;
	afx_msg void OnBnClickedOkBtn();
protected:
	afx_msg LRESULT OnTestmessage(WPARAM wParam, LPARAM lParam);//自定义的消息响应函数
};

DlgADlg.cpp


// DlgADlg.cpp : 实现文件

#include "stdafx.h"
#include "DlgA.h"
#include "DlgADlg.h"
#include "DlgB.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CDlgB DlgB;

// CDlgADlg 对话框
CDlgADlg::CDlgADlg(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgADlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDlgADlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_OkBtn, OkBtn);
}

BEGIN_MESSAGE_MAP(CDlgADlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_OkBtn, &CDlgADlg::OnBnClickedOkBtn)
	ON_MESSAGE(TestMessage, &CDlgADlg::OnTestmessage)
END_MESSAGE_MAP()


// CDlgADlg 消息处理程序

BOOL CDlgADlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	DlgB.Create(IDD_DlgB,this);

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CDlgADlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDlgADlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

HWND DlgA_hWnd;
void CDlgADlg::OnBnClickedOkBtn()
{
	// TODO: 在此添加控件通知处理程序代码

	//获取窗口A的句柄,并将其存至DlgA_hWnd中
	DlgA_hWnd=this->m_hWnd;

	POINT point;
	::GetCursorPos(&point); //得到鼠标点击的位置
	DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
	DlgB.ShowWindow(SW_SHOW);//显示对话框B
}


afx_msg LRESULT CDlgADlg::OnTestmessage(WPARAM wParam, LPARAM lParam)
{
	MessageBox(_T("在对话框A中已处理对话框B控件的消息!"));
	return 0;
}

对话框B:

DlgB.h

#pragma once
#include "afxwin.h"


// CDlgB 对话框

class CDlgB : public CDialogEx
{
	DECLARE_DYNAMIC(CDlgB)

public:
	CDlgB(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~CDlgB();
// 对话框数据
	enum { IDD = IDD_DlgB };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()

public:
	CButton SendMessageToDlgABtn;
	afx_msg void OnBnClickedSendMessageToDlgaBtn();
	virtual BOOL OnInitDialog();
};

DlgB.cpp

// DlgB.cpp : 实现文件
//

#include "stdafx.h"
#include "DlgA.h"
#include "DlgB.h"
#include "DlgADlg.h"
#include "afxdialogex.h"


// CDlgB 对话框

IMPLEMENT_DYNAMIC(CDlgB, CDialogEx)

BOOL CDlgB::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	// TODO:  在此添加额外的初始化

	return TRUE;  // return TRUE unless you set the focus to a control
}


CDlgB::CDlgB(CWnd* pParent /*=NULL*/)
	: CDialogEx(CDlgB::IDD, pParent)
{

}

CDlgB::~CDlgB()
{
}

void CDlgB::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CDlgB, CDialogEx)
	ON_BN_CLICKED(IDC_SendMessageToDlgABtn, &CDlgB::OnBnClickedSendMessageToDlgaBtn)
END_MESSAGE_MAP()


// CDlgB 消息处理程序
extern HWND DlgA_hWnd;
void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
	// TODO: 在此添加控件通知处理程序代码
	::SendMessage(DlgA_hWnd,TestMessage,0,0);
}


猜你喜欢

转载自blog.csdn.net/ett_qin/article/details/81671690