MFC——第五次上机之具体代码实现

image.png

image.png

image.png
void CPPOEx5View::OnLButtonDown(UINT nFlags, CPoint point) //当左键按下时,记录当时的按下的点的坐标  
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
       m_point = point;//获取鼠标按下去时的坐标point
       CView::OnLButtonDown(nFlags, point);
}
void CPPOEx5View::OnLButtonUp(UINT nFlags, CPoint point) //当鼠标左键弹起时,使用API方式进行画线。要进行申请设备,准备划线,释放设备的阶段。
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值

       
       //HDC hdc = ::GetDC(m_hWnd);//获取窗口句柄
       //::MoveToEx(hdc, m_point.x, m_point.y, NULL);//移动到当前画笔的位置
       //::LineTo(hdc, point.x, point.y);//从鼠标按下去的坐标开始画线到当前鼠标松开的坐标,不管路径长短,只取绝对位移
       //::ReleaseDC(m_hWnd, hdc);////释放句柄资源

       CDC *cdc = GetDC();//获取窗口句柄
       cdc->MoveTo(m_point);//移动到当前画笔的位置
       cdc->LineTo(point);//从鼠标按下去的坐标开始画线到当前鼠标松开的坐标,不管路径长短,只取绝对位移
       ReleaseDC(cdc);//释放句柄资源

       CView::OnLButtonUp(nFlags, point);
}
void CPPOEx5View::OnPaint()
{
       CPaintDC dc(this); // device context for painting
       // TODO:  在此处添加消息处理程序代码
       // 不为绘图消息调用 CView::OnPaint()
}

利用MFC的CDC类在客户区实现画线功能:

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CDC *pDC = GetDC();
       pDC->MoveTo( m_ptOrigin );
       pDC->LineTo( point );
       ReleaseDC( pDC );
 
       CView::OnLButtonUp(nFlags, point);
}

利用MFC的CClientDC类在客户区实现画线功能

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CClientDC dc(this);
       dc.MoveTo(m_ptOrigin);
       dc.LineTo(point);
       CView::OnLButtonUp(nFlags, point);
}

利用MFC的CWindowDC类在客户区实现画线功能

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CWindowDC dc(this);
       dc.MoveTo(m_ptOrigin);
       dc.LineTo(point);
CView::OnLButtonUp(nFlags, point);
}

利用MFC的CClientDC类在非客户区实现画线功能:

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CClientDC dc(GetParent());
       dc.MoveTo(m_ptOrigin);
       dc.LineTo(point);
       CView::OnLButtonUp(nFlags, point);
}

注:视类窗口只有客户区(即视类窗口本身),而框架窗口既有客户区(即菜单栏以下部分),还有非客户区(标题栏、菜单栏、工具栏和状态栏)。视窗口的父窗口就是框架窗口即与CMainFrame类相关联的窗口
利用MFC的CWindowDC类在非客户区实现画线功能:

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CWindowDC dc( GetParent() );
       dc.MoveTo(m_ptOrigin);
       dc.LineTo(point);
    CView::OnLButtonUp(nFlags, point);
}

利用MFC的CWindowDC类在桌面区实现画线功能:

void CTView::OnLButtonUp(UINT nFlags, CPoint point)
{
       CWindowDC dc(GetDesktopWindow());
       dc.MoveTo(m_ptOrigin);
       dc.LineTo(point);
    CView::OnLButtonUp(nFlags, point);
}

以上是最简单的了,接下来就要开始进行综合性的操作了。建议去看一下第一次的PPT。
要求是如何通过对话框,指定画笔的颜色
设计思路
先设置相应的菜单项,在菜单项中添加好相应的菜单按钮(下图双击),

image.png

image.png

image.png

添加好后右键PPOEx5,打开类向导窗口,然后根据菜单项的ID号,添加在上诉4个基本类中的响应函数。
image.png

在颜色设置菜单中,通过CColorDialog对话框进行选择颜色,使用GetColor可以返回一个COLORREF类型的颜色值,需要在类中添加相应类型的color变量一值,用于记录选择的值。
在画笔大小设置菜单中,需要先设置一个对话框类,用来接收输入的画笔的粗细。然后在菜单的响应函数中,实例化这个对话框,然后接收输入的参数。
添加对话框的步骤是:先右键DIalog。
image.png

