孙鑫VC++学习:对话框-ONE

   1、创建对话框    

        对话框分为模态(Modal)对话框和非模态(Modeless)对话框两种。模态对话框是指当其显示时,程序会暂停执行,直到关闭这个模态对话框后,才能继续执行程序中其他任务。非模态对话框显示时,允许转而执行程序中其他任务,而不用关闭这个对话框。对话框也是一种资源,可以在资源视图中新建一个对话框来实现。与对话框相关的是CDialog类,当我们使用类向导时,编译器自动检测出我们添加了一个资源,提示我们是否需要为他创建一个类,然后编译器会自动将我们的资源,以及基类选择好,我们只需要填写类名就可以了。编译器为这个类编写了两个函数:1.构造函数2.DoDataExchange,用来完成数据的交换和校验。

       首先,新建一个对话框工程项目MFCApplication,然后资源视图/Dialog/插入Dialog/在新插入的Dialog对话框上添加类,接着在MFCApplication对应的对话框中添加一个按钮“Start Modal”用来启动新插入的Dialog对话框,并在Dialog对话框添加消息响应函数:建立一个DialogDlg对像,调用DoModal函数来实现模态对话框:

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

	DialogDlg dlg;
	dlg.DoModal();
}
注意,还得自己包含#include "Dialog.h",因为这句代码类向导并没有帮助添加。
非模态对话框使用的是Create创建的,注意,创建完成后必须调用ShowWindow来显示对话框。
void CMFCApplicationDlg::OnBnClickedButton2()
{
	// TODO:  在此添加控件通知处理程序代码
	DialogDlg dlg;
	dlg.Create(IDD_DIALOG, this);
	dlg.ShowWindow(SW_SHOW);
} 

可是程序编译后依然没有显示,这是为什么呢?因为这里的dlg是一个局部变量,程序执行完以后,就析构了。那为什么前面的模态对话框就可以呢?因为模态对话框创建之后,程序就停在了这里,所以他的生命周期并没有结束。那么怎么解决呢?

1.把这个对话框变量定义为全局变量。

2.定义一个指针,指向堆上的数据:

void CMFCApplicationDlg::OnBnClickedButton2()
{
	// TODO:  在此添加控件通知处理程序代码
	DialogDlg* pDlg = new DialogDlg;
	pDlg->Create(IDD_DIALOG, this);
	pDlg->ShowWindow(SW_SHOW);
} 

虽然这样做可以实现功能,但是并不好,因为pDlg会在程序结束时被释放,所以我们以后就拿不到这个指针了,自然也就无法释放它,会造成内存泄露。这个问题可以通过将指针定义为类的成员变量来解决。

还有一点区别在于:对于模态对话框,点击“确定”后,对话框是被“销毁了”;而对于非模态对话框,则是“隐藏了”,你需要重写虚函数OnOK,并在其中调用DestroyWindow ,基类的OnOK只是调用了EndDialog来隐藏对话框。

程序运行结果:

2、单击对话框的按钮时,在对话框中动态的创建一个新的按钮

首先回到资源视图,打开原来的对话框,从工具箱中拖一个按钮到对话框上,修改它的ID为IDC_BTN_ADD,名字为ADD。然后右键点击它,选择类向导,然后为其添加消息响应函数OnBnClickedBtnAdd:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码

	m_btn.Create(_T("TEST"), BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
		CRect(0, 0, 100, 100), this, 123);
}
运行程序,点击ADD按钮,的确会显示一个新的“TEST”按钮,但是当再次点击时,就会报错,是因为当我们点击时,会调用OnBnClickedBtnAdd通过Create函数创建一个按钮;但是如果再次点击,那么还会调用Create函数将按钮与m_btn关联,可是m_btn已经和第一次创建的按钮相关联了,所以会出错。为此,可以通过增加一个成员变量m_bIsCreate来记录窗口是否已经创建,如果创建,那么就会销毁窗口,而在下一次点击时,就又可以创建了:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码
	//static BOOL bIsCreated = FALSE;
	if (m_bIsCreate == FALSE)
	{
		m_btn.Create(_T("TEST"), BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
			CRect(0, 0, 100, 100), this, 123);
		m_bIsCreate = TRUE;
	}
	else
	{
		m_btn.DestroyWindow();
		m_bIsCreate = FALSE;
	}	
}

