您的位置:首页 > 其它

第四课:MFC画图-MFC消息映射机制的剖析

2011-01-05 21:04 253 查看
MFC消息映射机制的剖析,讲述如何运用ClassWizard,,理解发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的函数进行响应的。掌握设备描述表及其封装类CDC的使用,CDC是如何与具体的设备发生关联的,融合具体的画图程序进行分析。如何设置封闭图形的填充刷子(位图画刷与透明画刷的使用)。
主要介绍一些绘图方面的知识。
程序实例:
新建工程:->MFC AppWizard[exe] 文件面为Draw 选择单文档的应用程序 完成之后编译运行。
画直线、画图、画刷画位图、空白画刷操作步骤[/b]
l 明确画图的思路:按下鼠标左键画图的原点,拖动鼠标到另外一个位置松开鼠标左键画图的终点。
l 画图需要消息的捕获。捕获的对象是鼠标左键按下WM_LBUTTONDOWN和抬起WM_LBUTTONUP的消息。
l 这两消息的捕获、响应在窗口类中进行,可以在框架类Frame类中也可以在视类View类中。
l 要对消息进行捕获我们需要设置一些额外的函数,这些函数MFC都有提供。如我们在CMainFrame上点击右键选择增加windows消息处理Add windows message handler……,选择WM_LBUTTONDOWN点击“Add Handler”添加而后点击“Edit Existing”退出。CMainFrame message handlers在CMainFrame最底下被创建。具体内容详见下面程序代码。(这段代码添加的位置是在implementation of the CMainFrame class中,文件名为CMainFrame.cpp)
l 为了简单说明每一步的作用这个程序做出的反应,在消息响应函数中添加一个MessageBox程序代码,这个MessageBox要比先前的WinApi中使用的少一个” HWND hWnd,”参数。MessageBox在CWnd中是属于其中的一个成员函数,他不需要句柄因为有一个数据成员保存了和这个窗口相关的句柄。这里只有一个MessageBox函数在Run的时候没有弹出来!具体原因看注意。
l 作为对比我们在CDrawView中也添加一个消息响应函数WM_LbuttonDown,同样设置一个MessageBox函数,这个函数的参数LpszText为”View click!”。Run我们发现这个消息弹出来了。
l 把框架类的的对WM_LBUTTONDOWN响应的OnLButtonDown函数删除。删除方法看注意。
l 查看当我们增加WM_LBUTTONDOWN这个消息响应的时候在头文件和源文件里边都增加了些什么。增加的程序代码详见5 。当我们增加一个消息响应函数的时候会在三个地方增加代码,头文件中消息原型声明、源文件中跟消息响应函数关联的宏、源文件中消息响应函数。这里在构造函数中给成员变量赋初值。
l MFC中的消息响应。粗略的来看MFC中的消息响应跟WinAPI中消息响应不一样没有调用GetMessage函数,也没有调用回调函数进行消息响应,MFC中对消息的处理方法为消息映射。
l 继续画图。我们调用的OnLButtonDown(UINT nFlags, CPoint point)里边的CPoint类,这个类就是代表了一个点。当我们按下鼠标左键这个点的参数已经通过CPP的对象传递进来,所需要做的就是在这个响应函数中把这个点的参数保存。做法:View类中增加一个私有成员变量 CPoint m_ptOrigin ,View构造函数中将这个点初始化为零,在On_LButtonDown中用给这个点的赋值语句” m_ptOrigin=point;”代替MessageBox函数保存点的参数,见程序段8
l 对终点的响应,也就是对鼠标左键弹开的响应。要增加消息响应函数WM_LBUTTONUP.。
l 添加变量HDC hdc ,用hdc保存获取到的View类窗口所表示的窗口的句柄,获取窗口句柄的程序段:hdc=::GetDC(m_hWnd);
l 作图函数:MoveToEx 。 先将点移动到源点然后再到终点,画线用LineTo划一条线。至此用API函数画图就已经结束了。相关的程序段见10
l MFC[/b]给我们封装了一个画图的代码[/b]。MFC将所有跟窗口相关的封装到了CWND中所有跟左图相关的都封装到了CDC中。用MFC提供的封装函数CDC[/b]进行画图
l 第三种做图的方式CClientDC[/b]封装函数作图。
l 如果我们想要获得View窗口的父窗口指针即Frame框架窗口指针(句柄),怎么做呢?看注意。
l 第四种作图方式:CWindowDC [/b],与CClientDC一样在构造的时候自己调用GetWindowDC再析构的时候自己调用ReleaseDC 。不过CWindowDC作图区域范围更广了可以在非客户区域作图。CWindowDC派生于CDC。
l 画彩色的线条,先前画的都是黑色的主要是因为在设备描述表中有一个缺省的画笔颜色为黑色。我们要创建一个新的画笔并放到设备描述表中就可以用新的画笔的颜色画画图。画笔用CPen[/b],将画笔注册到注册表用的是SelectObject[/b]
l 用画刷画矩形,画刷CBrush,矩形CRect(Rectangular),填满区域FillRect 。
l 用CBrush( CBitmap* pBitmap )[/b]创建一个带位图的画刷;用MSDN查看位图CBitmap[/b]函数。这个画刷画的是位图,填出的是位图。
l 空白画刷画图。首先画矩形用的参数Rectangle(CRect[/b](m_ptOrigin,[/b]point));(Rectangle有两种调用参数的形式)。这个比较复杂些详见注意中的一些介绍。
l 画连续的线,我们需要加入一个消息响应函数WM_MOUSEMOVE , 增加一个BOOL型View中的变量,这个变量要在构造函数中给这个变量赋初值为FALSE ,在WM_LBUTTONDOWN中定义为TRUE,在WM_LBUTTONUP中定义为FALSE,在WM_MOUSEMOVE中编写程序代码。
l 画不带边框的扇形,需要再定义一个CPoin的变量,并在构造函数中赋初值为零,在WM_LBUTTON中给他赋值,在WM_MOUSEMOVE中编写程序代码。
l 采用绘画的模式SetROP2进行画图。很有趣的。

