MFC两种拖动鼠标动态画图的方法

时间:2022-04-03 19:14:29

一般来说,一次画图的过程分为鼠标左键按下,鼠标移动,鼠标左键弹起。要想在鼠标移动的过程中动态显示所画的图的变化过程,所画的图只能是可以由两个点确定的图形,如直线、矩形、椭圆等等。举例来说,如何画一条线段?我们要知道起点和终点。起点自然是鼠标左键按下时鼠标的位置,终点是鼠标移动时当前的鼠标位置。既然是拖动鼠标动态画图,那么画图的动作自然是在鼠标移动的响应函数OnMouseMove()里完成。要想让图跟着鼠标移动而变化,就必须在鼠标移动时不断根据鼠标新位置画图,然而不停画图,原来画的旧图还停留在那里。本来一次鼠标移动只要画一根线段,现在一次鼠标移动画出好多根线段。所以关键是如何擦除旧图。第一种方法是保存上一次鼠标停留的位置,然后用背景色的画笔把旧图描一遍。

//鼠标左键按下

void C画图Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
  mousemode = 1;//标识鼠标左键已按下
  pt_Begin = pt_End = pt_Mid = point;//pt_Begin标识起点,pt_End标识新终点,pt_Mid标识旧终点
 CDialogEx::OnLButtonDown(nFlags, point);
}

//鼠标移动

void C画图Dlg::OnMouseMove(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if (mousemode == 1)//确保鼠标移动是在鼠标左键按下后
 {
  pt_Mid = pt_End;//将原终点的值赋给pt_Mid
  pt_End = point;//将当前鼠标的位置赋给新终点
  EraseLine(pt_Begin, pt_Mid);//擦掉旧线段
  DrawLine(color,pt_Begin, pt_End);//画新线段

CDialogEx::OnMouseMove(nFlags, point);
  }
 }

但是这样会把原来作好的图也擦掉。想到的解决方法是将原来作好的图的数据(如起点和终点,还可以扩充如画笔颜色,图形种类等,写成一个class)储存在vector里面,擦掉旧图后先把储存在vector里面的图画一遍,再画新图,当然也可以画新图之后再把储存在vector里面的图画一遍。储存画好的图在鼠标左键弹起的时候进行。

//鼠标移动

void C画图Dlg::OnMouseMove(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if (mousemode == 1)//确保鼠标移动是在鼠标左键按下后
 {
  pt_Mid = pt_End;
  pt_End = point;
  EraseLine(pt_Begin, pt_Mid);//擦掉旧线段
  DrawLine(color,pt_Begin, pt_End);//画新线段
  DrawpGraph(vec_pGraph);//重画已画好的图形

CDialogEx::OnMouseMove(nFlags, point);
 }

//鼠标左键弹起

void C画图Dlg::OnLButtonUp(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if (mousemode == 1)
 {
  mousemode = 0;
  vector<POINT> vec;
  vec.push_back(pt_Begin);
  vec.push_back(pt_End);
  previousGraph temp(drawmode, color, vec);//previousGraph是储存每个画好的图的数据的class
  vec_pGraph.push_back(temp);//将这次画的图形的种类,颜色,起点和终点存入向量vec_pGraph中
  pt_Begin = { 0, 0 };
  pt_Mid = { 0, 0 };
  pt_End = { 0, 0 };
 }

CDialogEx::OnLButtonUp(nFlags, point);

}

将画好的图的数据储存在vector里面的一个好处是我们可以很容易地进行撤销与重做。然而假如一开始不是在一张空白的图上作画,而是在一张背景图上作画时,这样还是会擦掉背景图。第二种方法可以解决这个问题。

第二种方法进行动态画图的思路是鼠标左键按下时将当前窗口的内容保存为一张图片,鼠标移动过程中每次画新图之前都先载入这张图片。

void CDrawXView::DrawpGraph()//函数,功能是载入保存了鼠标左键按下时窗口图像的图片
{
 test.Attach(image.Detach());//test是CBitmap类型的对象
 CDC MemDC;
 CDC dc;
 MemDC.CreateCompatibleDC(NULL);
 dc.CreateCompatibleDC(&MemDC);
 dc.SelectObject(&test);
 CClientDC ClientDC(this);
 ClientDC.BitBlt(0, 0, //目标设备逻辑横、纵坐标
  AreaWidth, AreaHeight, //显示位图的像素宽、高度
  &dc, //待显示位图数据的设备情境对象
  0, 0, //源数据中的横、纵坐标
  SRCCOPY);
}

//鼠标左键按下
void CDrawXView::OnLButtonDown(UINT nFlags, CPoint point)
{
 mousemode = 1;
 CRect rect; GetClientRect(&rect);//获取画布大小  
 CClientDC dc(this); 
 hbit = CreateCompatibleBitmap(dc, rect.right - rect.left, rect.bottom - rect.top);//创建兼容位图
 HDC hdc = CreateCompatibleDC(dc);      //创建兼容DC,以便将图像保存为不同的格式 
 HBITMAP hOldMap = (HBITMAP)SelectObject(hdc, hbit); //将位图选入DC,并保存返回值  
 BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, dc, 0, 0, SRCCOPY); //将屏幕DC的图像复制到内存DC中    
 hbit = (HBITMAP)SelectObject(hdc, hOldMap); 
 image.Attach(hbit);//将图像保存到image中,image是CImage类型
 pt_Begin = pt_End = pt_Mid = point;
 CScrollView::OnLButtonDown(nFlags, point);
}
//鼠标移动
void CDrawXView::OnMouseMove(UINT nFlags, CPoint point)
{
 if (mousemode == 1)
 {
  pt_End = point;
  DrawpGraph();
  DrawLine(color, pt_Begin, pt_End);
 }
 CScrollView::OnMouseMove(nFlags, point);
}

//鼠标左键弹起

void C画图Dlg::OnLButtonUp(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if (mousemode == 1)
 {
  mousemode = 0;
  pt_Begin = { 0, 0 };
  pt_End = { 0, 0 };
 }

CDialogEx::OnLButtonUp(nFlags, point);

}