也可以不使用成员变量,而使用静态局部变量static BOOL bIsCreated = FALSE也能够解决上述问题,并且相对于上面设置成员变量会更简单。对于静态变量来说,当第一次加载 OnBnClickedBtnAdd函数时,系统会为它分配内存空间,并初始化为FALSE。以后再次进入OnBnClickedBtnAdd函数时,就不会再为这个静态变量分配内存空间并对它进行初始化操作了。
实际上,有一种更为直接的方法:CWnd对像都有有一个成员变量m_hWnd指向窗口句柄,可以利用这个句柄是否为空来进行判断:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码

	if (!m_btn.m_hWnd)
	{
		m_btn.Create(_T("TEST"), BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,
			CRect(0, 0, 100, 100), this, 123);
		//m_bIsCreate = TRUE;
	}
	else
	{
		m_btn.DestroyWindow();
		//m_bIsCreate = FALSE;
	}	
}

3、对控件的访问

在对话框上放置几个控件:三个静态文本控件和三个编辑框控件,并将静态文本控件的文本分别设置为:“Number1:”,“Number2:”,“Number3:”。静态文本控件主要用来作为标签注释用;编辑框控件用来输一些文本信息。
a、首文件先来实现一个简单的功能即当点击静态文本框Number1以后,将它的显示变为“数字1”。
大的思路肯定是对这个控件响应BN_CLICKED消息。可以发现,这个控件的ID是IDC_STATIC,但是在类向导中根本找不见这个ID,原因是因为所有的静态文本框都是这个ID。实际上,一般静态文本框这个控件是用来当做标签用的,并不是用来响应消息的。但是如果非要让它响应消息,就需要改变它的标签,这里改为IDC_NUMBER1,然后为其添加消息响应函数:
控件实际上也是窗口,因此如果想要获取静态文本控件上显示的文本,就要先获得这个静态文本框,然后再获得它上面的文本,接着把文本重新设置下:
void CMFCApplicationDlg::OnStnClickedNumber1()
{
	// TODO:  在此添加控件通知处理程序代码

	CString str;
	if (GetDlgItem(IDC_NUMBER1)->GetWindowText(str), str == _T("Number1:"))
	{
		GetDlgItem(IDC_NUMBER1)->SetWindowText(_T("数值1:"));
	}
	else
	{
		GetDlgItem(IDC_NUMBER1)->SetWindowText(_T("Number1:"));
	}
}

运行程序点击控件发现并没有反应,原因是静态文件控件在默认状态下是不接收通告消息的,需要在这个控件的属性/Notity改为true。因此,需要让静态文本框控件响应消息,需要两个特殊的步骤:1.改变它的ID,2.Notify改为true。
b、在两个编辑框中输入数字,然后点击Add按钮,在第三个编辑框中显示结果。
方法一:定义3个int和3个char[],从编辑框中获取字符串转化为int,然后做加法,再转化回去显示在编辑框上。
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码
	int num1, num2, num3;
	char ch1[10], ch2[10], ch3[10];
	GetDlgItem(IDC_EDIT1)->GetWindowText(ch1, 10);
	GetDlgItem(IDC_EDIT2)->GetWindowText(ch2, 10);
	num1 = atoi(ch1);
	num2 = atoi(ch2);
	num3 = num1 + num2;
	_itoa_s(num3, ch3, 10);
	GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);	
}
方法二:跟方法一类似,但使用 GetDlgItemText和SetDlgItemText完成了方法一中需要通过两步才能完成的工作。
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{	
	int num1, num2, num3;
	char ch1[10], ch2[10], ch3[10];
	GetDlgItemText(IDC_EDIT1,ch1,10);
	GetDlgItemText(IDC_EDIT2, ch2, 10);
	num1 = atoi(ch1);
	num2 = atoi(ch2);
	num3 = num1 + num2;
	_itoa_s(num3, ch3, 10);
	SetDlgItemText(IDC_EDIT3, ch3);		
}
方法三:通过使用GetDlgItemInt函数直接通过控件ID获取空间的文本,并且将它转化为int类型,再使用SetDlgItemInt函数完成显示。
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	int num1 = GetDlgItemInt(IDC_EDIT1);
	int num2 = GetDlgItemInt(IDC_EDIT2);
	int num3 = num1 + num2;
	SetDlgItemInt(IDC_EDIT3,num3);		
}
方法四:
将这三个编辑框分别与对话框类的三个成员变量相关联,然后通过成员变量来检索和设置编辑框的文本。
首先,为这三个编辑框关联3个成员变量:在对话框上右键选择“类向导”,选择CMFCApplicationDlg类,然后依次选择“ID_EDIT1”,“ID_EDIT2”,“ID_EDIT3”,为它们添加int型变量m_num1,m_num2,m_num3。