注意:[/b]
l 对于CWnd中的Message因为他的参数规定了很多的缺省值因此可以简写。
l 在框架类中如果只有一个Message函数消息框不能弹出,消息捕获失败;在View类只有一个Message函数但消息框弹出成功,证明消息捕获成功。原因为:View类窗口覆盖在Frame类窗口之上,我们在窗口中操作始终是在View类窗口里,Frame窗口在底层,移动鼠标的动作是感知不到的,因此就无法捕捉消息。
l 增加相应函数还有一种办法:View->MFC ClassWizard -; ClassWizard是MFC的类的向导,这里边除了可以增加消息的响应函数外还可以增加命令的响应函数,也可以重载虚函数;
l 注意函数的删除,如果这个函数是通过添加的方式增加的,不要直接在CPP文件中删除,因为这样删除不能把内部关系删除干净。删除的办法为在该函数上点右键->Delete 删除就OK了。
l 消息映射[/b]:方式一:在基类中针对每一种消息设置一个虚函数,子类要对这个消息进行响应只需重写这个虚函数,当我们调用这个函数时根据函数的多态性子类有的调用子类的子类没有的调用基类的,也可以利用这个消息路由到消息执行的函数,如果用虚函数MFC要有100多个虚函数这样会对我们电脑资源造成很大的浪费,MFC有一个关于窗口句柄和窗口指针的对照表(即句柄与CPP类指针的映射),消息结构体中第一个参数就是指向与此消息相关的窗口,根据这个句柄查找对照表找到对应对象的指针,通过这个指针传递给基类由基类来调用这个函数WindowProc(),我们可以通过以下路径来查找这个函数“程序所安装的磁盘名称:/Softs/Microsoft Visual Studio/VC98/MFC/SRC”搜WindowProc,找到“WINCORE.CPP”这个是CWND的核心代码,双击打开后搜索“WindowProc”找到的代码见6,WindowProc调用OnWndMsg函数来完成消息映射的具体处理。OnWndMsg根据消息的结构体相关的参数回到相应的头文件中需找在DECLARE_MESSAGE_MAP()之前有没有消息响应函数原型的声明有则调用无则调用基类,如果有则到源文件中在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间有没有消息响应映射的宏,找到了这个消息响应函数就会调用这个消息响应函数进行处理,如果没有此类信息就会交由基类进行处理。
l hdc=::GetDC(m_hWnd);[/b]的注释两个问题一个是为什么用[/b]’::’[/b]二是为什么直接用m_hWnd[/b],而不对他进行声明![/b]用’::’[/b]因为我们引用的win32的全局函数也就是SDK的函数,这里需要明确GetDC的来源,否者就会认为我们用的是CWnd派生出来的CView中的成员函数,CWnd有自己的GetDC这个成员函数;m_hWnd[/b],因为系统会为派生类自动命名他的参数,自动命名的方式就是在系统提示的行数参数前面增加‘m_’,以’m_*(系统的默认参数名)’作为派生类的参数名称,这个是系统自动生成的;二是:CWND派生出来的类都有一个成员保存跟这个CPP类相关的窗口句柄,CDrawView使用CWND派生出来了,因此CDrawView有一个成员保存了跟CDrawView相关的窗口的句柄,因此可以直接用m_hWnd这个名字。(这里用的都是HDC,HDC不是MFC中而是CWND的,因此调用函数与HDC使用的也都是CWND的,而这些函数在MFC中也有封装但是与CWND的有一定的差别,因此我们要定义好这些函数的源。)
l 关于MoveTo中lpPoint参数,他要求的是旧的当前的点,因为我们用的是划直线,因此这个参数我们现在用不着所以设置这个参数为NULL 。
l 我们在MFC中作图可以用win32的API函数也可以直接用MFC封装的函数。我们用MFC封装的函数的时候,所有的函数都是CWnd的成员函数,不用双冒号’::’来明示函数来源。
l 不论用的是HDC还是用的CDC都不要忘记用ReleaseDC函数来释放,不同的这两个一个要用双冒号,一个粗需要用。
l CClientDC[/b]封装函数作图[/b]。它定义的是一个对象,虽然MSDN中定义的为一个指针,虽然我们使用指针构造的,但是他是一个对象,既然为对象就用’.’操作符,不用指针操作符。另外这个定义后的对象作用于CView,用this[/b]指针就可以指向它本身。自动调用GetDC,在程序周期结束析构的时侯自动掉ReleaseDC。
l 如果在View内想要获得Frame窗口的指针,我们可以根据语义拼写一个函数GetParent ,而后将这个函数名拿到MSDN中查找看看有没有适合的。一般都可以找的合适的。
l 用此绘图发现指针有点不准。可以用程序代码中13的的坐标参数增加值 X+2,Y+25,只是糊涂正好为鼠标的顶端。
l View的客户区就是那个白框,画图不会画到工具栏上,而Frame类的客户区包含了工具栏,所以会画到菜单栏上。View类没有非客户区。而Frame框架类有非客户区域,非客户区域就是标题栏和菜单栏。如果修正了参数的13程序代码那么也解决了画图会超出View类客户区域的问题。作图只能做到客户区。
l 对于CWindowDC如果不改变参数值,可以将直线画到非客户区域。
l 调用windows桌面句柄,有CWindowDC画图,可以画到桌面上。掉用桌面句柄用HWND GetDesktopWindow(VOID);直接输入GetDesktopWindow(VOID);
l 不论获取的句柄是谁的,画图的起点和终点只有都在View[/b]窗口区域内鼠标的左键按下和弹起的WM[/b]消息才能捕捉到。[/b]
l 除了获取初始Origin[/b]点之外,大部分的响应都是在获得WM_LBUTTONUP[/b]这个消息之后做出的,因此画线的函数都在OnLButtenDown[/b]中。[/b]
l SelectObject[/b]他的意义为指向被替换掉的画笔、画刷等的指针。A pointer to the object being replaced
l PS_DASH[/b]、PS_DOT[/b]、PS_DASHDOT[/b]、PS_DASHDOTDOT[/b]这里几种笔画线的时候线宽必须是1。
l CBitmap[/b]不能直接构造一个有用的位图,需要调用初始化函数和加载位图;我们用 LoadBitmap 就必须有一个资源可以加载。MFC中添加资源的方法:Insert->Resources此时弹出一个对话框。添加资源的另外一种方法:在工具栏上点击右键选择Resources勾选后在工具栏就出现加载资源的快捷工具。当我们选择加载一个新的资源时左侧就进入了Resources栏,展开的就是新加载资源的ID号。
l 我们不用空白画刷填充的时候我们画出来的图是层层覆盖,[/b]新画的要覆盖先前画的图。如果不想这样就需要选择空白的画刷!但是在MSDN中查找时发现在CBrush中没有可以用空白画刷的,但是在第一课我们用过GetStockObject这个函数有NULL_BRUSH这个空白画刷的使用,但是我们发现GetStockObject返回的是一个HGDIOBJ句柄,而CBrush使用的是一个指针如何使用这个函数呢?我们继续看MSDN我们发现了一个FromHandle,这个函数的意义是将从windows返回的HBrush句柄转换成一个CBrush的指针,这里有一个问题那就是GetStockObject返回的句柄不是HBrush的,因此我们还需要进行强制转化。CBrush::FromHandle[/b]这个类没有封装到MFC中,MFC要用这个函数的时候要注明函数的来源。可以试试不加CBrush::或者不加Cbrush的情况,这两种方法是不可以的,FromHandle不是全局变量。冒号用来索引[/b]关于那个双冒号准确的说明看下边的表格

