VC++ 绘制线条 OnLButtonDown函数(DrawView.cpp) 利用SDK全局函数实现画线功能 利用MFC的CDC类实现画线功能 利用MFC的CClientDC类实现画线功能

VC++ 绘制线条 OnLButtonDown函数(DrawView.cpp) 利用SDK全局函数实现画线功能 利用MFC的CDC类实现画线功能 利用MFC的CClientDC类实现画线功能,第1张

目录

绘制线条

OnLButtonDown函数(DrawView.cpp)

利用SDK全局函数实现画线功能

利用MFC的CDC类实现画线功能

 利用MFC的CClientDC类实现画线功能

接上:VC++ OnWndMsg 函数(wincore.cpp)

接下:VC++ 利用MFC的CWindowDC类实现画线功能 在桌面窗口中画线 绘制彩色线条 CPen nPenStyle nWidth crColor


绘制线条

了解了MFC的消息映射机制,下面来完成画线功能。在先前创建的Draw项目中,可以看到消息响应函数OnLButtonDown有两个参数(如下)。

连接:VC++消息映射机制 WindowProc的函数(wincore.cpp)

// 生成的消息映射函数
protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

其中第二个参数是CPoint类型,CPoint类表示一个点。也就是说,当按下鼠标左键时,鼠标单击处的坐标点已由此参数传递给 OnLButtonDown 这一消息响应函数。这样,我们所需要做的工作就是在此消息响应函数中保存该点的信息。为此,需要在视类中增加一个成员变量。给一个类增加成员变量可以通过在类视图中用鼠标右键单击该类,并从d出的快捷菜单中选择【添加】→【添加变量】,变量名称为m_ptOrigin,类型为CPoint,访问权限设置为private,如下图所示。

单击【确定】按钮,完成成员变量的添加 *** 作。接下来在CDrawView构造函数(DrawView.cpp)中初始化这个变量,将其值初始化为0。

CDrawView::CDrawView() noexcept
{
	// TODO: 在此处添加构造代码
	m_ptOrigin = 0;
}

CDrawView::~CDrawView()
{
}

然后,在消息响应函数OnLButtonDown中保存鼠标按下点的信息,代码如下所示。

OnLButtonDown函数(DrawView.cpp)
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_ptOrigin = point;
	CView::OnLButtonDown(nFlags, point);
}

此时,我们就得到了将要绘制的线条的起点,现在还要获得线条的终点才能绘制出一个线条。终点是在鼠标左键d起来时获得的,因此,在 CDrawView 类中还需要对WM_LBUTTONUP 消息进行响应。利用前面介绍的方法添加该消息响应函数,该函数的初始代码如下所示。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CView::OnLButtonUp(nFlags, point);
}

可以看到,Visual Studio自动产生的这个消息响应函数也有一个CPoint类型的参数,表示鼠标左键d起时的位置点,也就是需要绘制的线条的终点。有了这两个点,就可以绘制线条了。

cwind的核心代码都在wincore.cpp里。

利用SDK全局函数实现画线功能

如下是利用SDK函数实现画线功能的代码。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	// 首先获得窗口的设备描述表
	HDC hdc;
	hdc = ::GetDC(m_hWnd);
	// 移动到线条的起点
	MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
	// 画线
	LineTo(hdc, point.x, point.y);
	// 释放设备描述表
	::ReleaseDC(m_hWnd, hdc);

	CView::OnLButtonUp(nFlags, point);
}

之前介绍过,为了进行绘图 *** 作,必须获得一个设备描述表(DC)。因此,如上所示代码首先定义一个HDC类型的变量:hdc,接着调用全局函数GetDC获得当前窗口的设备描述表。前面讲述过,CWnd类有一个成员变量(m_hWnd)用于保存窗口句柄,而CDrawView类派生于CWnd类,因此该类也有这样的一个成员变量,这里的GetDC函数可以直接把这个成员变量作为参数来使用。

接下来进行画线 *** 作,首先调用MoveToEx函数将当前位置移动到需要绘制的线条的起点处。该函数有四个参数,其中第一个参数是设备描述表的句柄;第二个和第三个参数分别是新位置处的X坐标和Y坐标;第四个参数是指向POINT结构体的指针,用于保存移动 *** 作前鼠标的位置坐标,在本例中不需要这个坐标值,将此参数设置为NULL。

VS F1调出MSDN查看MoveToEx函数:

BOOL MoveToEx(
  HDC     hdc,
  int     x,
  int     y,
  LPPOINT lppt
);

然后调用 LineTo 函数绘制一条到指定点的线。该函数有三个参数,其中第一个参数是设备描述表的句柄,第二个和第三个参数分别是线条终点的X坐标和Y坐标。在前面已经讲述过,在绘图 *** 作结束后,一定要释放设备描述表资源。因此,代码最后调用ReleaseDC函数来完成这一功能。编译并运行 Draw 程序,拖动鼠标就可以在窗口中绘制线条了。程序运行结果如下图所示。

利用MFC的CDC类实现画线功能

MFC为我们提供一个设备描述表的封装类CDC,该类封装了所有与绘图相关的 *** 作。该类提供一个数据成员m_hDC,用来保存与CDC类相关的DC句柄,其道理与CWnd类提供m_hWnd成员变量保存与窗口相关的窗口句柄的道理是一样的。如例5-10所示就是利用MFC的CDC类实现画线功能的代码。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
	/*
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	// 首先获得窗口的设备描述表
	HDC hdc;
	hdc = ::GetDC(m_hWnd);
	// 移动到线条的起点
	MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
	// 画线
	LineTo(hdc, point.x, point.y);
	// 释放设备描述表
	::ReleaseDC(m_hWnd, hdc);
	*/

	CDC* pDC = GetDC();
	pDC->MoveTo(m_ptOrigin);
	pDC->LineTo(point);
	ReleaseDC(pDC);

	CView::OnLButtonUp(nFlags, point);
}

