`
Fangrn
  • 浏览: 818077 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

mfc绘图

    博客分类:
  • C
阅读更多

在Windows中,绘图一般在视图窗口的客户区进行,使用的是设备上下文类CDC中各种绘图函数。
1. 映射模式与坐标系
1)默认映射模式
映射模式(map mode)影响所有的图形和文本绘制函数,它定义(将逻辑单位转换为设备单位所使用的)度量单位和坐标方向,Windows总是用逻辑单位来绘图。
缺省情况下,绘图的默认映射模式为MM_TEXT,其绘图单位为像素(只要不打印输出,屏幕绘图使用该模式就够了)。若窗口客户区的宽和高分别为w和h像素,则其x坐标是从左到右,范围为0 ~ w-1;y坐标是从上到下,范围为0 ~ h-1。

2)设置映射模式
可以使用CDC类的成员函数GetMapMode和SetMapMode来获得和设置当前的映射模式:
int GetMapMode( ) const; // 返回当前的映射模式
virtual int SetMapMode( int nMapMode ); // 返回先前的映射模式


映射模式的nMapMode取值与含义
符号常量 数字常量 x方向 y方向 逻辑单位的大小 
MM_TEXT 1 向右 向下 像素 
MM_LOMETRIC 2 向右 向上 0.1 mm 
MM_HIMETRIC 3 向右 向上 0.01 mm 
MM_LOENGLISH 4 向右 向上 0.01 in 
MM_HIENGLISH 5 向右 向上 0.001 in 
MM_TWIPS 6 向右 向上 1/1440 in 
MM_ISOTROPIC 7 自定义 自定义 自定义 
MM_ANISOTROPIC 8 自定义 自定义 自定义


可 见,除了两种自定义映射模式外,x方向都是向右,y方向也只有MM_TEXT的向下,其余的都是向上,与数学上一致。除了MM_ANISOTROPIC 外,其他所有映射模式的x与y方向的单位都是相同的。所有映射模式的逻辑坐标的原点(0, 0)最初都是在窗口的左上角,但在CScrollView的派生类中,MFC会随用户滚动文档而自动调整逻辑原点的相对位置(改变视点的原点属性)。
3)自定义映射模式
自 定义映射模式MM_ISOTROPIC(各向同性,x与y方向的单位必须相同)和MM_ANISOTROPIC(各向异性,x与y方向的单位可以不同)的 单位和方向,可以通过用CDC类的成员函数G/SetWindowExt和G/SetViewportExt来获取/设置窗口和视口的大小来确定:
CSize GetWindowExt( ) const;
virtual CSize SetWindowExt( int cx, int cy );

virtual CSize SetWindowExt( SIZE size );
CSize GetViewportExt( ) const;
virtual CSize SetViewportExt( int cx, int cy );

virtual CSize SetViewportExt( SIZE size );
其中,cx或size.cx和cy或size.cy分别为窗口/视口的宽度与高度(逻辑单位)。
还可以用CDC类的成员函数SetViewportOrg来设置坐标原点的位置:
virtual CPoint SetViewportOrg( int x, int y );
CPoint SetViewportOrg( POINT point );
例如
void CDrawView::OnDraw(CDC* pDC) {
       CRect rect;
       GetClientRect(rect);
       pDC->SetMapMode(MM_ANISOTROPIC);
       pDC->SetWindowExt(1000,1000);
       pDC->SetViewportExt(rect.right, -rect.bottom);

       pDC->SetViewportOrg(rect.right / 2, rect.bottom /2);

       pDC->Ellipse(CRect(-500, -500, 500, 500));

}
将当前的映射模式设置为各向异性自定义映射模式,窗口大小为1000个逻辑单位宽和1000个逻辑单位高,视口大小同当前客户区,视口的坐标原点设置在当前客户区的中央。由于使用了负数作为SetViewportExt函数的第2个参数,所以y轴方向是向上的。

可见,圆被画成了椭圆,x与y方向上的逻辑单位不相同。
4)单位转换
对所有非MM_TEXT映射模式,有如下重要规则:
<!--[if !supportLists]-->l         <!--[endif]-->CDC的成员函数(如各种绘图函数)具有逻辑坐标参数

<!--[if !supportLists]-->l         <!--[endif]-->CWnd的成员函数(如各种响应函数)具有设备坐标参数(如鼠标位置point)

<!--[if !supportLists]-->l         <!--[endif]-->位置的测试操作(如CRect的PtInRect函数)只有使用设备坐标时才有效

<!--[if !supportLists]-->l         <!--[endif]-->长期使用的值应该用逻辑坐标保存(如窗口滚动后保存的设备坐标就无效了)


因此,为了使应用程序能够正确工作,除MM_TEXT映射模式外,其他映射模式都需要进行单位转换。下面是逻辑单位到设备单位(如像素)的转换公式:
x比例因子 = 视口宽度 / 窗口宽度
y比例因子 = 视口高度 / 窗口高度
设备x = 逻辑x * x比例因子 + x原点偏移量
设备y = 逻辑y * y比例因子 + y原点偏移量
Windows的GDI负责逻辑坐标和设备坐标之间的转换,这可以调用CDC类的成员函数LPtoDP和DPtoLP来进行:
void LPtoDP( LPPOINT lpPoints, int nCount = 1 ) const;

void LPtoDP( LPRECT lpRect ) const;
void LPtoDP( LPSIZE lpSize ) const;
void DPtoLP( LPPOINT lpPoints, int nCount = 1 ) const;

void DPtoLP( LPRECT lpRect ) const;
void DPtoLP( LPSIZE lpSize ) const;
例如:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) {

              CRect rect = m_rect; // 逻辑坐标

              CClientDC dc(this);
              dc.SetMapMode(MM_LOENGLISH);
              dc.LPtoDP(rect); // 转化成设备坐标
       if (rect.PtInRect(point)) // 位置的测试操作只有使用设备坐标时才有效

       ......
}
void CDrawView:: OnMouseMove (UINT nFlags, CPoint point) {

       float t,y;

              char buf[40];
       CDC* pDC = GetDC();

pDC->SetMapMode(MM_HIMETRIC);
              pDC->DPtoLP(&point); // 转化成逻辑坐标
t = t1 + (point.x * dt) / w; sprintf(buf, "%.4fs", t); pSB->SetPaneText(xV, buf);

y = (y0 - point.y) / dy; sprintf(buf, "%.4f", y); pSB->SetPaneText(yV, buf);
       ......
}
2. 画像素点
画像素点就是设置像素点的颜色,从前面3)(2)已知道这可由CDC的成员函数SetPixel来做,该函数的原型为:
COLORREF SetPixel( int x, int y, COLORREF crColor ); 或

COLORREF SetPixel( POINT point, COLORREF crColor );

其中,x与y分别为像素点的横坐标与纵坐标,crColor为像素的颜色值。例如
pDC->SetPixel(i, j, RGB(r, g, b));

3.画线状图
在Windows中,线状图必须用笔来画(笔的创建与使用见前面的3)(3)),下面是CDC类中可以绘制线状图的常用成员函数:
<!--[if !supportLists]-->l         <!--[endif]-->当前位置:设置当前位置为(x, y)或point:(返回值为原当前位置的坐标)

CPoint MoveTo( int x, int y ); 或 CPoint MoveTo( POINT point );

<!--[if !supportLists]-->l         <!--[endif]-->画线:使用DC中的笔从当前位置画线到点(x, y)或point:(若成功返回非0值):

BOOL LineTo( int x, int y ); 或BOOL LineTo( POINT point );
<!--[if !supportLists]-->l         <!--[endif]-->画折线:使用DC中的笔,依次将点数组lpPoints中的nCount(≥2)个点连接起来,形成一条折线:

BOOL Polyline( LPPOINT lpPoints, int nCount );
<!--[if !supportLists]-->l         <!--[endif]-->画多边形:似画折线,但还会将最后的点与第一个点相连形成多边形,并用DC中的刷填充其内部区域:

BOOL Polygon( LPPOINT lpPoints, int nCount );
<!--[if !supportLists]-->l         <!--[endif]-->画矩形:使用DC中的笔画左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形的边线,并用DC中的刷填充其内部区域:

BOOL Rectangle( int x1, int y1, int x2, int y2 ); 或
BOOL Rectangle( LPCRECT lpRect );
              有时需要根据用户给定的两个任意点来重新构造左上角和右下角的点,例如:
              rect = CRect(min(p0.x, point.x), min(p0.y, point.y), max(p0.x, point.x), max(p0.y, point.y));

<!--[if !supportLists]-->l         <!--[endif]-->画圆角矩形:使用DC中的笔画左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形的边线,并用宽x3或point.x高y3或point.y矩形的内接椭圆倒角,再用DC中的刷填充其内部区域:

BOOL RoundRect( int x1, int y1, int x2, int y2, int x3, int y3 );
BOOL RoundRect( LPCRECT lpRect, POINT point );
例如:
int d = min(rect.Width(), rect.Height()) / 4;

pDC-> RoundRect(rect, CPoint(d, d));
<!--[if !supportLists]-->l         <!--[endif]-->画(椭)圆:使用DC中的笔在左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形中画内接(椭)圆的边线,并用DC中的刷填充其内部区域:

BOOL Ellipse( int x1, int y1, int x2, int y2 );
BOOL Ellipse( LPCRECT lpRect );
注意,CDC中没有画圆的专用函数。在这里,圆是作为椭圆的(宽高相等)特例来画的。
<!--[if !supportLists]-->l         <!--[endif]-->画弧:(x1, y1)与(x2, y2)或lpRect的含义同画(椭)圆,(x3, y3)或ptStart为弧的起点,(x4, y4)或ptEnd为弧的终点:(逆时针方向旋转)

BOOL Arc( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);

BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
画圆弧:(其中(x, y)为圆心、nRadius为半径、fStartAngle为起始角、fSweepAngle为弧段跨角)
BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle);

<!--[if !supportLists]-->l         <!--[endif]-->画弓弦:参数的含义同上,只是用一根弦连接弧的起点和终点,形成一个弓形,并用DC中的刷填充其内部区域:

BOOL Chord( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Chord( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
4.画填充图
在 Windows中,面状图必须用刷来填充(刷的创建与使用见前面的3)(4))。上面(2)中的Polygon、Rectangle、Ellipse和 Chord等画闭合线状图的函数,只要DC中的刷不是空刷,都可以用来画对应的面状图(边线用当前笔画,内部用当前刷填充)。下面介绍的是CDC类中只能 绘制面状图的其他常用成员函数:
<!--[if !supportLists]-->l         <!--[endif]-->画填充矩形:用指定的刷pBrush画一个以lpRect为区域的填充矩形,无边线,填充区域包括矩形的左边界 和上边界,但不包括矩形的右边界和下边界:

void FillRect( LPCRECT lpRect, CBrush* pBrush );
<!--[if !supportLists]-->l         <!--[endif]-->画单色填充矩形:似FillRect,但只能填充单色,不能填充条纹和图案:

void FillSolidRect( LPCRECT lpRect, COLORREF clr );
void FillSolidRect( int x, int y, int cx, int cy, COLORREF clr );
<!--[if !supportLists]-->l         <!--[endif]-->画饼图(扇形):参数含义同Arc,但将起点和终点都与外接矩形的中心相连接,形成一个扇形区域,用DC中的刷 填充整个扇形区域,无另外的边线:

BOOL Pie( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
<!--[if !supportLists]-->l         <!--[endif]-->画拖动的矩形:先擦除线宽为sizeLast、填充刷为pBrushLast的原矩形lpRectLast,然 后再以线宽为size、填充刷为pBrush画新矩形lpRectLast。矩形的边框用灰色的点虚线画,缺省的填充刷为空刷:

void DrawDragRect( LPCRECT lpRect, SIZE size, LPCRECT lpRectLast,

SIZE sizeLast, CBrush* pBrush = NULL, CBrush* pBrushLast = NULL );

如:pDC->DrawDragRect(rect, size, rect0, size);

<!--[if !supportLists]-->l         <!--[endif]-->填充区域:

<!--[if !supportLists]-->n     <!--[endif]-->用当前刷从点(x, y)开始向四周填充到颜色为crColor的边界:

BOOL FloodFill(int x, int y, COLORREF crColor); // 成功返回非0

<!--[if !supportLists]-->n     <!--[endif]-->用当前刷从点(x, y)开始向四周填充:

BOOL ExtFloodFill(int x, int y, COLORREF crColor,

UINT nFillType); // 成功返回非0

<!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLBORDER:填充到颜色为crColor的边界(同FloodFill);(用于填充内部颜色不同但边界颜色相同的区域)
<!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLSURFACE:填充所有颜色为crColor的点,直到碰到非crColor颜色的点为止。(点(x, y)的颜色也必须为crColor),(用于填充内部颜色相同但边界颜色可以不同的区域)。例如:
pDC->ExtFloodFill(point.x, point.y, pDC->GetPixel(point), FLOODFILLSURFACE);

5.清屏
Windows没有提供专门的清屏函数,可以调用CWnd的下面两个函数调用来完成该功能:
void Invalidate(BOOL bErase = TRUE);
void UpdateWindow( );
或调用CWnd的函数
BOOL RedrawWindow(
   LPCRECT lpRectUpdate = NULL,

   CRgn* prgnUpdate = NULL,
   UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE

);
来完成。
例如(菜单项ID_CLEAR的事件处理函数):
CDrawView::OnClear() { // 调用OnDraw来清屏
       //Invalidate();
       //UpdateWindow( );
       RedrawWindow( );
}
也可以用画填充背景色矩形的方法来清屏,如:
       RECT rect;
       GetClientRect(&rect);
       pDC->FillSolidRect(&rect, RGB(255, 255, 255));

6.在控件上绘图
可以在对话框资源中放置图片控件,并对其类型属性选Frame。可在对话框的绘图消息响应函数OnPaint或其他函数中,用CWnd类的函数GetDlgItem:
CWnd* GetDlgItem( int nID ) const;
来获得图片控件的窗口对象,再用函数GetDC:
CDC* GetDC( );

由窗口对象得到DC,然后就可以用该DC在控件中画图。如(在ID为IDC_HUESAT的图片控件上画调色板)
void CColorDlg::OnPaint() 
{
       if (IsIconic()) {
              ... ...
       }
       else {
              CDialog::OnPaint();
              int i, j;
              BYTE r, g, b;
              // get control window and DC of Hue&Saturation

              CWnd *pWin = GetDlgItem(IDC_HUESAT);

              CDC *pDC = pWin->GetDC();

              // draw hue-saturation palette

              for (i = 0; i < 360; i++)

                     for (j = 0; j <= 255; j++) {

                            HSLtoRGB(i, 255 - j, 128, r, g, b); // 自定义函数,见网络硬盘的

// res目录中的ColTrans.cpp文件
                            pDC->SetPixel(i, j, RGB(r, g, b));

                     }
              ... ...
       }
}
在非Frame类静态控件上绘图,必须先按顺序依次调用CWnd类的Invalidate和UpdateWindow函数后,再开始用DC画图。如在一个ID为IDC_COLOR的按钮上绘图:
void CComDlgDlg::DrawColor()
{
       CWnd* pWnd = GetDlgItem(IDC_COLOR);

       CDC* pDC = pWnd->GetDC();

       CRect rect;

       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       pDC->FillRect(&rect, new CBrush(m_crCol));
}

若干说明:
<!--[if !supportLists]-->l     <!--[endif]-->除了基于对话框的程序外,其他对话框类都需要自己添加(重写型)消息响应函数OnInitDialog,来做一 些必要的初始化对话框的工作。添加方法是:先在项目区选中“类视图”页,再选中对应的对话框类,然后在属性窗口的“重写”页中添加该函数;

<!--[if !supportLists]-->l     <!--[endif]-->为了使在运行时能够不断及时更新控件的显示(主要是自己加的显式代码),可以将自己绘制控件的所有代码都全部加 入对话框类的消息响应函数OnPaint中。在需要时(例如在绘图参数修改后),自己调用CWnd的Invalidate和UpdateWindow函 数,请求系统刷新对话框和控件的显示。因为控件也是窗口,控件类都是CWnd的派生类。所以在对话框和控件中,可以像在视图类中一样,调用各种CWnd的 成员函数。

<!--[if !supportLists]-->l     <!--[endif]-->一般的对话框类,缺省时都没有明写出OnPaint函数。可以自己在对话框类中添加WM_PAINT消息的响应函数OnPaint来进行一些绘图工作。

<!--[if !supportLists]-->l     <!--[endif]-->为了在鼠标指向按钮时,让按钮上自己绘制的图形不被消去,可以设置按钮控件的“Owner Draw”属性为“True”。

<!--[if !supportLists]-->l     <!--[endif]-->如果希望非按钮控件(如图片控件和静态文本等),也可以响应鼠标消息(如单击、双击等),需要设置控件的“Notify”属性为“True”。

<!--[if !supportLists]-->l     <!--[endif]-->使用OnPaint函数在对话框客户区的空白处(无控件的地方)绘制自己的图形,必须屏蔽掉其中缺省的对对话框基类的OnPaint函数的调用:

//CDialog::OnPaint();
<!--[if !supportLists]-->l     <!--[endif]-->对话框的背景色,可以用CWnd类的成员函数:

DWORD GetSysColor( int nIndex);
得到,其中的nIndex取为COLOR_BTNFACE。例如:
dc.SetBkColor(GetSysColor(COLOR_BTNFACE));

下面是部分例子代码:(其中FillColor和ShowImg为自定义的成员函数)
void CSetDlg::OnBnClickedPenColor()
{
       // TODO: 在此添加控件通知处理程序代码
       CColorDialog colDlg(m_crLineColor);

       if (colDlg.DoModal() == IDOK) {

              m_crLineColor = colDlg.GetColor();

              Invalidate();
              UpdateWindow();
       }
}
// ……
void CSetDlg::OnPaint()
{
       CPaintDC dc(this); // device context for painting

       // TODO: 在此处添加消息处理程序代码
       // 不为绘图消息调用 CDialog::OnPaint()
       FillColor(IDC_PEN_COLOR, m_crLineColor);

       FillColor(IDC_BRUSH_COLOR, m_crBrushColor);

       if(m_pBitmap0 != NULL) ShowImg(IDC_BRUSH_IMG, m_hBmp0);

       else if(m_pBitmap != NULL) ShowImg(IDC_BRUSH_IMG, m_hBmp);

}
void CSetDlg::FillColor(UINT id, COLORREF col)

{
       CWnd* pWnd = GetDlgItem(id);

       CDC* pDC = pWnd->GetDC();

       pDC->SelectObject(new CPen(PS_SOLID, 1, RGB(0, 0, 0)));

       pDC->SelectObject(new CBrush(col));

       CRect rect; 
       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       pDC->RoundRect(&rect, CPoint(8, 8));

}
void CSetDlg::ShowImg(UINT ID, HBITMAP hBmp)

{
       CWnd* pWnd = GetDlgItem(ID);

       CDC* pDC = pWnd->GetDC();

       CRect rect; 
       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       BITMAP bs;
       GetObject(hBmp, sizeof(bs), &bs);

       CDC dc;
       if(dc.CreateCompatibleDC(pDC)) {

              int x0, y0, w, h;
              float rx = (float)bs.bmWidth / rect.right,

                     ry = (float)bs.bmHeight / rect.bottom;

              if (rx >= ry) {

                     x0 = 0; w = rect.right;

                     h = (int)(bs.bmHeight / rx + 0.5);

                     y0 = (rect.bottom - h) / 2;

              }
              else {
                     y0 = 0; h = rect.bottom;

                     w = (int)(bs.bmWidth / ry + 0.5);

                     x0 = (rect.right - w) / 2;

              }
              ::SelectObject(dc.GetSafeHdc(), hBmp);

              pDC->SetStretchBltMode(HALFTONE);
              pDC->StretchBlt(x0, y0, w, h, &dc, 0, 0, bs.bmWidth, bs.bmHeight, SRCCOPY);

              SetDlgItemInt(IDC_W, bs.bmWidth);

              SetDlgItemInt(IDC_H, bs.bmHeight);

       }
}
//……
5 设置绘图属性
除了映射模式外,还有许多绘图属性可以设置,如背景、绘图方式、多边形填充方式、画弧方向、刷原点等。
1.背景
1)背景色
当背景模式为不透明时,背景色决定线状图的空隙颜色(如虚线中的空隙、条纹刷的空隙和文字的空隙),可以使用CDC类的成员函数GetBkColor和SetBkColor来获得和设置当前的背景颜色:
COLORREF GetBkColor( ) const; // 返回当前的背景色
virtual COLORREF SetBkColor( COLORREF crColor ); // 返回先前的背景色
                                                                              // 若出错返回0x80000000
2)背景模式
背景模式影响有空隙的线状图的空隙(如虚线中的空隙、条纹刷的空隙和文字的空隙)用什么办法填充。可以使用CDC类的成员函数GetBkMode和SetBkMode来获得和设置当前的背景模式:
int GetBkMode( ) const; // 返回当前背景模式
int SetBkMode( int nBkMode ); // 返回先前背景模式
背景模式的取值
nBkMode值 名称 作用 
OPAQUE 不透明的(缺省值) 空隙用背景色填充 
TRANSPARENT 透明的 空隙处保持原背景图不变

2. 绘图模式
绘图模式(drawing mode)指前景色的混合方式,它决定新画图的笔和刷的颜色(pbCol)如何与原有图的颜色(scCol)相结合而得到结果像素色(pixel)。
1)设置绘图模式
可使用CDC类的成员函数SetROP2 (ROP = Raster OPeration光栅操作)来设置绘图模式:
int SetROP2( int nDrawMode );

其中,nDrawMode可取值:
绘图模式nDrawMode的取值
符号常量 作用 运算结果 
R2_BLACK 黑色 pixel = black 
R2_WHITE 白色 pixel = white 
R2_NOP 不变 pixel = scCol 
R2_NOT 反色 pixel = ~scCol 
R2_COPYPEN 覆盖 pixel = pbCol 
R2_NOTCOPYPEN 反色覆盖 pixel = ~pbCol 
R2_MERGEPENNOT 反色或 pixel = ~scCol | pbCol 
R2_MERGENOTPEN 或反色 pixel = scCol | ~pbCol 
R2_MASKNOTPEN 与反色 pixel = scCol & ~pbCol 
R2_MERGEPEN 或 pixel = scCol | pbCol 
R2_NOTMERGEPEN 或非 pixel = ~(scCol | pbCol) 
R2_MASKPEN 与 pixel = scCol & pbCol 
R2_NOTMASKPEN 与非 pixel = ~(scCol & pbCol) 
R2_XORPEN 异或 pixel = scCol ^ pbCol 
R2_NOTXORPEN 异或非 pixel = ~(scCol ^ pbCol)

其中,R2_COPYPEN(覆盖)为缺省绘图模式,R2_XORPEN(异或)较常用。
2)画移动图形
为了能画移动的位置标识(如十字、一字)和随鼠标移动画动态图形(如直线、矩形、椭圆),必须在不破坏原有背景图形的基础上移动这些图形。
移动图形采用的是异或画图方法,移动图形的过程为:异或画图、在原位置再异或化图(擦除)、在新位置异或画图、……。

       pGrayPen = new CPen(PS_DOT, 0, RGB(128, 128, 128));

pDC->SetBkMode(TRANSPARENT);
       pOldPen = pDC->SelectObject(pGrayPen);
pDC->SelectStockObject(NULL_BRUSH);
       pDC->SetROP2(R2_XORPEN);
       if (m_bErase) pDC->Ellipse(rect0);
       pDC->Ellipse(rect);
       pDC->SetROP2(R2_COPYPEN);
       pDC->SelectObject(pOldPen);
       rect0 = rect;
较完整的拖放动态画图的例子,可参照下面的“3. 拖放画动态直线”部分。
3)其他属性
<!--[if !supportLists]-->l         <!--[endif]-->多边形填充方式:可使用CDC类的成员函数GetPolyFillMode和SetPolyFillMode来确定多边形的填充方式:

int GetPolyFillMode( ) const;
int SetPolyFillMode( int nPolyFillMode );
其中nPolyFillMode 可取值ALTERNATE(交替——填充奇数边和偶数边之间的区域,缺省值)或WINDING(缠绕——根据多边形边的走向来确定是否填充一区域)
<!--[if !supportLists]-->l         <!--[endif]-->画弧方向:可使用CDC类的成员函数GetArcDirection和SetArcDirection来确定 Arc、Chord、Pie等函数的画弧方向:

int GetArcDirection( ) const;
int SetArcDirection( int nArcDirection );
其中,nArcDirection可取值AD_COUNTERCLOCKWISE(逆时针方向,缺省值)和AD_CLOCKWISE(顺时针方向)
<!--[if !supportLists]-->l         <!--[endif]-->刷原点:可使用CDC类的成员函数GetBrushOrg和SetBrushOrg来确定可填充绘图函数的条纹 或图案刷的起点:(缺省值为客户区左上角的坐标原点(0, 0))

CPoint GetBrushOrg( ) const;
CPoint SetBrushOrg( int x, int y );
CPoint SetBrushOrg( POINT point );
3.拖放画动态直线
下面是一个较完整的拖放动态画直线的例子:
// 类变量
class CDrawView : public CView { 
       //……
protected:
       BOOL m_bLButtonDown, m_bErase; // 判断是否按下左鼠标键

//和是否需要擦除图形的类变量
       CPoint p0, pm; // 记录直线起点和动态终点的类变量

       CPen * pGrayPen, * pLinePen; // 定义灰色和直线笔

       //……
}
// 构造函数
CDrawView::CDrawView() {
       m_bLButtonDown = FALSE; // 设左鼠标键按下为假

       m_bErase = FALSE; // 设需要擦除为假

       pGrayPen = new CPen(PS_SOLID, 0, RGB(128, 128, 128));// 创建灰色笔

       pLinePen = new CPen(PS_SOLID, 0, RGB(255, 0, 0));// 创建红色的直线笔

}
// 鼠标消息响应函数
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) {

       m_bLButtonDown = TRUE; // 设左鼠标键按下为真

       SetCapture(); // 设置鼠标捕获

       // SetCursor(LoadCursor(NULL, IDC_CROSS)); // 设置鼠标为十字

       p0 = point; // 保存矩形左上角

       pm = p0; // 让矩形右下角等于左上角
       CView::OnLButtonDown(nFlags, point);

}
void CDrawView::OnMouseMove(UINT nFlags, CPoint point) {

       SetCursor(LoadCursor(NULL, IDC_CROSS)); // 设置鼠标为十字

       if (m_bLButtonDown) { // 左鼠标键按下为真

              CDC* pDC = GetDC(); // 获取设备上下文

              pDC->SelectObject(pGrayPen);// 选取灰色笔
              pDC->SetROP2(R2_XORPEN);// 设置为异或绘图方式
              if (m_bErase) { // 需要擦除为真

                     pDC->MoveTo(p0); pDC->LineTo(pm); // 擦除原直线

              }
              else // 需要擦除为假

                     m_bErase = TRUE; // 设需要擦除为真
              pDC->MoveTo(p0); pDC->LineTo(point); // 绘制新直线

              pm = point; // 记录老终点

              ReleaseDC(pDC); // 释放设备上下文

       }
       CView::OnMouseMove(nFlags, point);

}
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) {

       ReleaseCapture(); // 释放鼠标捕获

       if (m_bLButtonDown) { // 左鼠标键按下为真

              CDC* pDC = GetDC(); // 获取设备上下文

              pDC->SelectObject(pGrayPen);// 选取灰色笔
              pDC->SetROP2(R2_XORPEN); // 设置为异或绘图方式
              pDC->MoveTo(p0); pDC->LineTo(pm); // 擦除原直线

              pDC->SelectObject(pLinePen); // 选择直线笔
              pDC->SetROP2(R2_COPYPEN);// 设置为覆盖绘图方式
              pDC->MoveTo(p0); pDC->LineTo(point); // 绘制最终的直线

              m_bLButtonDown = FALSE; // 重设左鼠标键按下为假

              m_bErase = FALSE; // 重需要擦除为假

              ReleaseDC(pDC); // 释放设备上下文

       }
       CView::OnLButtonUp(nFlags, point);

}

分享到:
评论

相关推荐

    MFC绘图教程大全

    ### MFC绘图教程知识点详解 #### 一、MFC绘图基础概念 **MFC绘图概述** MFC(Microsoft Foundation Classes)是一套由微软提供的面向对象的C++类库,它简化了Windows API的使用,使得开发者能够更加高效地进行...

    MFC绘图相关程序实例

    在《大学Visual C++程序设计案例教程》网络辅助资源中,你可能会找到以上提到的示例代码和详细解释,这些资源会帮助你更好地掌握MFC绘图的相关知识,通过实践加深理解和记忆。记得查阅源代码,跟踪每一步的操作,...

    MFC绘图编程教程(简单,容易上手)

    MFC绘图编程教程 MFC绘图编程教程是的发生的方式,旨在帮助开发者快速上手MFC绘图编程。该教程重点介绍了GDI和DC的概念、基本图形函数、Ondraw()方法和OnPaint()方法、视图中的图形绘制案例,以及典型案例——简单...

    MFC绘图示例

    MFC绘图示例是一个基于MFC框架的项目,它为开发者提供了展示如何在Windows应用中进行图形绘制的实例。这个示例不仅帮助开发者了解MFC的基础知识,还为他们构建自己的应用程序提供了一个起点。 在MFC中,绘图主要...

    MFC绘图板实例

    本实例“MFC绘图板”是基于C++实现的一个图形用户界面(GUI)应用程序,它利用Microsoft Foundation Classes (MFC)库来构建。MFC是微软提供的一个C++类库,封装了Windows API,使得开发者可以更容易地创建Windows...

    关于MFC绘图板的参考报告

    总的来说,MFC绘图板的设计涉及到了C++面向对象编程、Windows API的使用、事件驱动编程以及图形用户界面的交互设计。通过理解并掌握这些知识点,开发者能够构建出功能丰富、交互性强的图形应用程序。

    MFC绘图板源代码及设计报告

    在这个"MFC绘图板源代码及设计报告"中,我们可以深入探讨MFC如何被用来创建一个交互式的绘图应用程序。 首先,MFC绘图板通常会使用CView或CDocument类作为基础,这些类是MFC框架的一部分,它们分别代表了应用程序中...

    MFC绘图程序,实现绘图中的各种形式。

    在这个特定的MFC绘图程序中,我们看到它着重于实现各种绘图功能,包括但不限于填充颜色、绘制圆形、三角形以及多边形。以下是对这些知识点的详细解释: 1. **MFC框架**:MFC是基于Windows API的,它封装了Windows...

    MFC绘图 简单绘图

    在本文中,我们将深入探讨MFC(Microsoft Foundation Classes)库中的绘图功能,以及如何创建一个简单的MFC绘图程序,实现划线和画矩形。MFC是微软提供的一套C++类库,用于构建Windows应用程序,它封装了Windows API...

    简单的MFC绘图软件

    简单的MFC绘图软件 666666666666666

    MFC绘图源码

    在这个名为"MFC绘图源码"的资源中,我们很显然会探讨如何利用MFC进行图形绘制。 首先,MFC中的绘图主要涉及到`CDC`(Device Context)类,它是设备上下文的抽象,可以用来控制屏幕或打印机等输出设备。在MFC中,...

    mfc绘图程序

    在本案例中,"mfc绘图程序"指的是使用MFC开发的一个简单的绘图工具。 这个工具可能是基于MFC的框架,利用其窗口、控件和图形绘制功能来实现用户交互和图形显示。在MFC中,`CDC`(Device Context)类是用于图形绘制的...

    MFC绘图程序

    在这个“MFC绘图程序”中,我们主要探讨的是如何使用MFC来实现一个简单的图形绘制功能。 首先,我们要理解MFC中的绘图涉及到的主要类。`CView`是MFC框架中的核心绘图类,它继承自`CWnd`,并提供了一个用于处理视图...

    mfc绘图小软件-mfc作业

    总结,制作MFC绘图软件涉及到对MFC绘图机制的深刻理解,包括`CDC`的使用、重绘流程、内存DC的应用以及错误排查。通过不断学习和实践,我们可以逐步完善这个绘图软件,解决重绘时的错误,提升用户体验。希望这些知识...

    MFC绘图与数据库

    在这个“MFC绘图与数据库”的主题中,我们将探讨如何使用MFC进行图形绘制,并将这些图形数据存储到数据库中,以及串口通信在这个过程中的作用。 首先,让我们关注MFC绘图。在MFC中,绘图主要通过CDC(Device ...

    MFC绘图程序 MFC绘图程序

    在本项目中,“MFC绘图程序”是指利用MFC框架实现的图形绘制功能,特别是动态曲线的绘制。这对于理解和掌握Windows编程以及图形用户界面(GUI)的设计原则非常有帮助。 1. **MFC基础**: MFC是一组预先编写的C++...

    huituban.rar_MFC 文字输入_MFC 绘图_mfc绘图_绘图板_绘图板MFC

    综上所述,创建一个具备MFC绘图和文字输入功能的绘图板涉及的关键技术包括:MFC框架的理解与应用,`CView`和`CDC`的绘图机制,`CEdit`类的文字输入,鼠标事件的处理,以及图形对象的使用。通过熟练掌握这些知识,...

    mfc 绘图板

    在本文中,我们将深入探讨"MFC绘图板"这一主题,它是基于Microsoft Foundation Class (MFC) 库的,用于创建具有图形用户界面的应用程序。MFC是Microsoft为Windows平台开发的一个C++类库,它封装了Windows API,使得...

    MFC绘图基础

    【MFC绘图基础】是关于使用Microsoft Foundation Class (MFC)库进行图形绘制的知识,主要涉及双缓存去闪机制、基本图形绘制以及交互式绘图的实现。MFC是一个C++类库,用于构建Windows应用程序,它封装了Windows API...

    基于MFC绘图板设计

    在本项目中,我们探讨的是一个基于MFC(Microsoft Foundation Classes)框架的绘图板设计。MFC是微软提供的一套面向对象的类库,它为开发者提供了构建Windows应用程序的便利,尤其是桌面应用。这个绘图板项目是作为...

Global site tag (gtag.js) - Google Analytics