存在的问题:
(1)鼠标移动画方框时,直线动画不稳,闪烁,有时出现边框线擦除不净和重影的现象,不象PhotoShop中选择框非常稳定无闪烁;
(2)双击点击子窗体标题栏时,方框出现在鼠标点击处。
(3)pDoc->m_pDrawInfo->m_rect.NormalizeRect(); 并不能调整m_rect,就是鼠标移动点在开始点上方或左侧时,方框变成一条直线,并跟着鼠标移动,为何?
(4)不用多线程,而用定时器时,当不按鼠标连续移动时,动画就不动了,为何?
1.用Visual Studio创建一个多文档项目:
文件→新建→项目,在“新建项目”对话框中:Visual C++→MFC→MFC应用程序,在“名称”栏中输入“DrawAntLine”,点击“确定”。
2.在“MFC应用程序向导”中,应用类型选择“多个文档”和“文档/视图结构支持”,点击“完成”。
3.在stdafx.h头文件中定义如下结构:
typedef struct CDrawInfo
{
HDC m_hDC;
CRect m_rect;
}CDrawInfo;
4.添加一个多线程类。在“类视图”中:添加→类→MFC→MFC类→类名“CAntLineThread”→基类“CWinThread”→“确定”。清单如下:
/////AntLineThread.h//////////////////
#pragma once
#include "stdafx.h"
//---------------------------------------------------------------------------
void DrawTheRect(LPARAM pParam);
class CAntLineThread : public CWinThread
{
DECLARE_DYNCREATE(CAntLineThread)protected:
void DrawAntLine();public:
CDrawInfo *m_DrawInfo;
bool m_bContinue;
bool m_bDraw; CAntLineThread(); // 动态创建所使用的受保护的构造函数
virtual ~CAntLineThread();public:
virtual BOOL InitInstance();
virtual int ExitInstance();protected:
DECLARE_MESSAGE_MAP()
};
//---------------------------------------------------------------------------
/////AntLineThread.cpp//////////////////
#include "stdafx.h"
#include "DrawAntLine.h"
#include "AntLineThread.h"
DWORD Counter = 0;
DWORD CounterStart = 0;
void CALLBACK MovingDots(int X, int Y, LPARAM lpData)
{
CDrawInfo *pInfo = (CDrawInfo*)lpData;
CDC *pDC = CDC::FromHandle(pInfo->m_hDC); Counter = (Counter + 1) % 10; COLORREF vColor;
if (Counter < 5) vColor = RGB(255,255,255);
else vColor = RGB(0,0,0);
pDC->SetPixel(X,Y,vColor);
}
//---------------------------------------------------------------------------void DrawTheRect(LPARAM pParam)
{
CDrawInfo *pDrawInfo = (CDrawInfo*)pParam;
CRect R = pDrawInfo->m_rect; // Determines starting pixel color of Rect
Counter = CounterStart;
// Use LineDDA to draw each of the 4 edges of the rectangle
LineDDA(R.right, R.top, R.left, R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.left, R.bottom, R.left, R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.right, R.bottom, R.right,R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.right, R.bottom, R.left, R.bottom, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
}
//---------------------------------------------------------------------------IMPLEMENT_DYNCREATE(CAntLineThread, CWinThread)
BEGIN_MESSAGE_MAP(CAntLineThread, CWinThread)
END_MESSAGE_MAP()
//---------------------------------------------------------------------------CAntLineThread::CAntLineThread()
{
m_DrawInfo = NULL;
m_bDraw = false;
}
//---------------------------------------------------------------------------CAntLineThread::~CAntLineThread()
{
}
//---------------------------------------------------------------------------BOOL CAntLineThread::InitInstance()
{
DrawAntLine();
return true;
}
//---------------------------------------------------------------------------int CAntLineThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
//---------------------------------------------------------------------------void CAntLineThread::DrawAntLine()
{
m_bContinue = true;
m_bDraw = false; while (m_bContinue)
{
if (m_DrawInfo && m_bDraw)
{
CounterStart = (CounterStart+1) % 0XFFFFFFFF;
DrawTheRect((LPARAM)m_DrawInfo);
}
Sleep(60);
} if (!m_bContinue) AfxEndThread(0);
}
//---------------------------------------------------------------------------
(1)鼠标移动画方框时,直线动画不稳,闪烁,有时出现边框线擦除不净和重影的现象,不象PhotoShop中选择框非常稳定无闪烁;
(2)双击点击子窗体标题栏时,方框出现在鼠标点击处。
(3)pDoc->m_pDrawInfo->m_rect.NormalizeRect(); 并不能调整m_rect,就是鼠标移动点在开始点上方或左侧时,方框变成一条直线,并跟着鼠标移动,为何?
(4)不用多线程,而用定时器时,当不按鼠标连续移动时,动画就不动了,为何?
1.用Visual Studio创建一个多文档项目:
文件→新建→项目,在“新建项目”对话框中:Visual C++→MFC→MFC应用程序,在“名称”栏中输入“DrawAntLine”,点击“确定”。
2.在“MFC应用程序向导”中,应用类型选择“多个文档”和“文档/视图结构支持”,点击“完成”。
3.在stdafx.h头文件中定义如下结构:
typedef struct CDrawInfo
{
HDC m_hDC;
CRect m_rect;
}CDrawInfo;
4.添加一个多线程类。在“类视图”中:添加→类→MFC→MFC类→类名“CAntLineThread”→基类“CWinThread”→“确定”。清单如下:
/////AntLineThread.h//////////////////
#pragma once
#include "stdafx.h"
//---------------------------------------------------------------------------
void DrawTheRect(LPARAM pParam);
class CAntLineThread : public CWinThread
{
DECLARE_DYNCREATE(CAntLineThread)protected:
void DrawAntLine();public:
CDrawInfo *m_DrawInfo;
bool m_bContinue;
bool m_bDraw; CAntLineThread(); // 动态创建所使用的受保护的构造函数
virtual ~CAntLineThread();public:
virtual BOOL InitInstance();
virtual int ExitInstance();protected:
DECLARE_MESSAGE_MAP()
};
//---------------------------------------------------------------------------
/////AntLineThread.cpp//////////////////
#include "stdafx.h"
#include "DrawAntLine.h"
#include "AntLineThread.h"
DWORD Counter = 0;
DWORD CounterStart = 0;
void CALLBACK MovingDots(int X, int Y, LPARAM lpData)
{
CDrawInfo *pInfo = (CDrawInfo*)lpData;
CDC *pDC = CDC::FromHandle(pInfo->m_hDC); Counter = (Counter + 1) % 10; COLORREF vColor;
if (Counter < 5) vColor = RGB(255,255,255);
else vColor = RGB(0,0,0);
pDC->SetPixel(X,Y,vColor);
}
//---------------------------------------------------------------------------void DrawTheRect(LPARAM pParam)
{
CDrawInfo *pDrawInfo = (CDrawInfo*)pParam;
CRect R = pDrawInfo->m_rect; // Determines starting pixel color of Rect
Counter = CounterStart;
// Use LineDDA to draw each of the 4 edges of the rectangle
LineDDA(R.right, R.top, R.left, R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.left, R.bottom, R.left, R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.right, R.bottom, R.right,R.top, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
LineDDA(R.right, R.bottom, R.left, R.bottom, (LINEDDAPROC)MovingDots, (long)pDrawInfo);
}
//---------------------------------------------------------------------------IMPLEMENT_DYNCREATE(CAntLineThread, CWinThread)
BEGIN_MESSAGE_MAP(CAntLineThread, CWinThread)
END_MESSAGE_MAP()
//---------------------------------------------------------------------------CAntLineThread::CAntLineThread()
{
m_DrawInfo = NULL;
m_bDraw = false;
}
//---------------------------------------------------------------------------CAntLineThread::~CAntLineThread()
{
}
//---------------------------------------------------------------------------BOOL CAntLineThread::InitInstance()
{
DrawAntLine();
return true;
}
//---------------------------------------------------------------------------int CAntLineThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
//---------------------------------------------------------------------------void CAntLineThread::DrawAntLine()
{
m_bContinue = true;
m_bDraw = false; while (m_bContinue)
{
if (m_DrawInfo && m_bDraw)
{
CounterStart = (CounterStart+1) % 0XFFFFFFFF;
DrawTheRect((LPARAM)m_DrawInfo);
}
Sleep(60);
} if (!m_bContinue) AfxEndThread(0);
}
//---------------------------------------------------------------------------
6.在DrawAntLine.h中的CDrawAntLineApp添加
...
public:
CAntLineThread *m_pAntLineThread;
DWORD m_Tool;
...
7.在DrawAntLine.cpp中添加如下代码:
CDrawAntLineApp::CDrawAntLineApp()
{
m_pAntLineThread = NULL;
m_Tool = 0;
}CDrawAntLineApp::~CDrawAntLineApp()
{
if (m_pAntLineThread != NULL) m_pAntLineThread->m_bContinue = false;
m_pAntLineThread = NULL;
}BOOL CDrawAntLineApp::InitInstance()
{
CWinApp::InitInstance(); m_pAntLineThread = (CAntLineThread *)AfxBeginThread(RUNTIME_CLASS(CAntLineThread));
...
}
8.在类CDrawAntLineDoc中添加:
public:
CDrawInfo* m_pDrawInfo;
并在其构造函数中写:m_pDrawInfo=NULL;
9.在类CDrawAntLineView中分别添加以下消息函数:
WM_SETFOCUS、WM_KILLFOCUS、WM_ERASEBKGND、WM_CREATE、WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP、ID_BUTTON_RUN、ID_BUTTON_STOP。
清单如下:
/////DrawAntLineView.h//////////////////
// DrawAntLineView.h : CDrawAntLineView 类的接口
//#pragma onceclass CDrawAntLineView : public CView
{
protected: // 仅从序列化创建
CDrawAntLineView();
DECLARE_DYNCREATE(CDrawAntLineView)// 属性
public:
CDrawAntLineDoc* GetDocument() const;// 操作
public:
// 重写
public:
virtual void OnDraw(CDC* pDC); // 重写以绘制该视图
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:// 实现
public:
virtual ~CDrawAntLineView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endifprotected:// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnButtonRun();
afx_msg void OnButtonStop();
afx_msg void OnUpdateButtonRun(CCmdUI *pCmdUI);
afx_msg void OnUpdateButtonStop(CCmdUI *pCmdUI);
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};#ifndef _DEBUG // DrawAntLineView.cpp 中的调试版本
inline CDrawAntLineDoc* CDrawAntLineView::GetDocument() const
{ return reinterpret_cast<CDrawAntLineDoc*>(m_pDocument); }
#endif
/////DrawAntLineView.cpp//////////////////
// DrawAntLineView.cpp : CDrawAntLineView 类的实现
//
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "DrawAntLine.h"
#include "DrawAntLineDoc.h"
#include "DrawAntLineView.h"
//---------------------------------------------------------------------------
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//---------------------------------------------------------------------------
IMPLEMENT_DYNCREATE(CDrawAntLineView, CView)
//---------------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CDrawAntLineView, CView)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_COMMAND(ID_BUTTON_RUN, &CDrawAntLineView::OnButtonRun)
ON_COMMAND(ID_BUTTON_STOP, &CDrawAntLineView::OnButtonStop)
ON_UPDATE_COMMAND_UI(ID_BUTTON_RUN, &CDrawAntLineView::OnUpdateButtonRun)
ON_UPDATE_COMMAND_UI(ID_BUTTON_STOP, &CDrawAntLineView::OnUpdateButtonStop)
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_ERASEBKGND()
ON_WM_CREATE()
END_MESSAGE_MAP()
//---------------------------------------------------------------------------// CDrawAntLineView 构造/析构
CDrawAntLineView::CDrawAntLineView()
{
}
//-----------------------------------------------------------------------------CDrawAntLineView::~CDrawAntLineView()
{
}
//-----------------------------------------------------------------------------BOOL CDrawAntLineView::PreCreateWindow(CREATESTRUCT& cs)
{
return CView::PreCreateWindow(cs);
}
//-----------------------------------------------------------------------------int CDrawAntLineView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1) return -1; CDrawAntLineDoc* pDoc = GetDocument();
if (!pDoc) return -1;
if (pDoc->m_pDrawInfo == NULL)
{
pDoc->m_pDrawInfo = new CDrawInfo;
pDoc->m_pDrawInfo->m_rect = CRect(-1,-1,-1,-1);
pDoc->m_pDrawInfo->m_hDC = GetDC()->m_hDC;
theApp.m_pAntLineThread->m_DrawInfo = pDoc->m_pDrawInfo;
}
return 0;
}
//-----------------------------------------------------------------------------// CDrawAntLineView 绘制
void CDrawAntLineView::OnDraw(CDC* pDC)
{
CDrawAntLineDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc || pDoc->m_pDrawInfo == NULL) return; CDC dcMem; // 用于缓冲作图的内存DC
CBitmap bm; // 内存中承载临时图象的位图
CRect rect;
GetClientRect(&rect); // Step 1:为屏幕DC创建兼容的内存DC :CreateCompatibleDC()
dcMem.CreateCompatibleDC(pDC); // Step 2:创建位图:CreateCompatibleBitmap()
bm.CreateCompatibleBitmap(&dcMem, rect.Width(), rect.Height()); // Step 3:把位图选入设备环境:SelectObject(),可以理解为选择画布
dcMem.SelectObject(&bm); // 按原来背景填充客户区,不然会是黑色
dcMem.FillSolidRect(rect,pDC->GetBkColor()); pDoc->m_pDrawInfo->m_hDC = dcMem.m_hDC; // 画图(矩形)
DrawTheRect((LPARAM)pDoc->m_pDrawInfo);
// Step 4:把绘制好的图形“拷贝“到屏幕上:BitBlt()
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);
dcMem.DeleteDC(); // 删除DC
bm.DeleteObject(); // 删除位图
pDoc->m_pDrawInfo->m_hDC = GetDC()->m_hDC;
}
//-----------------------------------------------------------------------------BOOL CDrawAntLineView::OnEraseBkgnd(CDC* pDC)
{
return CView::OnEraseBkgnd(pDC);
}
//-----------------------------------------------------------------------------// CDrawAntLineView 诊断
#ifdef _DEBUG
void CDrawAntLineView::AssertValid() const
{
CView::AssertValid();
}
//-----------------------------------------------------------------------------void CDrawAntLineView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
//-----------------------------------------------------------------------------CDrawAntLineDoc* CDrawAntLineView::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDrawAntLineDoc)));
return (CDrawAntLineDoc*)m_pDocument;
}
#endif //_DEBUG
//-----------------------------------------------------------------------------void CDrawAntLineView::OnLButtonDown(UINT nFlags, CPoint point)
{
CDrawAntLineDoc *pDoc = GetDocument();
if (!pDoc || pDoc->m_pDrawInfo == NULL) return;
OnUpdate(this, NULL, NULL); // RemoveTheRect, Erase any existing rectangle
switch (theApp.m_Tool)
{
case ID_BUTTON_RUN:
pDoc->m_pDrawInfo->m_rect.left = point.x;
pDoc->m_pDrawInfo->m_rect.top = point.y;
pDoc->m_pDrawInfo->m_rect.right = point.x;
pDoc->m_pDrawInfo->m_rect.bottom = point.y; theApp.m_pAntLineThread->m_DrawInfo = pDoc->m_pDrawInfo;
break; default : // 没有选择蚂蚁线工具,若已经设置了选取,则清空选区
pDoc->m_pDrawInfo->m_rect = CRect(-1,-1,-1,-1);
break;
} CView::OnLButtonDown(nFlags, point);
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnMouseMove(UINT nFlags, CPoint point)
{
CDrawAntLineDoc* pDoc = GetDocument();
if ( pDoc == NULL || pDoc->m_pDrawInfo == NULL) return; switch (theApp.m_Tool)
{
case ID_BUTTON_RUN:
if (nFlags == MK_LBUTTON)
{
OnUpdate(this, NULL, NULL); // RemoveTheRect
pDoc->m_pDrawInfo->m_rect.right = point.x;
pDoc->m_pDrawInfo->m_rect.bottom = point.y;
// pDoc->m_pDrawInfo->m_rect.NormalizeRect(); // 并不能调整m_rect?
theApp.m_pAntLineThread->m_DrawInfo = pDoc->m_pDrawInfo;
}
break;
} CView::OnMouseMove(nFlags, point);
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnLButtonUp(UINT nFlags, CPoint point)
{
CDrawAntLineDoc *pDoc = GetDocument();
if (!pDoc || pDoc->m_pDrawInfo == NULL) return;
switch (theApp.m_Tool)
{
case ID_BUTTON_RUN :
pDoc->m_pDrawInfo->m_rect.right = point.x;
pDoc->m_pDrawInfo->m_rect.bottom = point.y;
theApp.m_pAntLineThread->m_DrawInfo = pDoc->m_pDrawInfo;
break;
} CView::OnLButtonUp(nFlags, point);
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnButtonRun()
{
theApp.m_Tool = ID_BUTTON_RUN;
theApp.m_pAntLineThread->m_bDraw = true;
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnButtonStop()
{
theApp.m_Tool = ID_BUTTON_STOP;
theApp.m_pAntLineThread->m_bDraw = false;
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnUpdateButtonRun(CCmdUI *pCmdUI)
{
pCmdUI->Enable(theApp.m_Tool != ID_BUTTON_RUN);
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnUpdateButtonStop(CCmdUI *pCmdUI)
{
pCmdUI->Enable(theApp.m_Tool == ID_BUTTON_RUN);
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnSetFocus(CWnd* pOldWnd)
{
CView::OnSetFocus(pOldWnd);
CDrawAntLineDoc *pDoc = GetDocument();
if (!pDoc || pDoc->m_pDrawInfo == NULL) return;
theApp.m_pAntLineThread->m_DrawInfo = pDoc->m_pDrawInfo;
}
//-----------------------------------------------------------------------------void CDrawAntLineView::OnKillFocus(CWnd* pNewWnd)
{
CView::OnKillFocus(pNewWnd); // 当前视图失去焦点时,更新视图,抹去一些绘图信息(动画选区)
OnUpdate(this, NULL, NULL);
}
//-----------------------------------------------------------------------------