具体编辑的自己需要更改界面
image.png

先然后右键DIalog主框架添加类CPenScale,然后在为Edit添加变量m_scale。进入CPenScale类的头文件,在#pragma once后添加#include "Resource.h"。然后就是主要的代码实现了,代码如下:
先在MainFrm.cpp下实现

* 设置全局变量
COLORREF coloR;
int scale = 10;

颜色选择实现

void CMainFrame::OnTextColor()
{
       // TODO:  在此添加命令处理程序代码
       CColorDialog m_ColorDialog;//通过CColorDialog实例化打开颜色选择框
       m_ColorDialog.DoModal();//创建有模式对话框
       coloR = m_ColorDialog.GetColor();//GetColor()会返回一个COLORREF类型的颜色值
}

设置字体大小:

void CMainFrame::OnTextSize()
{
       // TODO:  在此添加命令处理程序代码
       UpdateData(TRUE);
       CPenScale m_PenScale;
        m_PenScale.DoModal(); 
}

设置对话框的确定实现更改字体大小的功能:

void CPenScale::OnBnClickedCancel()
{
       // TODO:  在此添加控件通知处理程序代码
       UpdateData(TRUE);
       int extern scale ;
       if (m_scale != (CString)""){
             scale = _ttoi(m_scale);//将CString转化为int
       }
       else
       {
             CDialogEx::OnCancel();
       }
       CDialogEx::OnCancel();
}

画有色线:

void CPPOEx5View::OnLButtonUp(UINT nFlags, CPoint point) //当鼠标左键弹起时,使用API方式进行画线。要进行申请设备,准备划线,释放设备的阶段。
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
          CPen pen(PS_SOLID, scale, coloR); //实线,宽度10像素,红色
          CClientDC dc(this);
          CPen *pOldpen = dc.SelectObject(&pen);//DC将当前的画笔保存在poldPen中,将Pen作为当前的画笔
          dc.MoveTo(m_point);
          dc.LineTo(point);
          dc.SelectObject(pOldpen);//将poldPen所保存的画笔作为当前的画笔。如果没有CPen*pOldPen=dc.SelectObject(&pen);则poldPen的值是不确定的,当然不是我们想要保存的画笔。而如果没有dc.SelectObject(pOldPen);,则画笔将永远是Pen,而不能恢复。

       CView::OnLButtonUp(nFlags, point);
}
* 实现绘制国际象棋棋盘

我自己又添加了一个菜单项,进行棋盘绘制,里边绑定一个对话框,然后就行棋盘宽和高的设定,只要点击设定以后,只能在客户区绘制棋盘,点击取消绘制即可回复原装。


image.png

全局变量

int extern width, hight;
boolean extern key_CHess;//设定了一个key用来判断是否需要绘制棋盘

设定响应事件

void CChessHW::OnBnClickedCancel()
{
       // TODO:  在此添加控件通知处理程序代码
       
       if (m_ChessHigh != (CString)""){
             hight = _ttoi(m_ChessHigh);//将CString转化为int
       }
       else
       {
             CDialogEx::OnCancel();
             key_CHess = false;
       }
       if (m_ChessWide != (CString)""){
             width = _ttoi(m_ChessWide);//将CString转化为int
       }
       else
       {
             CDialogEx::OnCancel();
             key_CHess = false;
       }

       key_CHess = true;
       CDialogEx::OnCancel();
}

取消绘制棋盘

void CChessHW::OnBnClickedButton1()
{
       // TODO:  在此添加控件通知处理程序代码
       key_CHess = false;
       CDialogEx::OnCancel();
}
  • 绘制函数更改为:
void CPPOEx5View::OnLButtonUp(UINT nFlags, CPoint point) //当鼠标左键弹起时,使用API方式进行画线。要进行申请设备,准备划线,释放设备的阶段。
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值

       point_h = point;
       if (!key_CHess){
             CPen pen(PS_SOLID, scale, coloR); //实线,宽度10像素,红色
             CClientDC dc(this);
             CPen *pOldpen = dc.SelectObject(&pen);//DC将当前的画笔保存在poldPen中,将Pen作为当前的画笔
             dc.MoveTo(m_point);
             dc.LineTo(point);
             dc.SelectObject(pOldpen);//将poldPen所保存的画笔作为当前的画笔。如果没有CPen*pOldPen=dc.SelectObject(&pen);则poldPen的值是不确定的,当然不是我们想要保存的画笔。而如果没有dc.SelectObject(pOldPen);,则画笔将永远是Pen,而不能恢复。
       }
       else{
             OnChessdraw(m_point, point, hight, width);//自定义了一个函数进行棋盘绘制
       }
       

       CView::OnLButtonUp(nFlags, point);
}