添加完成后可以发现类向导为我们添加3处代码:
在头文件中:
	int m_num1;
	int m_num2;
	int m_num3;
源文件的构造函数中:
CMFCApplicationDlg::CMFCApplicationDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CMFCApplicationDlg::IDD, pParent)
	, m_num1(0)
	, m_num2(0)
	, m_num3(0)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_bIsCreate = FALSE;
}
在源文件的DoDataExchange中:
void CMFCApplicationDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_num1);
	DDX_Text(pDX, IDC_EDIT2, m_num2);
	DDX_Text(pDX, IDC_EDIT3, m_num3);
}
DDX_TEXT函数用来将ID指定的控件与特定的类成员变量相关联。既然编辑框控件已经与类成员变量相关联了,那么就可以直接利用这些相关联的变量来编程实现上述所需功能了。如果真是这样子简单,那么我们只要在OnBnClickedBtnAdd函数中添加如下一行代码即可了:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{	
	m_num3 = m_num1 + m_num2;	
}
运行程序后发现,在前两个编辑框中输入两个数值后,单击“ADD”按钮第三个编辑框中并没有出现所期待的结果。可以发现:不管输入多少,执行到m_num3 = m_num1 + m_num2时,m_num1和m_num2的值总为0。DoDataExchange函数不是直接调用的,而是通过UpdataData函数调用的。而UpdateData函数有一个参数:当参数为true时(默认),是从对话框获取数据,当参数为false时,是向对框写数据。于是只是将程序改为如下就可以了:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	UpdateData();
	m_num3 = m_num1 + m_num2;
	UpdateData(FALSE);	
}
因为我们把编辑框控件与一个int型的变量相关联了,所以如果我们在编辑框中输入一个非数值型的字符,然后点击“ADD”按钮,程序会弹出一个对话框,提示:“请键入一个整数”。
方法五:将编辑框与控件变量相关联。
这里选择的是CEdit类型。有了这些成员变量,我们就不需要第一种方法中的GetDlgItem(IDC_EDIT1)步骤了,直接用这些变量来调用GetWindowText或者SetWindowText就行了。
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码

	int num1, num2, num3;
	char ch1[10], ch2[10], ch3[10];
	m_edit1.GetWindowTextA(ch1,10);
	m_edit2.GetWindowTextA(ch2, 10);

	num1 = atoi(ch1);
	num2 = atoi(ch2);
	num3 = num1 + num2;
	_itoa(num3,ch3,10);

	m_edit3.SetWindowTextA(ch3);
		
}

方法六:通过消息。通过向编辑框发送指定的消息(获取文本WM_GETTEXT、设置文本WM_SETTEXT),来实现:

void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码
	
	int num1, num2, num3;
	char ch1[10], ch2[10], ch3[10];
	::SendMessage(m_edit1.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch1);
	::SendMessage(m_edit2.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch2);
	num1 = atoi(ch1);
	num2 = atoi(ch2);
	num3 = num1 + num2;
	_itoa(num3,ch3,10);
	m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3);
		
}
注意:
1.SendMessage是SDK下的函数,所以调用前需要加上::
2.发送WM_GETTEXT消息时,wParam指明最大字节数,lParam填字符串的地址,但是需要强制类型转化。
3.发送WM_SETTEXT消息时,wParam不使用,填为0,lParam填字符串的地址,但是需要强制类型转化。

方法七:直接给对画框的子控件发送消息:
void CMFCApplicationDlg::OnBnClickedBtnAdd()
{
	// TODO:  在此添加控件通知处理程序代码
	
	int num1, num2, num3;
	char ch1[10], ch2[10], ch3[10];
	SendDlgItemMessage(IDC_EDIT1, WM_GETTEXT, 10, (LPARAM)ch1);
	SendDlgItemMessage(IDC_EDIT2, WM_GETTEXT, 10, (LPARAM)ch2);
	num1 = atoi(ch1);
	num2 = atoi(ch2);
	num3 = num1 + num2;
	_itoa(num3,ch3,10);
	SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT, 0, (LPARAM)ch3);
		
}
使用成员函数,就省去了获取编辑框的工作。

如果想获取编辑框中的一部分文本,可以通过发送EM_SETSEL消息来获得,其中wParam表示起始位置,lParam表示结束位置。但是如果你想获取一部分文本,那么你的焦点必须设在这个文本框上:
	SendDlgItemMessage(IDC_EDIT3,EM_SETSEL, 1, 2);
	m_edit3.SetFocus();

总结起来,在以上七种方法中:方法一、四、五比较常用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值