MFC对话框学习笔记之模态对话框转化为非模态

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leo1949asd/article/details/38322189


模态对话框用起来很方便,直接定义一个对话框对象,然后调用DoModal(); 即可 

如:

void CYuanView::OnSet() 
{
// TODO: Add your command handler code here
CSettingDlg dlg;
if(IDOK==dlg.DoModal())
{
m_x=dlg.m_dlgx;
m_y=dlg.m_dlgy;
m_r=dlg.m_dlgr;
}

	Invalidate();//使得窗口无效 让系统自动调用ondraw()函数 默认参数是true 表示刷新背景 如果是false则不刷新背景
}
      要改成非模态的一般在调用对话框的时候动态new一个对话框对象出来 然后依次调用create 和 showindow函数即可显示出来 

如:

void CYuanView::OnSet() 
{
	// TODO: Add your command handler code here

	if(dlg == NULL)
	{
		dlg = new CSettingDlg;
		dlg->Create(IDD_DIALOG1,this);	
		dlg->ShowWindow(SW_SHOW);
	}
	else
	{
		dlg->SetActiveWindow();
	}
}
为了点击onset对应的按钮时候 只实例化出来一个对话框

在对应的view类中添加一个 对话框对象的指针,并在view的构造函数中初始化其为NULL;

public:CSettingDlg * dlg;


CYuanView::CYuanView()
{
	// TODO: add construction code here
	dlg = NULL;//初始化
}
如果dlg != NULL 则把对话框设置为当前的活动窗口

dlg->SetActiveWindow();

这样就能实现点击onset对应的按钮会出现一个对话框出来

接下来要实现的是在点击对话框中的确定按钮的时候 调用view类中ondraw函数来实现画圆的操作

用最简单的关联变量法,先把对话框中的三个edit控件分别关联三个变量,用来接受用户输入的数值,先来看一下,一直说的对话框的样子



当点击确定按钮的时候,要首先通过UpdateData(TRUE)函数获得用户的输入,然后再把用户的输入传给view类,并且调用view的ondraw函数,

我是这样做的 ,给对话框增加一个view类的指针成员变量

public:
	CYuanView * m_pView;
然后通过类向导增加一个OnInitDialog函数(这是一个消息响应函数 WM_INITDIALOG),在这个函数中让m_pView指向当前的活动的view类

BOOL CSettingDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// TODO: Add extra initialization here
	//AfxMessageBox("aqwe");
	m_pView = (CYuanView*)((CMainFrame*) AfxGetMainWnd())->GetActiveView();
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}
这样就可以通过m_pView来调用view类中的函数了,如果直接调用OnDraw函数 ,可以看到OnDraw函数有一个CDC *pDoc的指针,暂时不知道怎么获得这个参数。。。

还是通过view类的Invalidate函数来让系统自动调用OnDraw函数.

在调用之前当然要先把刚才获得的用户输入传给view类对应的变量(m_x  , m_y ,m_r)

我们是要在点击确定的时候画圆,所以在对话框的确定函数中调用view类对应的函数

可以在view类中增加一个自定义的函数 MyDraw函数 通过这个函数给m_x  , m_y ,m_r 赋值,并调用Invalidate()函数,完成画圆。


这样就完成了从模态对话框到非模态对话框的转换,但是这还没有完

因为对话框时在堆上new出来的,所以在对话框上点击取消的时候还要释放这个内存,要不然会造成内存泄露

在对话框中重写虚函数PostNcDestroy函数

void CSettingDlg::PostNcDestroy() 
{
	// TODO: Add your specialized code here and/or call the base class
	
	CDialog::PostNcDestroy();
	delete this;
}

这个函数是在产生ON_DESTROY消息的时候调用,所以在取消按钮对应的oncancel函数中调用DestroyWindow();函数,但是这样还没完,调用DestroyWindow();函数的同时,还有把view类的dlg恢复成NULL。要不然下次点击onset函数对应的按钮时候会无效。

void CSettingDlg::OnCancel() 
{
	// TODO: Add extra cleanup here

	m_pView->dlg = NULL;
	DestroyWindow();
	//CDialog::OnCancel();
}

OnOK()或OnCancel() ---> EndDialog() ---> DestroyWindow() ---> OnDestroy() ---> PostNcDestroy() 

最后一步调用的是PostNcDestroy() 函数 所以一般在这个函数完成删除指针释放内存的操作

销毁窗口对象


在单文档上做和对话框的交互的情况下 还可以这么做
给对话框类增加一个view类的指针变量 ,在view类new出来一个dlg对象后 要在调用dlg的create函数后才能把view类的this指针赋值给dlg中的view指针,
这样就可以交互数据了 
void CMyTestView::OnShezhi() 
{
	// TODO: Add your command handler code here
	/*CSetDlg dlg;
	dlg.DoModal();
	*/
	if(setDlg == NULL)
	{
		setDlg = new CSetDlg;
		setDlg->Create(IDD_DIALOG1,this);	
		setDlg->myView = this;
		setDlg->ShowWindow(SW_SHOW);

	}
	else
	{
		setDlg->ShowWindow(SW_SHOW);
		setDlg->SetActiveWindow();
	}

}