棋盘绘制:


image.png

别忘了在View头文件中添加绘制函数的声明。

void CPPOEx5View::OnChessdraw(CPoint point1, CPoint point2, int hight, int width)
{
       // TODO: Add your command handler code here 

       CClientDC dc(this);

       //注意到,当i+j为偶数时,象棋位的颜色是白色,而当其为奇数时,象棋位大的颜色是黑色,所以采用了上面的方式进行设计。
       for (int i = 0; i<13; i++)
       {
             for (int j = 0; j<13; j++)
             {
                    point1.x = i*hight;
                    point1.y = j*width;
                    point2.x = (i + 1)*hight;
                    point2.y = (j + 1)*width;
                    if ((i + j) % 2 == 0)
                    {
                           CBrush brush(RGB(0, 0, 0));
                           dc.FillRect(CRect(point1, point2), &brush);
                    }
                    else
                    {
                           CBrush brush(RGB(255, 255, 255));
                           dc.FillRect(CRect(point1, point2), &brush);
                    }

             }
       }
}
* 当你缩放窗口大小时,窗口会重绘,导致以前的棋盘消失,其中一个不完全额度解决方法就是在OnPaint()方法中进行图像重绘。
void CPPOEx5View::OnPaint()
{
       CPaintDC dc(this); // device context for painting
       // TODO:  在此处添加消息处理程序代码
       // 不为绘图消息调用 CView::OnPaint()
       if (key_CHess){
             OnChessdraw(m_point, point_h, hight, width);
       }
}

//这个咱没用到

ClientToScreen( )是把窗口坐标转换为屏幕坐标

pWnd->GetWindowRect(&rc);是获取整个窗体的大小

pWnd->GetClientRect(&rc1);是获取窗体中客户区的大小

ScreenToClient( )是把屏幕坐标转换为窗口坐标

屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的 VC下,有些函数使用窗口坐标,有些使用屏幕坐标,使用时要分清。

一个窗体分为两部分:系统区和客户区
象标题和菜单之类的是系统区,由系统来控制,客户区就是你的地盘喽!!!

Width, Height 是指整体的,ClientWidth, ClientHeight是指客户区的,两者相减就是系统区的啦!!

ClientToScreen是把坐标从当前窗体转化成全屏幕的!!!
ScreenToClient是把屏幕坐标转化成相对当前窗体的坐标!!!!

  • 对于成像会在窗口改变后导致绘图消失,是因为窗口一改变就会调用OnPaint()进行窗口重绘,你如果不在里边进行一些逻辑操作,就会将窗口重绘为空白。所以就是在这里便进行一些重新窗口绘制即可。

      先了解一下基本知识,OnPaint()是窗口重绘时的消息响应函数,而OnDraw()则是OnPaint()进行重绘的一种方法。可以在OnPaint()中进行调用OnDraw(),在OnDraw()内添加一些逻辑代码。
    
      先暂且不提PPT中给的双缓冲机制,先来谈一下自定义绘制棋盘的时候,咱们可以自定义一下棋盘每个方格的高、宽,这时每当窗口改变的时候我们重绘完以后的棋盘应该不能在改变大小,所以我把上面的绘制自定义的函数进行封装一下,到时候只需要在OnPaint()中进行调用方法就行。大体代码如下:
    
  • 自定义棋盘绘制方法

//绘制自定义的棋盘
void CPPOEx5View::OnChessdraw(CPoint point1, CPoint point2, int hight, int width)
{
       // TODO: Add your command handler code here 

       CClientDC dc(this);

       //注意到,当i+j为偶数时,象棋位的颜色是白色,而当其为奇数时,象棋位大的颜色是黑色,所以采用了上面的方式进行设计。
       for (int i = 0; i<13; i++)
       {
             for (int j = 0; j<13; j++)
             {
                    point1.x = i*hight;
                    point1.y = j*width;
                    point2.x = (i + 1)*hight;
                    point2.y = (j + 1)*width;
                    if ((i + j) % 2 == 0)
                    {
                           CBrush brush(RGB(0, 0, 0));
                           dc.FillRect(CRect(point1, point2), &brush);
                    }
                    else
                    {
                           CBrush brush(RGB(255, 255, 255));
                           dc.FillRect(CRect(point1, point2), &brush);
                    }

             }
       }
}

