作者:朱金灿
基于文档视图结构程序的双缓冲绘图框架比较多,那么如何在对话框上绘图呢?以前通常的做法是拖一个静态文本控件或其它控件当作绘图区域或者在这个区域上创建一个视图出来。看了微软的一个示例程序DrawCli(一个绘图的单文档程序),产生了一些灵感,决心把它移植到对话框绘图上,摸索了一下,搞了一个基于对话框的简单双缓冲绘图框架。
具体代码如下,对话框头文件代码:
#include <vector>
//@brief 直线结构体
struct stLine
{
stLine(CPoint &Begin,CPoint &End)
{
m_Begin = Begin;
m_End = End;
}
//@brief 起点
CPoint m_Begin;
//@brief 终点
CPoint m_End;
};
class CDoubleBufDrawDlg : public CDialog
{
DECLARE_DYNAMIC(CDoubleBufDrawDlg)
public:
CDoubleBufDrawDlg(CWnd* pParent = NULL); // 标准构造函数
virtual ~CDoubleBufDrawDlg();
// 对话框数据
enum { IDD = IDD_DIALOG_GDIPLUS };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnBnClickedDrawPoint();
afx_msg void OnBnClickedDrawLine();
afx_msg void OnSize(UINT nType, int cx, int cy);
void AdjustControls();
afx_msg void OnPaint();
protected:
//
void InitDrawPara();
protected:
//@brief 上一个点
CPoint m_PrePt;
//@brief 鼠标按下点
CPoint m_DownPt;
//@brief 直线结构体数组
std::vector<stLine> m_Lines;
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
};
Cpp文件源码(这里只列出主要函数):
//@brief 初始化绘图参数
void CDoubleBufDrawDlg::InitDrawPara()
{
m_PrePt = CPoint(-1,-1);
m_DownPt = CPoint(-1,-1);
}
CDoubleBufDrawDlg::CDoubleBufDrawDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDoubleBufDrawDlg::IDD, pParent)
{
InitDrawPara();
}
void CDoubleBufDrawDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 记录鼠标按下点
m_DownPt = point;
CDialog::OnLButtonDown(nFlags, point);
}
void CDoubleBufDrawDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 假如在移动鼠标的同时按下左键
if (MK_LBUTTON&nFlags)
{
CDC *pDC = GetDC();
pDC->SetROP2(R2_NOTXORPEN);
// 将绘图区域限制在对话框的客户区上面的/4区域
CRect rtDraw(rtClient.top,rtClient.left,rtClient.Width(),3*(rtClient.Height()/4));
CRgn DrawRgn;
DrawRgn.CreateRectRgn(rtDraw.left,rtDraw.top,rtDraw.right,rtDraw.bottom);
pDC->SelectClipRgn(&DrawRgn);
if (m_PrePt.x!=-1)
{
// 擦除上一条线
pDC->MoveTo(m_DownPt);
pDC->LineTo(m_PrePt);
}
// 画线
pDC->MoveTo(m_DownPt);
pDC->LineTo(point);
ReleaseDC(pDC);
m_PrePt = point;
}
CDialog::OnMouseMove(nFlags, point);
}
void CDoubleBufDrawDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 将绘制的直线加入到直线数组
m_Lines.push_back(stLine(m_DownPt,point));
// 初始化绘图参数
InitDrawPara();
CDialog::OnLButtonUp(nFlags, point);
}
void CDoubleBufDrawDlg::OnPaint()
{
// 最小时绘制菜单图标,因为我是在一个单文档程序中弹出该对话框的,故不需要绘制图标
// 如果是基于对话框的程序则需要绘制图标
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
//int cxIcon = GetSystemMetrics(SM_CXICON);
//int cyIcon = GetSystemMetrics(SM_CYICON);
//CRect rect;
//GetClientRect(&rect);
//int x = (rect.Width() - cxIcon + 1) / 2;
//int y = (rect.Height() - cyIcon + 1) / 2;
Draw the icon
//dc.DrawIcon(x, y, m_hIcon);
}
else
{
CPaintDC dc(this);
// 定义一个兼容DC
CDC CompaDC;
// 初始化绘图DC指针
CDC* pDrawDC = &dc;
CBitmap bitmap;
CBitmap* pOldBitmap = 0;
CRect rtClient;
GetClientRect(&rtClient);
// 将对话框的客户区上面的/4区域设为绘图区域
CRect rtDraw(rtClient.top,rtClient.left,rtClient.Width(),3*(rtClient.Height()/4));
// 假如不是打印机DC
if (!dc.IsPrinting())
{
// 创建兼容DC,创建兼容位图,将兼容位图选进兼容DC
if (CompaDC.CreateCompatibleDC(&dc))
{
if (bitmap.CreateCompatibleBitmap(&dc,rtDraw.Width(),rtDraw.Height()))
{
pDrawDC = &CompaDC;
pOldBitmap = CompaDC.SelectObject(&bitmap);
}
}
}
// 定义一个白色画刷,将背景色设为白色
CBrush brush;
if (!brush.CreateSolidBrush(RGB(255,255,255)))
return;
brush.UnrealizeObject();
pDrawDC->FillRect(rtDraw,&brush);
// 绘制直线
for (size_t i = 0;i<m_Lines.size();i++)
{
pDrawDC->MoveTo(m_Lines[i].m_Begin);
pDrawDC->LineTo(m_Lines[i].m_End);
}
// 使用GDI+绘制一个线性渐变画刷
Gdiplus::Graphics graphics(pDrawDC->m_hDC);
LinearGradientBrush linGrBrush(Point(100,0),Point(100,100),Color(255,255,0,0),Color(255,0,255,0));
Color colors[] = {
Color(255, 255, 0, 0), // red
Color(255, 255, 255, 0), //yellow
Color(255, 0, 0, 255), // blue
Color(255, 0, 255, 0)}; // green
REAL positions[] = {
0.0f,
0.33f,
0.66f,
1.0f};
linGrBrush.SetInterpolationColors(colors, positions,4);
// 填充指定区域矩形
graphics.FillRectangle(&linGrBrush,100,0,100,100);
graphics.ReleaseHDC(pDrawDC->m_hDC);
if (pDrawDC != &dc)
{
// 将绘图DC贴到真正的设备DC上
dc.BitBlt(rtDraw.left,rtDraw.top,rtDraw.Width(),rtDraw.Height(),&CompaDC, 0, 0, SRCCOPY);
CompaDC.SelectObject(pOldBitmap);
}
}
}
效果图如下,其中上面的白色区域为绘图区域:
现在你怎么改变对话框的大小绘图区域也不会产生非双缓冲绘图那种闪烁。