前言
习题来源:《计算机图形学基础教程》孔令德(第二版)
用mfc基于对话框的编程,实现下图的RGB颜色模型演示程序。点击颜色按钮能将“颜色及代码”这个组框中的静态文本框变成对应的颜色,调色板按钮可以调出自带的颜色选择对话框。滚动条和旁边的编辑框都可以调整颜色。
设计对话框过程不详述,直接开始代码和思路介绍。参考链接见文末。
改变演示块颜色
我在这里将用于演示颜色的静态文本框称为演示块,对应的ID为IDC_COLOR_BOX
。
查找了很久关于“如何修改控件颜色”的资料。
改变控件颜色需要在对话框类的OnCltColor()
成员函数里面写对应代码。要生成这个方法,需要添加WM_CTLCOLOR
这个消息的响应函数,在Class View
的对话框类上右键可以找到Add Windows Message Handler
,在这里添加就可以了。
生成的代码如下:
HBRUSH CComputerGraphcisExercise2Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: Change any attributes of the DC here
// TODO: Return a different brush if the default is not desired
return hbr;
}
这个函数会在每个控件被重绘时调用,所以将改变控件颜色的代码放在这里就行了。
参数pWnd
可以用来识别现在是哪个控件正在被重绘。
它的返回值是用于填充控件的画刷。
可以先判断是哪个控件正在被重绘,当演示块被重绘时,将它的颜色调整为自己设置的颜色。
HBRUSH CComputerGraphcisExercise2Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: Change any attributes of the DC here
if(pWnd->GetDlgCtrlID() == IDC_COLOR_BOX)//判断控件
{
hbr=CreateSolidBrush(m_color);//调整颜色
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
上面代码的m_color
是一个对话框类的protected
变量,我把它和自动生成的m_hIcon
放在了一起。
注意:如果这个变量被设置为public
,就会在运行时产生错误,原因未知。
此变量在对话框类的初始化函数OnInitDialog()
内初始化。
有了以上代码之后,想要改变演示块的颜色,只需要改变m_color
的值并刷新对话框(例如使用Invalidate()
)就可以了。
显示颜色代码
在演示块下面有一个静态文本框用于显示当前颜色的十六进制代码,例如“#ffffff”。
由于颜色每次都是在对话框刷新的时候被改变的,可以将这个功能写在OnPaint()
内。获取方式也不难,看代码基本能看懂,不赘述。
void CComputerGraphcisExercise2Dlg::OnPaint()
{
//其他代码
//获取颜色代码
CStatic *color_code = (CStatic*)GetDlgItem(IDC_COLOR_CODE);
CString color;
color.Format("#%02x%02x%02x",GetRValue(m_color),GetGValue(m_color),GetBValue(m_color));
color_code ->SetWindowText(color);
}
实现颜色按钮
双击每个颜色按钮,添加它们的响应事件,例如:
void CComputerGraphcisExercise2Dlg::OnButtonRed()
{
// TODO: Add your control notification handler code here
m_color = RGB(255,0,0);
Invalidate();
}
调整它们的颜色,并进行刷新重绘。
至于调色板按钮,需要使用mfc内置的颜色对话框CColorDlg
:
void CComputerGraphcisExercise2Dlg::OnButtonPalette()
{
// TODO: Add your control notification handler code here
CColorDialog palette;
int nResponse = palette.DoModal();
if(nResponse == IDOK)
{
m_color = palette.GetColor();//获取调色板的颜色
}
Invalidate();
}
实现滚动条
初始化滚动条
首先需要在对话框的OnInitDialog()
方法内,新增初始化滚动条范围值的代码。
BOOL CComputerGraphcisExercise2Dlg::OnInitDialog()
{
//其它代码
//...
CScrollBar *scroll=(CScrollBar*)GetDlgItem(IDC_SCROLLBAR_R);
scroll->SetScrollRange(0,255);
scroll = (CScrollBar*)GetDlgItem(IDC_SCROLLBAR_G);
scroll->SetScrollRange(0,255);
scroll = (CScrollBar*)GetDlgItem(IDC_SCROLLBAR_B);
scroll->SetScrollRange(0,255);
return TRUE; // return TRUE unless you set the focus to a control
}
这段代码初始化了三个滚动条控件,首先用GetDlgItem()
来获取ID对应的控件对象的指针,然后调用SetScrollRange()
来设定其范围为0~255。
响应滚动条事件
滚动条的响应事件不像按钮一样是每个按钮分开的,而是分为水平滚动条事件响应函数,和垂直滚动条响应函数。
在Class View
里对对话框类右键,在右键菜单中找到Add Windows Message Handler
,添加WM_HSCROLL
消息的响应函数(如果是垂直滚动条,应该是WM_VSCROLL
消息)。
生成的响应函数是这样的:
void CComputerGraphcisExercise2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
没看文档,不过参数大概意思可能是:
- nSBCode:滚动条响应的消息类型
- nPos:滚动条改变状态之后的值
- pScrollBar:指向被改变状态的滚动条控件的指针
滚动条拖动的代码需要自己写,在实现功能之前,你即使用鼠标拖动滑块,滑块也会回到原来的位置。
在这个响应函数里面,我只让滚动条改变对应的编辑框对应的数值。
void CComputerGraphcisExercise2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
int pos = pScrollBar->GetScrollPos();//获取当前位置
switch(nSBCode)
{
case SB_THUMBPOSITION://被拖动
pos = nPos;
break;
//其实这里还可以写别的事件响应,丰富功能
}
pScrollBar->SetScrollPos(pos);
//设置与滚动条对应的编辑框的数值
switch(pScrollBar->GetDlgCtrlID())
{
case IDC_SCROLLBAR_R:
SetDlgItemInt(IDC_EDIT_R,pos);
break;
case IDC_SCROLLBAR_G:
SetDlgItemInt(IDC_EDIT_G,pos);
break;
case IDC_SCROLLBAR_B:
SetDlgItemInt(IDC_EDIT_B,pos);
break;
}
}
响应编辑框变化事件
现在已经可以滑动滚动条来修改编辑框内的值了,但演示块的颜色还不会改变,我把这个功能写在编辑框里面了,这样,可以顺便实现“在编辑框内修改值来修改颜色”的功能。
这是其中一个编辑框的响应函数代码,其他两个类似,要说的内容都写在注释里面了。
void CComputerGraphcisExercise2Dlg::OnChangeEditR()
{
// 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
UpdateData();//更新数据,将数据从控件上同步到绑定的变量
int pos = atoi(m_R_value.GetBuffer(0));
((CScrollBar*)GetDlgItem(IDC_SCROLLBAR_R))->SetScrollPos(pos);
//根据滚动条位置设置当前颜色值
int R=0,G=0,B=0;
R=((CScrollBar*)GetDlgItem(IDC_SCROLLBAR_R))->GetScrollPos();
G=((CScrollBar*)GetDlgItem(IDC_SCROLLBAR_G))->GetScrollPos();
B=((CScrollBar*)GetDlgItem(IDC_SCROLLBAR_B))->GetScrollPos();
m_color = RGB(R,G,B);
//为了防止整个对话框闪烁,只刷新演示块
CRect rect;
((CStatic*)GetDlgItem(IDC_COLOR_BOX))->GetWindowRect(&rect);
ScreenToClient(&rect);//转换为对话框上的客户坐标
InvalidateRect(rect);//只刷新控件位置
}
完成这一步之后,已经能够实现使用滚动条或者编辑框来改变颜色了,但是当你在点击颜色按钮时,虽然颜色改变了,但是滚动条的位置和编辑框的值不会随之改变。
因此还需要一步:
滚动条随颜色而变化位置
这个对话框内只有颜色按钮能够改变颜色,所以简单地在所有颜色按钮的代码内添加改变位置的代码即可。
而改变滚动条的位置只需要改变对应的编辑框的数值就可以了。
于是颜色按钮代码变成了这样
void CComputerGraphcisExercise2Dlg::OnButtonRed()
{
// TODO: Add your control notification handler code here
m_color = RGB(255,0,0);
//调整滚动条位置
int R=0,G=0,B=0;
R=GetRValue(m_color);
G=GetGValue(m_color);
B=GetBValue(m_color);
SetDlgItemInt(IDC_EDIT_R,R);
SetDlgItemInt(IDC_EDIT_G,G);
SetDlgItemInt(IDC_EDIT_B,B);
Invalidate();
}
部分用到的MFC函数或宏的简介
详细的自己百度
GetRValue()
,GetGValue()
,GetBValue()
,分别用于获取颜色值的RGB三个通道的值SetDlgItemInt()
,可以将值送入ID对应的控件GetDlgItem()
,通过ID来获取指向控件的指针,记得转换指针类型Invalidate()
,使客户区无效化,引起重绘