提要:
图形的绘制,如何使用自定义画笔(颜色,线宽,线形)。
如何为程序中添加选项菜单和选项设置对话框,
如何使用标准颜色对话框,
如何使用字体对话框,在选项对话框中实现预览功能。
实现选项对话框和窗口类中的数据交换。
如何改变对话框和控件的背景色,
如何改变控件的文本颜色,对按钮控件的特殊处理。
如何在窗口中显示一幅位图。
一、课程内容 (例程 Graphic 基于 Single Doc)
1、菜单上设置绘图方式(点、矩形、椭圆),由View类处理这些菜单命令的消息,设置成员变量 UINT m_nDrawType; 用来保存当前用户选取的绘图方式
2、在View类中捕捉WM_LBUTTONDOWN和WM_LBUTTONUP两个鼠标消息,用来设定画线、画椭圆等时所需要的两个点。加入成员变量CPoint m_ptOrigin;
迎来表示绘图的起点。
鼠标按下时,取得绘图起始点
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_ptOrigin=point;
CView::OnLButtonDown(nFlags, point);
}
然后在WM_LBUTTONUP鼠标消息中绘图
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TOD Add your message handler code here and/or call default
CClientDC dc(this);
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);//设定画笔,绘画的画笔颜色
dc.SelectObject(&pen);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//设定透明画刷,使得绘图中心面积为透明的
dc.SelectObject(pBrush);
switch(m_nDrawType)
{
case 1:
dc.SetPixel(point,m_clr);//画点
break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point); //画线
break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point));//画矩形
break;
case 4:
dc.Ellipse(CRect(m_ptOrigin,point));//椭圆
break;
}
CView::OnLButtonUp(nFlags, point);
}
3、设置绘图Option对话框(IDD_DLG_SETTING)对应的类为:CSettingDlg,可以设置一些绘图参数(16:40开始),由菜单IDM_SETTING"设置"来关联
对应于每个设置项,设定一个Dialog类中的成员变量,同时在View类中也要加入同名的成员变量,然后从对话框中得到设置值赋予给View类的成员变量中
然后依此决定绘图行为
4、颜色对话框(37:40开始)的创建
利用MFC现成的CColorDialog类.增加菜单IDM_COLOR来相应颜色对话框的弹出
void CGraphicView::OnColor()
{
CColorDialog dlg;
dlg.m_cc.Flags|=CC_RGBINIT | CC_FULLOPEN;//m_cc为CColorDialog的一个成员变量,类型是CHOOSECOLOR结构体
//只有设定CC_RGBINIT的Flag才能使颜色不总是为默认色,CC_FULLOPEN表示展开颜色对话框
dlg.m_cc.rgbResult=m_clr; //rgbResult为m_cc的一个COLORREF类型的成员,返回用户在颜色对话框中选定的颜色
if(IDOK==dlg.DoModal())
{
m_clr=dlg.m_cc.rgbResult; //m_clr为View类的一个COLORREF类型的成员变量,用来保存用户的颜色选择
}
}
5、字体对话框(50:50开始)的创建
利用MFC现成的CFontDialog类.增加菜单IDM_FONT来相应字体对话框的弹出
void CGraphicView::OnFont()
{
CFontDialog dlg;
if(IDOK==dlg.DoModal())
{
if(m_font.m_hObject) //m_font为View类的CFont 类型的成员变量,m_hObject为CGdiObject对象指向一个WindowsGDI资源的句柄
//这句用来判断CFont对象是否和一个Window GDI对象关联,如果已经关联则不能再次调用CreateFontIndirect,只能先用DeleteObject,断开关联
m_font.DeleteObject();
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);//m_cf为 CFontDialog的一个成员变量,类型是CHOOSEFONT结构体
//这句表示从对话框返回的m_cf结构体的lpLogFont上生成字体对象
m_strFontName=dlg.m_cf.lpLogFont->lfFaceName;//m_strFontName为View类的CString类型的成员变量,用来存放字体名称
Invalidate();//这句用来使窗体重绘,于是调用View类的OnDraw函数,使新字体生效,这样需要在OnDraw函数中:pDC->SelectObject(&m_font);
}
}
6、示例功能(01:06:10开始),指选定字体后在一个框中即时显示字体的样子,这里绘图Option对话框中选定一个线条类型和线宽后,即时显示线条的样子
在对话框中的Edit控件和radio按钮控件中都调用Invalidate()函数,使得窗体重绘(WM_PAINT消息相应函数),在重绘中如下即时显示示例,这样代码比较简捷,只编写一处
void CSettingDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
UpdateData();//即时将对话框中的控件的值反应到成员变量中
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);
dc.SelectObject(&pen);
CRect rect;
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);//得到示例外边框(IDC_SAMPLE)的矩形区域,此处为屏幕坐标,需要用下面函数转换为客户区坐标
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
}
7、如何改变对话框和控件的背景色及控件的字体颜色(01:22:00开始)
对话框捕获消息WM_CTLCOLOR,在其响应函数CWnd::OnCtlColor
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if(pWnd->GetDlgCtrlID()==IDC_LINE_STYLE)//获得字窗口的ID值,对于控件可以得到其ID
{
pDC->SetTextColor(RGB(255,0,0));//设置控件的字体颜色
pDC->SetBkMode(TRANSPARENT); //设置控件文字的背景模式为透明
return m_brush;//对话框成员变量 CBrush m_brush;,需要构造函数中初始化 m_brush.CreateSolidBrush(RGB(0,0,255));
}
if(pWnd->GetDlgCtrlID()==IDC_LINE_WIDTH)
{
pDC->SetTextColor(RGB(255,0,0));//设置Edit控件的字体颜色
//pDC->SetBkMode(TRANSPARENT);
pDC->SetBkColor(RGB(0,0,255));//设置Edit控件的背景颜色
return m_brush;
}
if(pWnd->GetDlgCtrlID()==IDC_TEXT) //static静态文本控件
{
pDC->SelectObject(&m_font);//对话框成员变量 CFont m_font,需要构造函数中初始化m_font.CreatePointFont(200,"华文行楷");
}
/*if(pWnd->GetDlgCtrlID()==IDOK) //OK按钮控件,在OnCtlColor函数中无法修改按钮的颜色等
{
pDC->SetTextColor(RGB(255,0,0));
return m_brush;
}*/
return hbr;//返回HBRUSH,针对所有对话框中的控件起作用,上面每次return m_brush只对单个控件起作用
//return m_brush;
}
在OnCtlColor函数中无法修改按钮的颜色等GDI类型,需要自己建立一个从CButton类派生的按钮类(CTestBtn 做为对话框类的成员变量),
然后改写CTestBtn的虚函数DrawItem(代码拷贝自MSDN例子)
void CTestBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
UINT uStyle = DFCS_BUTTONPUSH;
// This code only works with buttons.
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
// If drawing selected, add the pushed style to DrawFrameControl.
if (lpDrawItemStruct->itemState & ODS_SELECTED)
uStyle |= DFCS_PUSHED;
// Draw the button frame.
::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem,
DFC_BUTTON, uStyle);
// Get the button's text.
CString strText;
GetWindowText(strText);
// Draw the button text using the text color red.
COLORREF crOldColor = ::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按钮关联到一个成员变量m_btnTest,其类型为CTestBtn
还需要将对话框中的OK按钮属性中加上Owner draw的Style
可以参考两个按钮类SXBtn.cpp,BtnST.cpp,
演示Cancel按钮关联到m_BtnCancel成员变量(CSXBtn类的对象),
演示ButtonST按钮关联到m_btnSTl成员变量(CButtonST类的对象),注意:对于CButtonST类的使用不需要将按钮加上Owner draw的Style
在WM_INITDIALOG消息中处理
BOOL CSettingDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_btnST.SetActiveBgColor(RGB(0,0,255));//设置按钮活动背景
m_btnST.SetActiveFgColor(RGB(255,0,0));//设置按钮活动文字颜色
m_btnST.SetInactiveBgColor(RGB(255,0,255));//设置按钮非活动背景
m_btnST.SetInactiveFgColor(RGB(255,255,0));//设置按钮非活动文字颜色
return TRUE; // return TRUE unless you set the focus to a control
}
8、在窗口中显示一幅位图(01:58:10开始)
4个步骤:
1、创建位图
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
2、创建兼容DC
CDC dcCompatible;
dcCompatible.CreateCompatibleDC(pDC);
3、将位图选到兼容DC中
dcCompatible.SelectObject(&bitmap);
4、将兼容DC中的位图贴到当前DC中。
pDC->BitBlt(rect.left,rect.top,rect.Width(),
rect.Height(),&dcCompatible,0,0,SRCCOPY);
可以在void CGraphicView::OnDraw(CDC* pDC)中处理(会出现闪烁-每次都要擦除后重绘造成的)
或者在 WM_ERASEBKGND 消息对应函数BOOL CGraphicView::OnEraseBkgnd(CDC* pDC) 中处理
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC)
{
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
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);//BitBlt拷贝的位图不改变大小
pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&dcCompatible, //StretchBlt拷贝的位图或者拉抻或者压缩,使之与目的DC一样大
0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY); //注意这里bmp用的是BITMAP结构变量
return TRUE;
// return CView::OnEraseBkgnd(pDC);
}
二、知识点
1、新加的类无法在ClassWizard中出现
删除*.clw文件重新生成即可
2、对于多个Radio单选按钮,有一个设置为Group属性,则只到下一个Group属性的按钮出现,否则所有的按钮都属于同一组,
可以只针对具有Group属性的按钮关联一个成员变量,所有的按钮都对应着个变量,其值从0开始递增(Group属性的按钮值为0)
3、为何在上面 dlg.m_cc.Flags|=CC_RGBINIT 中用 dlg.m_cc.Flags=CC_RGBINIT 会报内存错
这是因为只用CC_RGBINIT不足以满足颜色对话框的初始要求,又要用到CC_RGBINIT这个Flag完成所需功能,于是可以用|逻辑符来实现
4、如何得到控件的外围举行区域RECT
CRect rect;
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);//得到'示例'外边框(IDC_SAMPLE)的矩形区域,此处rect为屏幕坐标,需要用下面函数转换为客户区坐标
ScreenToClient(&rect);
5、如何通过CWnd值得到控件的ID
if(pWnd->GetDlgCtrlID()==IDC_LINE_STYLE)//GetDlgCtrlID函数用来获得字窗口的ID值,对于控件可以得到其ID
6、如何设置DC的背景模式
pDC->SetBkMode(TRANSPARENT); //设置背景模式为透明