这里是在鼠标弹起时的代码,我都进行了一些方法封装。

void CPPOEx5View::OnLButtonUp(UINT nFlags, CPoint point) //当鼠标左键弹起时,使用API方式进行画线。要进行申请设备,准备划线,释放设备的阶段。
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
       CDC *dc = GetDC();
       point_h = point;
       if (!key_CHess&&!key_ChessMax){
             OnDrawLine();
             return;
       }
       if (key_CHess){//自定义的Key用来判断是否绘制自定义棋盘
             
             OnChessdraw(m_point, point_h, hight, width);
             return;
       }
       if (key_ChessMax){//自定义的Key用来判断是否绘制自适应屏幕棋盘
             OnDrawMax(dc);
             return;
       }
       CView::OnLButtonUp(nFlags, point);
}

OnPaint():

void CPPOEx5View::OnPaint()
{
       CPaintDC dc(this); // device context for painting
       // TODO:  在此处添加消息处理程序代码
       // 不为绘图消息调用 CView::OnPaint()

       if (key_CHess){
             OnChessdraw(m_point, point_h, hight, width);
       }
       if (key_ChessMax){
             OnDraw(&dc);
       }
}

接下来就是所谓的双缓冲实现方法了,我也进行了一定的封装,自定义了一个函数进行方法实现。所谓的双缓冲,咱们可以理解为缓存,就是把你绘制的图像所在的矩形给抠出来然后缓存起来,等会在进行取出重绘,最后在释放资源即可。具体代码实现如下:代码备注解释的挺详细的了,我就不多说了。

//绘制适应屏幕的棋盘
void CPPOEx5View::OnDrawMax(CDC* pDC){
       if (key_ChessMax){
             GetWindowRect(&rect);
             ScreenToClient(&rect);
             if (count >-1)//如果已经绘制了初始棋盘
             {
                    

                    count++;
             }
             else
             {
                    CDC MemDC; //定义一个显示设备对象 
                    CBitmap MemBitmap; //定义一个位图 

                    //定义一个与屏幕显示兼容的内存显示设备 
                    MemDC.CreateCompatibleDC(NULL);
                    //现在还不能绘图 
                    //定义一个屏幕显示兼容的位图 


                    //上三行代码实现获得屏幕的大小的宽高 
                    MemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

                    //将位图选入到设备中,类似于画笔的选择 
                    CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);
                    //清除原来的背景颜色,这里选用了白色 
                    MemDC.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(255, 255, 255));

                    //绘图代码 
                    //这里要注意的地方,绘图时,一定要使用MemDC设备进行绘图才能显示,不然只使用函数调用的方式,不能正确的显示图像。
                    //  paint(); 

                    int  width = rect.Width() / 13;
                    int  hight = rect.Height() / 13;
                    if (width < hight){//使得绘制出来的棋盘为正方形
                           hight = width;
                    }
                    else{
                           width = hight;
                    }
                    for (int i = 0; i<13; i++)
                    {
                           for (int j = 0; j<13; j++)
                           {
                                 m_point.x = i*width;
                                 m_point.y = j*hight;
                                 point_h.x = m_point.x + width;
                                 point_h.y = m_point.y + hight;

                                 if ((i + j) % 2 == 0)
                                 {
                                        CBrush brush(RGB(0, 0, 0));
                                        MemDC.FillRect(CRect(m_point, point_h), &brush);
                                 }
                                 else
                                 {
                                        CBrush brush(RGB(255, 255, 255));
                                        MemDC.FillRect(CRect(m_point, point_h), &brush);
                                 }

                           }
                    }
                    //内存中的图拷入到屏幕中显示 
                    pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &MemDC, 0, 0, SRCCOPY);//第一二个参数为位置开始出(左上角),第三四个参数为设备的高宽,第六七个参数为切 

                    //清理工作 
                    MemBitmap.DeleteObject();
                    MemDC.DeleteDC();
             }
       }
}