No.1
No.2
No.3
No.4
No.5
#include <iostream.h>
class point
{
public:
void output()
{ }
static void init()
//静态的定义
{ }
};
void main()
{
point pt;
pt.init();
pt.output();}
#include <iostream.h>
class point
{
public:
void output()
{ }
static void init()
{ }
};
void main()
{
point::init();
point::output();
}
#include <iostream.h>
class point
{
public:
void output()
{ }
static void init()
{ x=0;y=0; }
private:
int x,y;
};
void main()
{
point::init();
}
#include <iostream.h>
class point
{
public:
void output()
{ x=0;y=0;
init();}
static void init()
{ }
private:
static int x,y;
};
void main()
{ point::init();}
#include <iostream.h>
class point
{
public:
void output()
{ }
static void init()
{ x=0;y=0; }
private:
Static int x,y;
};
void main()
{
point::init();
}
No.1、pass;
No.2、Fail:error C2352: 'point::output' : illegal call of non-static member function see declaration of 'output'在point::output()这里出错,对非静态成员函数的非法调用。但是对静态成员initial()的调用就没有报错。
No.3、报错,静态成员引用了非静态成员。非静态成员在没有产生实例化的对象之前没有分配内存空间,静态成员虽然分配了空间但是没有调用的参数没有分配那么怎么能给这些参数赋值呢!同样静态成员函数也不能调用非静态成员函数。这个报错是在编译的时候就报了。
No.4、通过,非静态的成员可以调用静态的成员。
No.5、这个报错,但是这个错误是在link链接的时候报错,编译的时候通过了。报错的内容如下:
unresolved external symbol "private: static int point::x unresolved external symbol "private: static int point::y
这里如果静态的成员变量想要被调用就必须在类的外边进行初始化。
No.6、初始化后的程序如下,运行通过。