附: 在这里面遇到了一个这样的错误

syntax error : missing ';' before '*' 

查了一下是交叉引用的错误

如果是这个顺序
#include "Sdi1View.h"
#include "Sdi1Doc.h"
就会出现上面的错误

如果是
#include "Sdi1Doc.h"
#include "Sdi1View.h"
则就没有问题

这个错误就是CSdi1Doc没有定义
因为sdiview.h中有这个函数CSdi1Doc*GetDocument();
解决方法有两种:
1,在包含#include "Sdi1View.h"之前一定要有#include "Sdi1Doc.h"
2,在sdliview.h的前面写上:class CSdi1Doc;


要使得画的圆不会覆盖彼此 

这样的


改成这样


只用

在ondraw函数中添加这一句画

dc.SelectStockObject(NULL_BRUSH);
完整的如下

void CYuanView::OnDraw(CDC* pDC)
{
	CYuanDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	CClientDC dc(this);
	CPen pen(PS_SOLID,1,m_clr);
	dc.SelectObject(&pen);
	dc.SelectStockObject(NULL_BRUSH);//这里!
	dc.Ellipse(m_x-m_r,m_y-m_r,m_x+m_r,m_y+m_r);
	// TODO: add draw code for native data here
}


Client 对象的成员函数 Ellipse画椭圆的原理我想大概是:任何一个椭圆垂直于其极轴的四条切线唯一确定一个矩形,所以一个椭圆对应一个外接矩形,一个矩形对应一个内接椭圆


另外屏幕坐标轴的原点在窗口左上角,X轴水平向又,Y轴垂直向下


看MSDN中Ellipse中的解释



只用给出椭圆对应矩形的左上角和右下角坐标即可画出椭圆

如果要画圆 给出的圆心坐标是 x, y 半径是r  则用Ellipse画圆的话,矩形的左上角是( x - r , y - r ) 右上角的坐标是( x + r , y + r )

也可以用Arc函数或者ArcTo函数来画圆,这两个函数是画椭圆的一段圆弧的



MSDN上Arc的解释 ArCTo类似



注意start 到end是逆时钟

还有start 和 end不必再椭圆上 ,最终的圆弧式start和椭圆中心点的连线和椭圆的交点到end和椭圆中点的连线与椭圆的交点


Arc与ArcTo的区别在于,Arc函数只是绘出给定的弧线,不会对画笔位置产生影响;而ArcTo函数在工作时将会从画笔原来所在点开始绘制一条直线到弧线的开始点,绘画弧线完成后还会将画笔移动到弧线的终点,从而对画笔位置造成影响。

CColorDialog的用法,可以用它来让用户自己选择颜色

void CSetDlg::OnButtonColor() 
{
	// TODO: Add your control notification handler code here
	CColorDialog dlgColor;
	dlgColor.m_cc.rgbResult = myView->m_clr;

	dlgColor.m_cc.Flags |= CC_RGBINIT | CC_FULLOPEN;
	if(dlgColor.DoModal() == IDOK)
	{
		//myView->m_clr = dlgColor.GetColor();//两种方法都可以
		myView->m_clr = dlgColor.m_cc.rgbResult;
		myView->Invalidate();
	}
}

另外加载位图作为园的背景色有两种选择,一种是在资源管理器中自己添加或者导入一个位图,然后调用 CBitmap 中的LoadBitmap即可

	CBitmap bit;
	bit.LoadBitmap(IDB_BITMAP1);//加载位图
	CBrush brush;
	brush.CreatePatternBrush(&bit);
	dc.SelectObject(&brush);
也可以让用户通过对话框来选择一个位图加载进来

void CSetDlg::OnButtonBitmap() 
{
	// TODO: Add your control notification handler code here
	CClientDC dc(myView);
	CPen pen(PS_SOLID,1,myView->m_clr);
	dc.SelectObject(&pen);
	CBitmap bit;
	//bit.LoadBitmap(IDB_BITMAP1);//加载位图
	CFileDialog dlgFile(TRUE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"位图文件(*.bmp)|*.bmp||",this);

	if(IDOK == 	dlgFile.DoModal())
	{
		CString filePath = dlgFile.GetPathName();
		HBITMAP m_hBmp = (HBITMAP)LoadImage(NULL,filePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
		bit.Attach(m_hBmp);
		CBrush brush;
		brush.CreatePatternBrush(&bit);
		dc.SelectObject(&brush);
		int m_x,m_y,m_r;
		m_x = myView->m_x;
		m_y = myView->m_y;
		m_r = myView->m_r;
		dc.Ellipse(m_x-m_r,m_y-m_r,m_x+m_r,m_y+m_r);	
	}

}



<span style="white-space:pre"></span>







参考

VC++工程头文件重复和循环引用

MFC循环引用的问题


在MFC中获取窗口\视图句柄 &获取当前活动的CView .

得到当前窗口句柄


代码
链接: http://pan.baidu.com/s/1bn5vAaj 密码: jori



展开阅读全文

没有更多推荐了,返回首页