为了能更好地理解使用CDC类和使用SDK函数实现画线功能的异同,这里并未删除先前利用SDK函数编写的画线代码,只是将其注释起来,这样可以更直观地比较这两种实现方式。从以上代码可以看出,在利用MFC类实现画线功能时,首先需要定义一个CDC类型的指针,并利用CWnd类的成员函数GetDC获得当前窗口的设备描述表对象的指针;然后利用CDC类的成员函数MoveTo和LineTo完成画线 *** 作;最后调用CWnd类的成员函数ReleaseDC释放设备描述表资源。编译并运行Draw程序,拖动鼠标同样可以在窗口中绘制线条。

提示: 因为CWnd类提供了成员函数GetDC和ReleaseDC,因此上面在利用 SDK 函数实现画线功能时,这两个函数前面都加上了两个冒号,表明它们是全局SDK函数。否则,VC++编译器将认为它们是CWnd类的成员函数。

 利用MFC的CClientDC类实现画线功能

下面再介绍一种画线的实现方法,这里将利用MFC提供的CClientDC类来实现这一功能。这个类派生于CDC类,并且在构造时调用GetDC函数,在析构时调用ReleaseDC函数。也就是说,当一个CClientDC对象在构造时,它在内部会调用GetDC函数,获得一个设备描述表对象;在这个CClientDC对象析构时,会自动释放这个设备描述表资源。这样的话,如果在程序中使用CClientDC类型定义DC对象,就不需要显式地调用GetDC函数和ReleaseDC函数了。只需要定义一个CClientDC对象,就可以利用该对象提供的函数进行绘图 *** 作了。当该对象的生命周期结束时,会自动释放其所占用的设备资源,这就是CClientDC对象的好处。如下所示就是利用CClientDC类实现画线功能的代码。

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

	CView::OnLButtonUp(nFlags, point);
}

如上所示的代码中,在构造CClientDC对象时,需要一个CWnd类型的指针作为参数。如果这时我们想在视类窗口中绘图,就应该传递 CDrawView 对象的指针。在前面讲述C++知识时就曾经介绍过,每个对象都有一个this指针指向自己本身。因为本例想要构造一个与视类窗口有关的CClientDC对象,所以就把代表视类对象的this指针作为参数传递给了该对象的构造函数。

提示: 这里的CClientDC类型的变量(dc)是一个对象,因此使用点 *** 作符(.)来调用该对象的函数。

可以注意到,利用CClientDC类绘图时,因为该对象在析构时会自动调用ReleaseDC函数释放设备资源,所以不需要再去调用这个函数了。编译并运行Draw程序,拖动鼠标同样可以在窗口中绘制线条。

以上几种实现方式都可以实现在程序窗口的客户区绘制线条,但是发现它们都不能在工具栏和菜单栏上画线。另外,上面代码构造了一个与视类相关的CClientDC对象,那么如何构造一个与视窗口的父窗口相关的CClientDC对象呢?视窗口的父窗口就是框架窗口,即与CMainFrame类相关联的窗口。前面已经讲述过,在编写程序时,如果不知道某个函数的具体名称,那么可以根据函数的功能来猜测其名称。例如,这里想要获得父窗口的指针,那么我们可以猜测其函数名是不是“Get”加上“Parent”,于是以此名称在 MSDN 中查找,发现确实存在一个这样的函数,仔细阅读该函数的帮助信息,发现它就是我们所需要的那个函数。在实际编程时,可以试试这种猜测方法,相信会大有帮助。现在,将上面所示代码中构造CClientDC对象的代码替换为下面这行代码。

	CClientDC dc(GetParent());

编译并运行Draw程序,拖动鼠标在窗口中绘制线条,发现此时线条可以画到程序的工具栏上,如下图所示。

视类窗口和框架窗口的位置关系:整个程序窗口就是框架窗口,而工具栏以下的白色区域部分才是视类窗口。视类窗口只有客户区(即视类窗口本身),而框架窗口既有客户区(即菜单栏以下部分),也有非客户区(就是程序运行界面中的标题栏和菜单栏)。而绘图 *** 作一般都是在窗口的客户区进行的。因此,对上面所示的代码来说,因为其构造的设备描述表与视类窗口相关,所以程序只能在视窗口的客户区中画线。而如果构造的设备描述表与框架窗口相关,那么就可以在工具栏上绘制图形了,因为工具栏所在位置也属于框架窗口的客户区。

为什么线条出现在工具栏上时,线条的起点都是同一个点。要想在工具栏上画出线条,那么鼠标左键按下的位置就要在工具栏上,此时,视类窗口能否接收到 WM_LBUTTONDOWN 消息呢?如果接收不到,那么代表线条起点的成员变量m_ptOrigin的值应该是多少?想清楚这些问题,就能知道答案了。

提示: 工具栏是浮动的,可以利用鼠标把它拖动到程序窗口的任意位置处。

接上:VC++ OnWndMsg 函数(wincore.cpp) 接下:VC++ 利用MFC的CWindowDC类实现画线功能 在桌面窗口中画线 绘制彩色线条 CPen nPenStyle nWidth crColor

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/735353.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存