No.6
No.7
#include <iostream.h>
class point
{
public:
void output()
{ init();}
static void init()
{ x=0;y=0; }
private:
static int x,y;
};
int point::x=0; int point::y=0;
void main()
{ point::init(); }
#include <iostream.h>
class point
{
public:
void output()
{ init();}
static void init()
{ x=0;y=0; }
private:
static int x,y;
};
void main()
{
}
另外有一个细节问题,见NO.7我们没有对静态参数x,y进行初始化,但是只要我们在main函数中不调用引用为初始化的静态变量的静态函数成员,我们的编译连接器都不会报错。这个问题是隐含的,以后肯定会报错那是除了问题就很难解决。
问题分析:非静态成员函数在其所在的类中如果没有实例化为某个对象系统是不会给他分内存空间,而静态成员函数只要这个类存在了他就和类一起被系统分配了一个内存空间。当我们想要调用某个成员函数,只有在这个成员函数实实在在的存在于这个内存空间里才可以被访问被调用。Static静态的方法不属于某个类的对象而是属于类本身,类被加载了这个static函数也就加载了,非静态的成员函数是属于对象的。
对进FromHandle[/b]行Go to definition 操作,我们可以发现 static[/b] CBrush* PASCAL FromHandle(HBRUSH hBrush); FromHandle[/b]在定义的时候就是静态的。
l 用FillRect( LPCRECT lpRect[/b], CBrush* pBrush );画矩形,pBrush不能为空。他因为有了这个指针参数所以不用使用设备描述表中的默认设备,他也不会对设备描述表进行任何的修改。

小结:[/b]
l HDC与CDC在找源点的函数名不同,格式也不同具体区别见下表:
CDC::MoveTo[/b]
HDC [/b]用MoveToEx[/b]
This method moves the current position to the point specified by x and y, or by point.

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

MoveTo可以采用点point代替(x,y),因为MoveTo是MFC封装的因此不需要插入句柄的参数,因为调用MoveTo的函数已经明确了句柄。MoveToEx则不同,调用这个函数必须指明句柄,否则不知道是哪个窗口的源点。
MoveToEx
The MoveToEx function updates the current position to the specified point and optionally returns the previous position.

BOOL MoveToEx(
HDC hdc, // handle to device context
int X, // x-coordinate of new current position
int Y, // y-coordinate of new current position
LPPOINT lpPoint // old current position
);
l 在MFC中使用MoveTo和LineTo这两个函数时他的参数有两种:一种是填入(x,y)整数值,另一种填入CPoint型的point参数变量。
l 当经行函数名组合查找函数如我们想获得一个窗口的句柄,一般情况下我们第一个字母为Get,后边的是要获取的对象。如要获取父窗口的句柄用GetParent(),获取Windows桌面窗口的句柄可组合为:GerWindowDesktop(),注意使用的时候小括号内都没有内容。
l 如果有的函数不能引用画刷的地址,那么定义了画刷是需要宣导设备描述表中的才能使用。
l 一个关乎SelectObject()括号内参数的说明,SelectObject()括号内用定义为指针的参数。
Pen SelectObject()括号参数
Brush SelectObject()括号参数
CPen pen(PS_INSIDEFRAME,0,RGB(255,0,0));
CClientDC cli(this);
CPen *Oldpen=cli.SelectObject(&pen);
cli.MoveTo(m_ptOrigin.x,m_ptOrigin.y);
cli.LineTo(point.x,point.y);
cli.SelectObject(Oldpen);
这段代码中Pen定义了一个变量pen,这个pen不是指针因此SelectObject调用pen时用取地址的形式&pen。Oldpen是被定义成指针的,SelectObject调用时直接引用Oldpen 。
CBrush *cbs=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CClientDC cli(this);
CBrush *oldcbs=cli.SelectObject(cbs);
cli.Rectangle(CRect(m_ptOrigin,point));
cli.SelectObject(oldcbs);
Cbs是CBrush定义的指针,因此直接写到了SelectObject()口号内
l 程序代码段21、24中一个使用了SelectObject()一个没有使用,原因是dc.FillRect(CRect(m_ptOrigin,point),&cbrush);这个FillRect函数的参数包含了指定的刷子的代码,因此不需要用设备描述表[/b]里注册的,而cli.Rectangle(CRect(m_ptOrigin,point));和{ cldc.MoveTo(m_ptOrigin.x,m_ptOrigin.y);cldc.LineTo(point.x,point.y);}这个组合画图函数中MoveTo 、LineTo 都不具备自己自定调用的画刷和笔,因此都需要用设备描述表中的默认值,如果想用其他笔和刷子就需要用SelectObject这个函数更改设备描述表中的内容
l SelectObject()一会儿调用笔、一会儿调用刷子,他怎么可以通用呢???这个跟SelectObject()的参数定义有关,微软给SelectObject()定义了很多如:CPen* pPen、CBrush* pBrush、CFont* pFont、CBitmap* pBitmap、CRgn* pRgn等参数,可以直接使用SelectObject()掉要这些作为参数。SelectObject()除了CRgn* pRgn之外返回的都是指针值。
l CPen定义了一种笔是不是只能用一次呢?
l 加入画图的时候你的鼠标移到窗口外边释放了鼠标左键结果会怎么样呢????这是一个BUG。很好玩的。

