要想这种改变反应到示例这个组框当中,也就是说我们就需要捕获用户它的这种改变这一事件,当用户在这种编辑框上面对文本进行改变的时候会发送一个EN_CHANGE这个通告消息。那我们只需要捕获EN_CHANGE这个消息,在消息响应函数当中,我们去通知这个组框,让它反应出这种改变就可以了。
任务:图形的绘制,设置对话框,颜色对话框和字体对话框的创建以及如何在一个窗口当中显示一幅位图。
1.我们先添加一个菜单,在这个菜单当中,我们添加四个菜单项,分别是点、直线、矩形、椭圆。当用户选择其中一个菜单项的时候,我们在随后的图形绘制过程当中,按照用户的选择去绘制这个图形。
先创建菜单项:
绘画
点
直线
矩形
椭圆
然后响应这四个函数:
(1)在CGraphicView.cpp中添加成员变量:
private:
UINTm_nDrawType;
初始化:
CGraphicView::CGraphicView()
{
//TODO: add construction code here
m_nDrawType=0;
}
(2)响应这四个函数:
①线宽:
//当用户点击其中一个菜单项的时候,我们应该将用户的选择保存起来
//这样在随后的图形绘制过程当中,我们就可以根据用户的选择然后去
//绘制这个图形。为了保存用户的选择,我们可以先添加成员变量。
void CGraphicView::OnDot()
{
//TODO: Add your command handler code here
m_nDrawType=1;
}
void CGraphicView::OnLine()
{
//TODO: Add your command handler code here
m_nDrawType=2;
}
void CGraphicView::OnRectangle()
{
//TODO: Add your command handler code here
m_nDrawType=3;
}
void CGraphicView::OnEllipse()
{
//TODO: Add your command handler code here
m_nDrawType=4;
}
线形:
(3)再在CGraphicView.cpp中添加成员变量
CPointm_ptOrigin;
在构造函数中初始化:
//初始化为0,也就是将原点设置成(0,0)这个点。
m_ptOrigin=0;
void CGraphicView::OnLButtonDown(UINTnFlags, CPoint point)
{
//TODO: Add your message handler code here and/or call default
//当我们按下鼠标左键的时候可以把这个点保存下来,
//当鼠标左键按下的时候,将这个点保存到成员变量当中
m_ptOrigin=point;
CView::OnLButtonDown(nFlags,point);
}
void CGraphicView::OnLButtonUp(UINT nFlags,CPoint point)
{
//TODO: Add your message handler code here and/or call default
//当鼠标左键松开的时候,另外一个点就有了,这样我们就可以作图了
//要做图的话,首先要有DC
CClientDCdc(this);
//创建画笔
// CPenpen(PS_SOLID,1,RGB(255,0,0));//方法一
//我们创建画笔的时候,这个宽度就可以用这个变量来代替了
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);
//然后将这个画笔选到设备描述表当中
dc.SelectObject(&pen);
//创建一个透明的画刷
//FromHandle它可以转换一个画刷的句柄,返回一个CBrush指针
CBrush*pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//将这个画刷选择到这个设备描述表当中
dc.SelectObject(pBrush);
switch(m_nDrawType)
{
case1:
dc.SetPixel(point,m_clr);
break;
case2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case3:
//CRect重载了一个操作符叫:operator LPCRECT,转换一个CRect到一个LPCRECT
//当我们在给Rectangle这个函数赋值的时候,当发现这是一个对象,那么它就会
//隐式的去调用LPCRECT,将一个CRect转换为一个LPCRECT。
//所以我们有时候看到这个函数传参的时候,如果你传进去的数值和它所需要的
//数值的类型不匹配的情况下:
//有的情况是有的类型之间本来就可以互相转换,如short和int类型
//如果是对象的话,你就要考虑:它是选择了一个对象的构造方法做了一个隐式
//的转换或者是有其他的重载的操作符。
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case4:
dc.Ellipse(CRect(m_ptOrigin,point));
break;
}
CView::OnLButtonUp(nFlags,point);
}
(4)添加一个“设置”菜单项:
在这个CView类当中,要让这个设置对话框显示出来,我们要去保护这个设置对话框所包含的类的头文件
#include “SettingDlg.h”
添加一个成员变量:
private:
UINTm_nLineWidth;
初始化:
m_nLineWidth=0;
//设置
添加一个成员变量:
Private:
intm_nLineStyle;
初始化:
m_nLineStyle=0;
void CGraphicView::OnSetting()
{
//TODO: Add your command handler code here
CSettingDlgdlg;
//我们再一次进入这个对话框的时候,我们应该可以看到先前的设置
//在对话框当中,这个变量的值就是我们在View类中所保存的值
//第一次调用的时候都是0,下一次调用的时候就是先前所保存的值
dlg.m_nLineWidth=m_nLineWidth;
dlg.m_nLineStyle=m_nLineStyle;
dlg.DoModal();
//我们就要获取用户所输入的线宽值
//如何保存这个线宽,我们要增加一个成员变量
if(IDOK==dlg.DoModal())
{
//当用户点击了确定按钮之后将它保存起来
//线宽
m_nLineWidth=dlg.m_nLineWidth;
//线形
m_nLineStyle=dlg.m_nLineStyle;
}
}
(5)创建颜色对话框:
创建一个成员变量:
private:
COLORREFm_clr;
初始化:
m_clr=RGB(255,0,0);
//颜色对话框
void CGraphicView::OnColor()
{
//TODO: Add your command handler code here
CColorDialogdlg;
//Flags应该是有一个初始的或者说是缺损的设置,当我们
//把这个标记CC_RGBINIT赋给Flags时,就是把先前的标记
//给覆盖了。
//这里我们可以加一个或标记“|”,将CC_RGBINIT这个标记
//和它先前的标记组合起来。
dlg.m_cc.Flags|=CC_RGBINIT| CC_FULLOPEN;
dlg.m_cc.rgbResult=m_clr;
// dlg.DoModal();
//我们就应该将用户选择的颜色保存下来,怎么获取用户的颜色?
if(IDOK==dlg.DoModal())
{
m_clr=dlg.m_cc.rgbResult;
}
}
(6)创建字体对话框:
添加成员变量,主要是保存字体的名字:
private:
CStringm_strFontName;
初始化:
m_strFontName="";
要保存字体可以增加一个成员变量:
private:
CFontm_font;
//字体
void CGraphicView::OnFont()
{
//TODO: Add your command handler code here
CFontDialogdlg;
// dlg.DoModal();
//完成功能:用户选择一种字体之后,我们将这种字体保存下来
//然后我们将字体的名字在View窗口中用用户所选择的字体显示出来
if(IDOK==dlg.DoModal())
{
//当第一次选择一种字体之后呢CFont对象m_font它已经和字体
//对象关联起来了,当你再次进入的时候就产生了非法操作
//我们这里要判断一下,如果我们这种字体对象已经和一种字体
//资源相关联了,就应该切断它们之间的联系,然后释放这种字体资源
//用句柄来判断,先释放一种字体资源,再与另一种字体资源想关联
if(m_font.m_hObject)
m_font.DeleteObject();
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);
m_strFontName=dlg.m_cf.lpLogFont->lfFaceName;
//然后我们要在CView类窗口当中将这个窗口的名字用m_font这种字体输出
//那么这个操作我们把它放在OnDraw()函数当中,
//这里我们只需要调用一个Invalidate(),使窗口无效,这样当下一次
//wPen消息发送的时候,窗口就用进行重绘
Invalidate();
}
}
(7)示例的创建:
将用户的这种改变通知给示例这个组框:
①线宽的编辑框:
void CSettingDlg::OnChangeLineWidth()
{
//TODO: If this is a RICHEDIT control, the control will not
//send this notification unless you override the CDialog::OnInitDialog()
//function and call CRichEditCtrl().SetEventMask()
//with the ENM_CHANGE flag ORed into the mask.
//TODO: Add your control notification handler code here
//我们可以在这四个函数当中调用Invalidate(),使窗口无效,这样的话
//当下一次WM_PAINT消息发送的时候,那么这个窗口就要进行重绘,那么我
//们只需要去效应WM_PAINT消息的消息响应函数当中编写一处代码就可以了
Invalidate();
}
②线形单选按钮:
//还要将这三个单选按钮设置成一组。
//将单选按钮关联成员变量:
Member variable name: m_nLineStyle;
Category: Value;
Variable type: int;
void CSettingDlg::OnRadio1()
{
//TODO: Add your control notification handler code here
Invalidate();
}
void CSettingDlg::OnRadio2()
{
//TODO: Add your control notification handler code here
Invalidate();
}
void CSettingDlg::OnRadio3()
{
//TODO: Add your control notification handler code here
Invalidate();
}
③让颜色的改变也在示例中显示:
添加一个成员变量:
public:
COLORREFm_clr;
我们将它设置成共有的,因为我们在View中要去访问这个成员变量
在CSettingDlg::CSettingDlg将这个变量进行初始化:
m_clr=RGB(255,0,0);
在CGraphicView::OnSetting中添加第四行代码:
void CGraphicView::OnSetting()
{
//TODO: Add your command handler code here
CSettingDlgdlg;
//我们再一次进入这个对话框的时候,我们应该可以看到先前的设置
//在对话框当中,这个变量的值就是我们在View类中所保存的值
//第一次调用的时候都是0,下一次调用的时候就是先前所保存的值
dlg.m_nLineWidth=m_nLineWidth;
dlg.m_nLineStyle=m_nLineStyle;
//让颜色的改变也在示例中显示:
//我们将在View中所保存的颜色值传递给它
//因为我们点击颜色的时候会弹出颜色对话框,当用户选择一种颜色
//之后,这个颜色就保存到了CGraphicView的成员变量当中,当你再
//一次去显示这个设置对话框的时候,我们将在View类当中所保存的
//成员变量的值给它传进去。
dlg.m_clr=m_clr;
dlg.DoModal();
//我们就要获取用户所输入的线宽值
//如何保存这个线宽,我们要增加一个成员变量
if(IDOK==dlg.DoModal())
{
//当用户点击了确定按钮之后将它保存起来
//线宽
m_nLineWidth=dlg.m_nLineWidth;
//线形
m_nLineStyle=dlg.m_nLineStyle;
}
}
void CSettingDlg::OnPaint()
{
CPaintDCdc(this); // device context for painting
//TODO: Add your message handler code here
//改变线宽和线形的值时,线条没有发生变化?
//当一个控件它和一个成员变量相关联的时候,如果说,你想要让这个控件
//上面的值能够反映到成员变量当中,那么需要去调用一个方法UpdateData()
//当初始化的时候Dialogue会自动去调用这个UpdateData(),传递一个参数
//当点击OK的时候,对话框也会去调用UpdateData()
//但是在这个过程当中你想让这个控件上的值反映到成员变量当中,你就必须去
//调用这个UpdateData(),让它们之间进行数据交换
UpdateData();
//在这个里面我们就可以完成线条的绘制
//首先应该去创建这个画笔,按照用户所指定的线长及线宽去创建这个画笔
CPen pen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));
//然后将它选择到设备描述表当中
dc.SelectObject(&pen);
//要画这个线条,首先要知道这个组框它的矩形区域的范围
//这样我们在这个组框的线条范围内去画这个线条
CRectrect;
//获取这个组框的窗口指针,然后去调用获取它的窗口矩形区域大小
//GetWindowRect获取的是屏幕坐标
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
//在得到窗口矩形大小之后可以用SCreenToClient做一个转换
SCreenToClient(rect);
//得到这个区域就可以作图了
//这样就是相当于将原点移动到组框的左边的靠中间的位置
dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);
dc.LineTo(rect.right-20,rect.top+rect.Height()/2);
//Do not call CDialog::OnPaint() for painting messages
}
(8)如何去改变对话框和对话框上面控件的背景色以及控件的文字颜色?
增加Windows消息处理,选择WM_CTLCOLOR
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC,CWnd* pWnd, UINT nCtlColor)
{
HBRUSHhbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
//TODO: Change any attributes of the DC here
//如果想控制某一个控件的背景改变?
//用nCtlColor我们只能当前绘制的是哪一类控件,如果我们要精确
//控制某一个控件,需要用函数:GetDlgCtrlID.
//现在绘制的是线形这个组框,改变它的背景色
if(pWnd->GetDlgCtrlID()==IDC_LINE_STYLE)
{
//改变文本的颜色,此时设置的是组框的文本颜色
pDC->SetTextColor(RGB(255,0,0));
//将文字设置成透明的
pDC->SetBkColor(TRANSPARENT);
//返回一个指定的画刷
returnm_brush;
}
if(pWnd->GetDlgCtrlID()==IDC_LINE_WIDTH)
{
//改变文本的颜色,此时设置的是组框的文本颜色
pDC->SetTextColor(RGB(255,0,0));
//将文字设置成透明的
pDC->SetBkColor(TRANSPARENT);
//返回一个指定的画刷
returnm_brush;
}
//静态文本框中的字体按照我们所指定的字体显示
//然后我们判断一下当前绘制的是不是静态文本框
if(pWnd->GetDlgCtrlID()==IDC_TEXT)
{
pDC->SelectObject(&m_font);
}
/*
//改变OK按钮它的背景色和文字的颜色
if(pWnd->GetDlgCtrlID()==IDOK)
{
pDC->SetTextColor(RGB(255,0,0));
return m_brush;
}*/
//TODO: Return a different brush if the default is not desired
//对于其他的控件我们依然用原来的画刷,此时将returnm_brush;注释起来
returnhbr;
//然后在返回的时候我们返回自己的画刷
// returnm_brush;
}
在CSettingDlg中增加一个成员变量:
private:
CBrushm_brush;
在构造函数中进行初始化:
m_brush.CreateSolidBrush(RGB(0,0,255));
在CSettingDlg中增加一个成员变量:
private:
CFontm_font;
在构造函数中进行初始化:
m_font.CreatePointFont(200,"华文行楷");
我们应该先创建一个Button类,然后将CSettingDlg这个对话框上面的按钮控件和这个类的对象相关联。
插入一个新的类
Name:CTestBtn;
Base class:CButton;
在CTestBtn中增加一个虚函数:DrawItem;
void CTestBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your codeto draw the specified item
UINTuStyle=DFCS_BUTTONPUSH;
ASSERT(lpDrawItemStruct->CtlType==ODT_BUTTON);
if(lpDrawItemStruct->itemState& ODS_SELECTED)
uStyle|=DFCS_PUSHED;
::DrawFrameControl(lpDrawItemStruct->hDC,&lpDrawItemStruct->rcItem,
DFC_BUTTON,uStyle);
CString strText;
GetWindowText(strText);
COLORREFcrOldColor=::SetTextColor(lpDrawItemStruct->hDC,RGB(255,0,0));
::DrawText(lpDrawItemStruct->hDC,strText,strText.GetLength(),
&lpDrawItemStruct->rcItem,DT_SINGLELINE|DT_VCENTER|DT_CENTER);
::SetTextColor(lpDrawItemStruct->hDC,crOldColor);
}
接下来我们将对话框中的OK按钮关联一个成员变量:
Member variable name:m_btnTest;
Variable type:CTestBtn;
然后在CSettingDlg中包含一个头文件:#include "TestDlg.h"
在OK按钮上点右键,选择属性->Style->Owner draw勾选上
运行之后,我们可以看到OK按钮的文字颜色改变了。
(9)如何在一个窗口当中显示一幅位图?
OnEraseBkgnd这个是在窗口要擦除的时候响应的消息:
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add yourmessage handler code here and/or call default
//首先去创建一个位图
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
//定义一个BITMAP结构体这样的一个变量,然后利用CBitmap对象
//调用GetBitmap传递&bmp地址
BITMAP bmp;
bitmap.GetBitmap(&bmp);
CDC dcCompatible;
dcCompatible.CreateCompatibleDC(pDC);
dcCompatible.SelectObject(&bitmap);
CRect rect;
GetClientRect(&rect);
// pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY);
//StretchBlt可以将这个位图拉伸或者压缩,来填充这个目的矩形
pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,
bmp.bmWidth,bmp.bmHeight,SRCCOPY);
return TRUE;
//不能擦出
// return CView::OnEraseBkgnd(pDC);
}
将CGraphicView::OnEraseBkgnd中的代码移动到CGraphicView::OnDraw中结果是一样的。但是效果是不一样的,当改变窗口尺寸的时候,窗口有一些闪烁。这是因为当窗口尺寸发生变化的时候,引起窗口的重绘,首先它要擦除背景,然后砸在OnDraw函数当中再重新贴上这幅位图,所以有一些闪烁。而在OnEraseBkgnd中去完成,闪烁是比较小的,因为我们没有去擦除背景。
总结:
介绍了图形的绘制,设置对话框,颜色对话框,字体对话框的创建,还有示例功能的增加,我们如何去改变一个对话框的背景色,控件的背景色,控件文字显示的颜色,文字的字体,还介绍了如何去贴一幅图片,也就是说如何在窗口当中去显示一幅位图。