在对话框中设置一个示例区,当用户选择不同线型时,在示例区能看到。
首先在对话框中设置一个组框,Caption为组框,因为要对组框进行操作,将其ID设置为IDC_SAMPLE。
当用户对编辑框上面的文本进行改变时,会向对话框发送一个EN_CHANGE通知消息;同样地,当用户单击单选按钮时,该按钮会向对话框发送BN_CLICKED消息。
对CSettingDlg类添加编辑框控件EN_CHANGE消息响应函数,三个单选按钮的BN_CLICKED消息响应函数。
在四个添加的消息响应函数中调用Invalidate函数让窗口无效,发送WM_PAINT消息,并在CSettingDlg类消息响应函数OnPaint中完成示例线条的绘制。
void CSettingDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CPen pen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));
dc.SelectObject(&pen);
CRect rect;
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&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
}
运行发现并没有直线在示例框中出现,但是将对话框移动到屏幕左上角时,就看到直线了,原因是GetWindowRect函数收到的是屏幕坐标,而我们在绘图时是以对话框左上角为原点的坐标。这时候在得到示例组框矩形大小之后,应该将其由屏幕坐标转换成客户坐标。
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);后面添加:
ScreenToClient(&rect);
运行结果正常,如图。
但是改变线宽,示例中的线条并没有发生变化。当一个控件与成员变量相关联的时候,要想让控件的值立刻反映到成员变量上,
需要在CPen pen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));之前添加:UpdateData();
如果用户选择的颜色也需要反映到示例线条上时,先给对话框类添加一个公有的COLORREF类型的成员变量:m_clr,公有的保证视类中可以访问此变量,并在该类的构造函数中将其初始化为红色。
m_clr=RGB(255,0,0);
接下来在视类OnSetting函数中Domodal函数调用之前,添加:
dlg.m_clr=m_clr;将用户选择的颜色保存到对话框成员变量中。
接下来将对话框类的语句修改为:
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);
运行结果符合要求。如图。
10.6改变对话框和控件的背景及文本颜色
首先介绍一条消息:WM_CTLCOLOR,它的响应函数为CWnd类的OnCtlColor。
afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor );
第一个参数是要绘制控件的显示上下文指针。
第二参数是要绘制控件的指针;第三个参数是要绘制的控件的类型。
该函数返回绘制控件背景的画刷句柄。当一个子控件将要绘制这时,它会向它的父窗口发送一个WM_CTLCOLOR消息来准备一个上下文,以使用正确的颜色绘制该控件。在OnCtlColor函数中改变控件文本的颜色是通过SetTextColor函数来实现。
10.6.1改变整个对话框及其子控件的背景色
为对话框类添加WM_CTLCOLOR消息响应函数:
HBRUSH CSettingDlg::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;
}
在这个函数中,首先调用基类的OnCtlColor函数,接收返回一个画刷句柄,直接返回该句柄,利用这个画刷句柄就可以绘制对话框以及子控件的背景。
因此如果想要改变对话框的背景,只需要自定义一个画刷,然后让OnCtlColor函数返回这个画刷就可以了。
首先为对话框类添加一个CBrush类型私有成员变量:m_brush,并在其构造函数中利用CreateSolidBrush函数将该画刷初始化为一个蓝色画刷。
m_brush.CreateSolidBrush(RGB(0,0,255));
然后将OnCtlColor函数中return hbr;修改为:return m_brush;
运行结果如图。子控件和对话框的背景都是蓝色,但是两个按钮的背景没有改变。
10.6.2仅改变某个子控件的背景以及文本颜色
OnCtlColor函数第二个参数:pWnd能够知道当前绘制的控件窗口对象。通过调用CWnd类GetDlgCtrlID成员函数就可以得到该控件的ID。
HBRUSH CSettingDlg::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
if(IDC_LINE_STYLE==pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255,0,0));
return m_brush;
}
return hbr;
}
当程序背景绘制到线型组框的时候,我们让它背景变蓝,将文本颜色设置为红色。运行如图。
因为控件文本本身也有背景色,所以将控件文本背景色设置为透明的。在pDC->SetTextColor(RGB(255,0,0));后面添加:
pDC->SetBKMode(TRANSPARENT);
运行结果如图。
下面来实现编辑框控件背景的改变:
HBRUSH CSettingDlg::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
if(IDC_LINE_STYLE==pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255,0,0));
pDC->SetBkMode(TRANSPARENT);
return m_brush;
}
if(IDC_LINE_WIDTH==pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255,0,0));
pDC->SetBkMode(TRANSPARENT);
return m_brush;
}
return hbr;
}
运行结果如图。
10.6.3改变控件上的文本字体
为对话框资源添加一个静态文本控件,Caption为程序员,ID为IDC_TEXT。
为对话框类添加一个CFont类型私有成员变量:m_font,在构造函数中进行初始化:
m_font.CreatePointFont(200,“华文行楷”);
在OnCtlColor函数中,将新建的字体选入设备描述表:
if(IDC_TEXT==pWnd->GetDlgCtrlID())
{
pDC->SelectObject(&m_font);
}
运行结果如图。
10.6.4改变按钮的背景色及文本颜色
在OnCtlColor函数中添加:
if(IDOK==pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255,0,0));
return m_brush;
}
运行,发现并没有起作用。实际上,如果想改变按钮控件的背景色和文本颜色,需要用到CButton类的一个成员函数:DrawItem。
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
因此要想实现一个自绘按钮的控件,应该重载这个虚函数。
参数lpDrawItemStruct是一个结构体类型:
typedef struct tagDRAWITEMSTRUCT { // dis
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
DWORD itemData;
} DRAWITEMSTRUCT;
该结构体中有一个hDC成员,指向将要绘制按钮的DC,可以向该DC中选入自定义的颜色、画刷等对象。但是在重载函数结束前,一定要恢复hDC中原有对象。
因此想要改变OK按钮的文本颜色,要编写一个自己的按钮类,让它派生于CButton类,并重写DrawItem函数,在此函数中实现按钮背景色和文本颜色的设置。然后将OK按钮对象与此类相关联。
第一创建一个派生于CButton的类:
Insert->New Class->基类选择CButton,类名为CTestBtn。
接下来为此类添加DrawItem虚函数重写,将MSDN中DrawItem函数例子拷贝到该函数中来:
void CTestBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
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。然后根据系统提示,在对话框头文件中包含CTestBtn头文件。
因为自绘控件应该具有BS_OWNERDRAW风格,所以打开OK属性对话框,在Style标签页中选中Owner draw选项。
运行,即可看到OK按钮文字变成红色了。如图。
为了改变按钮背景色,这里用到了两个类,CSXBtn和CButtonST。将SXBtn.h和SXBtn.cpp,BtnST.cpp和BtnST.h四个文件复制到Graphic工程所在的目录中,然后将它们添加到自己工程中:
Project->Add To Peoject->Files->选中这四个文件,单击OK。
ClassView中就出现了上述的两个类。
首先利用CSXBtn类实现Cancel按钮文本颜色和背景色的改变,第一步将该按钮选中Owner draw;第二步,给该按钮关联一个成员变量,命名为m_btnCancel,但是选择变量类型时找不到CSXBtn类,就先随机选择一种类型。
然后在对话框的头文件中打开新增变量的定义,手工将其类型修改为CSXBtn。并在对话框头文件中和包含SXBtn.h头文件。
运行,就可以看到,Cancel按钮背景色变成了绿色,文字成了红色。
为了演示CButtonST类的使用,在对话框中又添加了一个按钮,ID为IDC_BTN_ST,Caption为ButtonST。同样地,为此按钮关联一个成员变量:m_btnST,并将其类型手动修改为CButtonST。在对话框类的头文件中添加此类头文件。
与上述两个按钮不同的是,该按钮不用设置Owner draw选项。
接下来初始化m_btnST变量,在对话框类OnInitDialog函数中进行,先给对话框类添加WM_INITDIALOG消息响应函数。
然后在该消息响应函数中设置背景色和前景色:
m_btnST.SetActiveBgColor(RGB(0,0,255));
m_btnST.SetActiveFgColor(RGB(255,0,0));
运行,当鼠标移动到按钮上面时,背景成蓝色,文字成红色。移走后,就恢复了正常。如图。
还可以设置不活动时候的按钮的背景色和前景色:
m_btnST.SetInactiveBgColor(RGB(0,0,255));
m_btnST.SetInactiveFgColor(RGB(255,0,0));
鼠标从按钮上面移走后就是不活动时候的状态。
10.7位图的显示
第一步创建位图,先用CBitmap构造一个位图对象,然后利用LoadBitmap函数加载一幅位图资源。
第二步是创建兼容DC,其中CreateCompatibleDC函数将创建一个内存设备上下文。
第三步将位图选入兼容DC中。
第四步是将兼容的DC中的位图贴到当前DC中。本例是调用BitBlt函数将兼容DC中的位图复制到当前DC中。
BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );
第一个和第二个参数指定目标矩形区域左上角;第三第四个参数指定源和目标矩形区域的逻辑宽度和逻辑高度。第五个参数指向源设备上下文对象;第六个参数指定源矩形区域左上角坐标。第七个参数指定复制的模式。
如果函数调用成功就会返回非0值。
先将一幅位图资源放到ret目录下,Insert->Resource->选择Bitmap资源类型-》单击Import按钮,选择要插入的位图。
接下来完成位图的显示,包含两个步骤,首先擦除窗口背景,然后对窗口重新进行绘制。当擦除窗口背景时,程序会发送一个WM_ERASEBKGND消息,可以在此消息中完成位图的绘制。
首先为视类添加WM_ERASEBKGND消息的响应函数:
BOOL CGraphicView::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
CDC dcCompatible;
dcCompatible.CreateCompatibleDC(pDC);
dcCompatible.SelectObject(&bitmap);
CRect rect;
GetClientRect(&rect);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY);
return CView::OnEraseBkgnd(pDC);
}
在这里模式选择复制,但是运行后,并没有达到理想的效果,原因就是该函数最后返回时又调用了视类的基类的OnEraseBkgnd函数。位图只是一闪就消失了。
于是将最后一行代码修改为:return true;
运行结果如图所示。
但是位图并没有完整地显示出来,因为BitBlt函数无法实现压缩和拉伸。因此在这里介绍另一个显示位图的函数StretchBlt:
BOOL StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop );
与BitBlt相比,多了nSrcWidth和nSrcHeight用来指示源矩阵的宽度和高度。
如果想得到源矩阵的宽度和高度,就要想办法得到位图的高度和宽度,这可以通过调用CBitmap类的GetBitmap函数来获得。
int GetBitmap( BITMAP* pBitMap );
typedef struct tagBITMAP {
int bmType;
int bmWidth;
int bmHeight;
int bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPVOID bmBits;
} BITMAP;
bmWidth和bmHeight指示位图的宽度和高度。
bitmap.LoadBitmap(IDB_BITMAP1);之后添加:
BITMAP bmp;
bitmap.GetBitmap(&bmp);
接下来就可以调用StretchBit函数代替之前的函数:
pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
运行结果如图。