程序代码段:[/b]
1、WM_LBUTTONDOWN[/b]
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_LBUTTONDOWN
WPARAM wParam, // key indicator
LPARAM lParam // horizontal and vertical position
);
2、WM_LBUTTONUP[/b]
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_LBUTTONUP
WPARAM wParam, // key indicator
LPARAM lParam // horizontal and vertical position
);
3、CWnd::MessageBox[/b]
This method creates and displays a window that contains an application-supplied message and caption, plus a combination of the predefined icons and pushbuttons described in the Message-Box Styles list. Use the global function AfxMessageBox instead of this method to implement a message box in your application.
int MessageBox (
LPCTSTR lpszText, // text in message box 消息显示的文本
LPCTSTR lpszCaption = NULL, // message box title 消息的标题,即在消息的蓝框中的内容显示
UINT nType = MB_OK );

WinApi的MessageBox
int MessageBox(
HWND hWnd, // handle to owner window 消息框被创建那个窗口拥有这个消息框的句柄
LPCTSTR lpText, // text in message box 消息显示的文本
LPCTSTR lpCaption, // message box title 消息的标题,即在消息的蓝框中的内容显示
UINT uType // message box style 消息框的类型,如MB_OK表示消息框包含了一个OK按键
的button按钮。MB_OK定义的是”0”,我们可以直接用”0”代
替MB_OK . 此处如果写零,不需要加引号
4、添加WM_LBUTTONDOWN[/b]自动生成的程序代码[/b]:(这个是对鼠标左键消息响应的一个函数)
/////////////////////////////////////////////////////////////////////////////此处为消息响应实现代码。
// CMainFrame message handlers
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point[/b]) //这个里边的这个point参数经常会用到
{
// TODO: Add your message handler code here and/or call default
MessageBox("Left Button Down"); //但对这行代码在Frame框架中不能弹出窗口。在View框架中弹出
CFrameWnd::OnLButtonDown(nFlags, point);
}
5、头文件中增加的代码:[/b]
protected:
//{{AFX_MSG(CDrawView) //注释宏
afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //因为是在两个注释宏中间所以这个函数原型声明为灰色
// afx_msg也是宏,她表示此函数原型为消息响应函数原型的声明。
//}}AFX_MSG //注释宏
DECLARE_MESSAGE_MAP() //声明消息映射
源文件中增加的代码声明:[/b]
IMPLEMENT_DYNCREATE(CDrawView, CView)
BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN() //这是一个宏。WN_LBUTTONDOWN是一个消息,
//前边增加一个ON_就变成一个宏。
//这个宏将WM_LBUTTONDOWN消息和OnLButtonDown这个消息响应函数关联
//起来。一旦有消息产程就会调用消息响应函数来处理。
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
6、WindowProc[/b]函数段[/b]
// main WindowProc[/b] implementation