BOOL Arc(
  HDC hdc,         // handle to device context                       dc句柄
  int nLeftRect,   // x-coord of rectangle's upper-left corner       包容矩形左X值
  int nTopRect,    // y-coord of rectangle's upper-left corner       包容矩形顶Y值
  int nRightRect,  // x-coord of rectangle's lower-right corner      包容矩形右X值
  int nBottomRect, // y-coord of rectangle's lower-right corner      包容矩形底Y值
  int nXStartArc,  // x-coord of first radial ending point           第一点X坐标
  int nYStartArc,  // y-coord of first radial ending point           第一点Y坐标
  int nXEndArc,    // x-coord of second radial ending point         结束点X坐标
  int nYEndArc     // y-coord of second radial ending point         结束点Y坐标
);

在最后一页的问题上,好麻烦,我就不太计较了,大体能够实现就行。他说要求进行交互式的图形绘制,就是去添加一个菜单项,然后绑定一个对话框,然后再里边进行一些按钮的添加,到时候可以进行选择操作。


image.png

我设置壳很多key,用来判断点击当前是哪个点击了,每点击一个对应的button,就设置其对应的key为true,并将其他的key归为false即可。

* key
boolean key_bightctl = false;//曲线
boolean key_linectl = false;//直线
boolean key_circlectl = false;//圆
boolean key_rectanglectl = false;//矩形
boolean key_polygonctl = false;//多边形

在VIew中进行逻辑判断,然后进行绘图操作。我是在鼠标点击和鼠标松开分别传入id为1,2.有个地方会用到。

void CPPOEx5View::Choose(int id){
       switch (id)
       {
       case 1:
             if (key_bightctl){
                    key_bight = true;
             }
             if (key_polygonctl){
                    key_polygon = true;
             }
             break;
       case 2:
             if (key_bightctl){
                    key_bight = false;
             }
             if (key_linectl){
                    OnDrawLine();
             }
             if (key_circlectl){
                    OnDrawCircle();
             }
             if (key_rectanglectl){
                    OnDrawRectangle();
             }
             if (key_polygonctl){
                    key_polygon = false;
             }
             break;
       default:
             break;
       }
       
}
* 绘制直线和绘制多边形都直接用直线绘制就行了,还是以前哪个函数。
* 绘制曲线则需要一定的逻辑判断,就是让他绘制点然后在鼠标移动时绘制图像就成了曲线,于是添加了一个消息响应函数

OnMouseMove(),在类向导中添加。

void CPPOEx5View::OnMouseMove(UINT nFlags, CPoint point)
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
       if (key_bight){
             m_point = point;
             point_h = point;
             OnDrawLine();
       }
       
       CView::OnMouseMove(nFlags, point);
}

绘制矩形

void CPPOEx5View:: OnDrawRectangle(){
       CClientDC dc(this);
       CBrush brush(coloR);
       dc.FillRect(CRect(m_point, point_h), &brush);
}
* 

绘制圆形,可以实现各种圆形,我这不想多弄,就只能绘制一个固定的圆。

void CPPOEx5View::OnDrawCircle(){
       //CDC *pDC = GetDC();
       //CPPOEx5Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc);  HPEN hPen; //画笔;                  
       //hPen = CreatePen(PS_DASH, scale, coloR);//生成绿色画笔;         
       //SelectObject(pDC->m_hDC,hPen);//把画笔引入设备场境 ;   
       //Arc(pDC->m_hDC, m_point.x, m_point.x, point_h.y, point_h.y, m_point.x, m_point.y, point_h.x, point_h.y);//画圆 ;
       //DeleteObject(pDoc);

       CDC* pDC= GetDC();
       CPoint pt;//圆心
       int r;//半径
       pt.x = 200;
       pt.y = 200;
       r = 150;
       pDC->BeginPath();
       pDC->MoveTo(pt);
       pDC->AngleArc(pt.x, pt.y, r, 0, 360);//这是画了一个360度的圆弧,也可以用Ellipse
       pDC->EndPath();
       CRgn rgn;
       rgn.CreateFromPath(pDC);
       CBrush brush(coloR);//根据自己需要填充颜色
       pDC->FillRgn(&rgn,&brush);
}

对于BMP格式保存。不写了不写了,以后再说吧,反正就是运用双缓冲技术进行保存。

你可能感兴趣的:(MFC——第五次上机之具体代码实现)