LRESULT CWnd::WindowProc[/b](UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg[/b](message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
对WindowProc 进行Go to definition多次后看到这个函数在基类中的定义是一个虚函数。
virtual LRESULT WindowProc[/b](UINT message, WPARAM wParam, LPARAM lParam);
virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
7、构造函数[/b]中将点初始化为0[/b]:[/b]
CDrawView::CDrawView()
{
// TODO: add construction code here
m_ptOrigin=0;
}
8、进行语句替换后的On_LButtonDown[/b]
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_ptOrigin=point;
CView::OnLButtonDown(nFlags, point);
}
9、MoveToEx [/b]作图专用的函数[/b]
MoveToEx[/b]
The MoveToEx function updates the current position to the specified point and optionally returns the previous position.
BOOL MoveToEx[/b](
HDC hdc, // handle to device context
int X, // x-coordinate of new current position
int Y, // y-coordinate of new current position
LPPOINT lpPoint // old current position 这是一个指针,在本节我们把这个参数设置为空NULL
);
10、用API[/b]函数画图的代码段[/b]
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_ptOrigin=point; //赋源点的值。
CView::OnLButtonDown(nFlags, point);
}
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
HDC hdc;
hdc=::[/b]GetDC(m_hWnd);
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL[/b]);
LineTo(hdc,point.x,point.y);
::ReleaseDC(m_hWnd,hdc);
CView::OnLButtonUp(nFlags, point);
}
11[/b]、[/b]第一种用MFC[/b]提供的封装函数进行画图的函数代码 [/b](CDC[/b]、ReleaseDC[/b]、GetDC[/b])[/b]
CDC::CDC [/b](COBJECT->CDC[/b]) [/b]Constructs a CDC object
This constructor creates an instance of a CDC object.
CDC( );
CWnd::ReleaseDC[/b]
int ReleaseDC( CDC* pDC );
Return Value
Nonzero if successful; otherwise 0.
Parameters
pDC
Identifies the device context to be released.
CWnd::GetDC [/b]
CDC* GetDC( );
Return Value
Identifies the device context for the CWnd client area if successful; otherwise, the return value is NULL. The pointer may be temporary and should not be stored for later use.
MFC[/b]提供的封装函数进行画图的函数代码[/b]
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CDC *cdc=GetDC(); //其实这里用pDC更好一些,以为在ReleaseDC()提供的提示就是pDC很方便。
//GetDC()括号内为空,因为这个函数获取的sahib当前的。
cdc->MoveTo(m_ptOrigin); //因为CDC定义的参数cdc为指针,因此当指向其他目标的时候用’->’符号。
cdc->LineTo(point); //这两个都是引用MFC的封装函数。
ReleaseDC(cdc);
CView::OnLButtonUp(nFlags, point);
}
上边兰体字代码段可以改为:[/b]
CDC *cdc;
cdc=GetDC();
cdc->MoveTo(m_ptOrigin);
cdc->LineTo(point);
ReleaseDC(cdc);
12[/b]、[/b]第二种用MFC[/b]提供的封装函数进行画图 CClientDC[/b]作图代码段:[/b]
CClientDC::CClientDC [/b]
CClientDC( CWnd* pWnd );
throw( CResourceException );
Parameters
pWnd
The window whose client area the device context object will access.

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) //[/b]这段是VC++[/b]添加的。[/b]
{
// TODO: Add your message handler code here and/or call default
/* 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 *cdc=GetDC();
cdc->MoveTo(m_ptOrigin);
cdc->LineTo(point);
ReleaseDC(cdc); */
CClientDC cli(this[/b]);
cli.MoveTo(m_ptOrigin);
cli.LineTo(point);

CView::OnLButtonUp(nFlags, point);
}
13、参数值的修正 x+2, y+25 [/b]
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// CClientDC cli(this); //在View窗口画图。
CClientDC cli(GetParent()); // 在Frame窗口画图。
cli.MoveTo(m_ptOrigin.x+2[/b],m_ptOrigin.y+25[/b]);
cli.LineTo(point.x+2[/b],point.y+25[/b]);
CView::OnLButtonUp(nFlags, point);
}
14[/b]、[/b]第三种用MFC[/b]提供的封装函数进行画图 CWindowDC[/b]调整到适合的参数后的代码段:[/b]
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CWindowDC cli(GetParent()); //Frame
cli.MoveTo(m_ptOrigin.x+6,[/b]m_ptOrigin.y+75[/b]);
cli.LineTo(point.x+6[/b],point.y+75[/b]);
CView::OnLButtonUp(nFlags, point);
}
用CWindowDC[/b]在View[/b]窗口画图代码段[/b]
CWindowDC cli(this); //CDrawView
cli.MoveTo(m_ptOrigin.x+2,m_ptOrigin.y+2);
cli.LineTo(point.x+2,point.y+2);
CView::OnLButtonUp(nFlags, point);
用CWindowDC[/b]在View[/b]窗口中画图获取句柄为桌面的代码[/b]
CWindowDC cli(GetDesktopWindow()); //Desktop
cli.MoveTo(m_ptOrigin);
cli.LineTo(point);
CView::OnLButtonUp(nFlags, point);
↑↑↑↑↑↑↑↑↑↑↑以上用的都是系统默认的笔进行画图↑↑↑↑↑↑↑↑↑↑↑↑↑
===============================================================================
↓↓↓↓↓↓↓↓↓↓以下用的都是自定义的笔、画刷进行画图↓↓↓↓↓↓↓↓↓↓↓↓
15[/b]、CPen[/b]的函数结构及其用到的参数:(CPen[/b]、RGB)[/b]
CPen::CPen [/b]
CPen( );
CPen( int nPenStyle, int nWidth, COLORREF crColor )[/b];
CPen( int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL )[/b];
RGB[/b]
The RGB macro selects a red, green, blue (RGB) color based on the arguments supplied and the color capabilities of the output device.
COLORREF RGB(
BYTE byRed, // red component of color
BYTE byGreen, // green component of color
BYTE byBlue // blue component of color
);
如:RGB([/b]255[/b],[/b]255[/b],[/b]255)[/b]
16、SelectObject[/b]函数结构 [/b]Selects a GDI drawing object such as a pen 将设备描述表里边的Pen获取保存,把他选择的刷子注册到设备描述表中。
CDC::SelectObject .
CPen* SelectObject( CPen* pPen );
CBrush* SelectObject( CBrush* pBrush );
virtual CFont* SelectObject( CFont* pFont );
CBitmap* SelectObject( CBitmap* pBitmap );
int SelectObject( CRgn* pRgn );
Return Value A pointer to the object being replaced[/b]他们的返回值就是一个被替换的指针,如果不保存这个返回值将丢失
17[/b]、[/b]用CPen[/b]和SelectObject[/b]的程序代码[/b]
……
CPen pen(PS_DASHDOT,0,RGB(255,0,0)); // PS_DASHDOT画刷的类型
CClientDC cldc(this);
CPen *Oldpen=cldc.SelectObject(&pen); //将选中的画笔送到设备描述表中,并将设备描述表中的笔送到Oldpen;
cldc.MoveTo(m_ptOrigin.x,m_ptOrigin.y);
cldc.LineTo(point.x,point.y);
cldc.SelectObject(Oldpen); //还原设备描述表中默认的笔;
……
18、用画刷画矩形用到的函数:(CBrush[/b]、CRect [/b]、FillRect)[/b]
CBrush[/b] [/b]
CBrush( );
CBrush( COLORREF crColor ); //19[/b]中使用这个函数来用单一的颜色画图[/b]
CBrush( int nIndex, COLORREF crColor );[/b]
CBrush( CBitmap* pBitmap ); [/b] //21[/b]中使用这个函数来画位图指针;[/b]
Cbrush *cbrush ; //[/b]不带参数只能定义为指针。[/b]
CRect [/b]
CRect::CRect
CRect( );
CRect( int l, int t, int r, int b );
CRect( const RECT& srcRect );
CRect( LPCRECT lpSrcRect );
CRect( POINT point, SIZE size );
CRect( POINT topLeft, POINT bottomRight ); //[/b]我们用的是这种[/b]
FillRect [/b]Fills a given rectangle by using a specific brush.
CDC::FillRect
This method fills a specified rectangle using the specified brush. The method fills the complete rectangle, including the left and top borders, but it does not fill the right and bottom borders.
void FillRect( LPCRECT lpRect[/b], CBrush* pBrush );
19[/b]、[/b]使用画刷画矩形区域并填充颜色的程序代码段:[/b]
……
CBrush cbrush(RGB(0,255,0));
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin , point[/b]),&cbrush); //我们只是选地址选中一个画刷并没有把他注册到设备描述表中,因此不用SelectObject这个函数
……
20[/b]、[/b]位图CBitmap[/b]用到的函数:(CBitmap[/b]、LoadBitmap)[/b]
CBitmap::CBitmap [/b]
CBitmap( );
Remarks
Constructs a CBitmap object. The resulting object must be initialized with one of the initialization member functions.

CBitmap::LoadBitmap [/b]
BOOL LoadBitmap( LPCTSTR lpszResourceName[/i][/b] );
BOOL LoadBitmap( UINT nIDResource[/i][/b] );
Return Value
Nonzero if successful; otherwise 0.
Parameters
lpszResourceName[/i][/b]
Points to a null-terminated string that contains the name of the bitmap resource.
nIDResource[/i][/b]
Specifies the resource ID number of the bitmap resource.
21[/b]、[/b]用位图画刷画位图程序代码段: ( [/b]包含了一个对比函数)[/b]
……
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
CBrush cbrush(&bitmap[/b]);
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&cbrush);
……
对比程序代码段:
……
CBrush cbrush(RGB(0,255,0)[/b]);
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&[/b]cbrush);
……
22[/b]、[/b]用空白画刷的相关函数定义:(FromHandle[/b]、GetStockObject [/b]、CDC::Rectangle)[/b]
CBrush::FromHandle[/b]
static CBrush* PASCAL FromHandle( HBRUSH hBrush );
Return Value[/i]
A pointer to a CBrush object if successful; otherwise NULL.[/i]
Parameters[/i]
hBrush[/i][/b]
HANDLE to a Windows GDI brush.[/i]
HGDIOBJ GetStockObject ( int fnObject // stock object type );
Parameters[/b]
fnObject
[in] Specifies the type of stock object. This parameter can be one of the following values.

Value
Meaning[/b]
BLACK_BRUSH[/b][/b]
Black brush.
DKGRAY_BRUSH[/b][/b]
Dark gray brush.
DC_BRUSH[/b][/b]
Windows 2000/XP: Solid color brush. The default color is white. The color can be changed by using the SetDCBrushColor function. For more information, see the Remarks section.
GRAY_BRUSH[/b][/b]
Gray brush.
HOLLOW_BRUSH[/b][/b]
Hollow brush (equivalent to NULL_BRUSH).
LTGRAY_BRUSH[/b][/b]
Light gray brush.
NULL_BRUSH[/b][/b]
Null brush (equivalent to HOLLOW_BRUSH).
WHITE_BRUSH[/b][/b]
White brush.
BLACK_PEN[/b][/b]
Black pen.
DC_PEN[/b][/b]
Windows 2000/XP: Solid pen color. The default color is white. The color can be changed by using the SetDCPenColor function. For more information, see the Remarks section.
WHITE_PEN[/b][/b]
White pen.
ANSI_FIXED_FONT[/b][/b]
Windows fixed-pitch (monospace) system font.
ANSI_VAR_FONT[/b][/b]
Windows variable-pitch (proportional space) system font.
DEVICE_DEFAULT_FONT[/b][/b]
Windows NT/2000/XP: Device-dependent font.
DEFAULT_GUI_FONT[/b][/b]
Default font for user interface objects such as menus and dialog boxes. This is MS Sans Serif. Compare this with SYSTEM_FONT.
OEM_FIXED_FONT[/b][/b]
Original equipment manufacturer (OEM) dependent fixed-pitch (monospace) font.
SYSTEM_FONT[/b][/b]
System font. By default, the system uses the system font to draw menus, dialog box controls, and text.
Windows 95/98 and Windows NT: The system font is MS Sans Serif.
Windows 2000/XP: The system font is Tahoma
SYSTEM_FIXED_FONT[/b][/b]
Fixed-pitch (monospace) system font. This stock object is provided only for compatibility with 16-bit Windows versions earlier than 3.0.
DEFAULT_PALETTE[/b][/b]
Default palette. This palette consists of the static colors in the system palette.
CDC::Rectangle //[/b]
BOOL Rectangle( int x1, int y1, int x2, int y2 );
BOOL Rectangle( LPCRECT lpRect );
Return Value
Nonzero if the function is successful; otherwise 0.
23[/b]、[/b]用默认刷子刷矩形程序代码段:[/b]
……
CClientDC clidc(this);
clidc.Rectangle(CRect(m_ptOrigin,point));
……
24[/b]、[/b]用空白的刷子刷矩形代码段:[/b]
……
CBrush *cbs=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CClientDC cli(this);
CBrush *oldcbs=cli.SelectObject(cbs);
cli.Rectangle(CRect(m_ptOrigin,point));
cli.SelectObject(oldcbs);
……
25、用空白画刷画矩形外加由其他的笔画对角线程序代码段:
……
CBrush *cbs=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CPen pen(PS_DOT,1,RGB(255,0,0));
CClientDC cli(this);
CBrush *oldcbs=cli.SelectObject(cbs);
CPen *oldpen=cli.SelectObject(&pen);
//the other
cli.MoveTo(point.x,m_ptOrigin.y);
cli.LineTo(m_ptOrigin.x,point.y);
cli.SelectObject(oldpen);
//normal
cli.MoveTo(m_ptOrigin);
cli.LineTo(point);
cli.Rectangle(CRect(m_ptOrigin,point));
cli.SelectObject(oldcbs);
……
26、画连续的线的程序代码段:
……
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC mdc(this);
if(m_bDraw==TRUE)
{
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(point);
m_ptOrigin=point;
}
CView::OnMouseMove(nFlags, point);
}
……
27、画扇形不带边线的程序代码段[/b]
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC mdc(this);
if(m_bDraw==TRUE)
{
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(m_ptOldpen);
// mdc.MoveTo(m_ptOrigin);
// mdc.LineTo(point);
m_ptOldpen=point;
}
CView::OnMouseMove(nFlags, point);
}
28、画扇形带边线的程序代码段[/b]
……
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC mdc(this);
if(m_bDraw==TRUE)
{
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(m_ptOldpen);
mdc.MoveTo(m_ptOldpen);
mdc.LineTo(point);
m_ptOldpen=point;
}
CView::OnMouseMove(nFlags, point);
}
……
上面这段代码段不够完美,下边的要完美一些。
……
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC mdc(this);
if(m_bDraw==TRUE)
{
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(m_ptOldpen);
mdc.MoveTo(m_ptOldpen);
mdc.LineTo(point);
mdc.MoveTo(m_ptOrigin); //多了这一段。
mdc.LineTo(point);
m_ptOldpen=point;
}
CView::OnMouseMove(nFlags, point);
}
……
29、绘画的模式函数:(SetROP2[/b])[/b]
CDC::SetROP2
int SetROP2( int nDrawMode );
Return Value
The previous drawing mode.
It can be any of the values given in the Windows SDK documentation.
30[/b]、[/b]采用会话模式的程序代码段:[/b]
……
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC mdc(this);
if(m_bDraw==TRUE)
{
mdc.SetROP2(R2_NOT);
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(m_ptOldpen);
mdc.MoveTo(m_ptOldpen);
mdc.LineTo(point);
mdc.MoveTo(m_ptOrigin);
mdc.LineTo(point);
m_ptOldpen=point;
}
CView::OnMouseMove(nFlags, point);
}
……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: