MFC编程之文档、视图结构应用程序

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第2章文档/视图结构应用程序
2.0 知识要点
1. 文档/视图结构的思想是将数据的管理与显示分离,其中文档用于管理应用程序的数据,而视图是用户界面,用于显示、打印文档中的数据,并管理与用户的交互。

2. SDI具有文档/视图结构的应用程序框架,该框架包括应用程序类、窗口框架类、文档类和视图类,编程的任务是在文档类和视图类中添加适当的代码。

其中文档类中一个重要的成员函数是OnNewDocument(),用户在此添加代码完成对数据的初始化;而视图类中最重要的成员函数是OnDraw(),通过在该函数中添加代码实现窗口内容的输出。

3. 通过介绍几种SDI结构的典型应用程序来了解其结构及设计方法,典型应用包括:
•利用画笔、画刷等工具及位图处理的方法实现文字和图形的输出。

•利用定时器控件制作动画程序。

•对鼠标和键盘进行编程的基本方法。

•文件、字体、颜色等几种通用对话框的使用方法及自定义对话框的设计、调用方法。

•菜单的设计方法。

2.1 文档/视图结构
SDI 应用程序由 AppWizard 创建好后,即使没有编写一行代码,仍然可以运行,并且具有一个标准 Windows应用程序所需要的组成成份,程序员的工作就是往这个框架添加必要的代码。

以下通过一个简单实例说明文档/视图结构应用程序的创建过程。

【例2-1】创建一个如图所示的应用程序,在窗口中显示一个矩形框,框中显示“同舟共济自强不息”。

假定本例的工程名为TEST,程序创建过程如下:
(1)利用 AppWizard 创建一个 SDI 应用程序框架。

(2)文档类是存放数据的主要地方,本例在其中说明一个存放矩形框数据的 CRect 类对象 r 和一个存放字符串的 CString 对象s,方法为:
在工作区的“ ClassView ”中,双击 CTESTDoc 类,在该类代码中添加如下代码:
public:
CRect r;
CString s;
说明:
CRect 是 MFC 定义的一个类,有 4 个数据成员: left 、 top 、 right 和bottom 。

(left, top) 和( right, bottom )分别表示一个矩形左上角坐标和右下角坐标。

(3)在文档类的成员函数 OnNewDocument() 中完成数据成员的初始化。

方法为:在工作区的 ClassView 中展开 CTESTDoc 类,找到其成员函数OnNewDocument() ,在其中添加代码:
BOOL CTESTDoc::OnNewDocument()
{ if (!Cdocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
r=CRect(100,60,20,100);
s=”同舟共济自强不息”;
return TRUE;
}
(4)在工作区的“ Class View ”中展开视图类 CTESTView 类,找到其成员函数OnDraw() ,添加输出矩形框和文字的代码:
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->Rectangle(pDoc->r);
pDC->TextOut(110,70,pDoc->s);
}
说明:
①GetDocument() 是视图类的一个重要成员函数,其返回值是指向当前文档的指针。

视图类中的成员函数通过它访问文档类的公有数据成员。

②函数 OnDraw() 是实现输出的关键函数,绝大多数的显示工作都是在这个函数中完成的。

其参数 pDC 指向窗口中央的客户区对象,程序使用了客户区对象的 Rectangle 和 TextOut 函数绘制矩形和输出文字。

(5)运行程序,结果如图所示。

SDI 应用程序框架
1.应用程序类
每个应用程序类必须从 CWinApp 派生出自己的应用程序类,该类封装了包括初始化、运行和结束的整个过程,其名称是 AppWizard 根据工程名称自动命名的,规则如下:(1)如果工程名符合标识符的命名规则,则应用程序类名由字母 C 、工程名和 App 三部分组成。

例如,工程名为 TEST ,则应用程序类名为 CTESTApp 。

(2)如果工程名以数字开始,则应用程序类名由字母 Cmy 、工程名和 App 三部分组成。

例如,工程名为 1TEST ,则应用程序类名为 CMy1TESTApp 。

(3)忽略工程名的汉字。

文档类和视图类的命名规则与应用程序类类似,区别在于在后面分别添加了 Doc 和View 。

2.窗口框架类
从 CMainWnd 派生,提供了一个 SDI 窗口的所有功能,如显示一个标题、一个菜单栏、一个工具栏等。

所有 SDI 应用程序窗口框架类都是 CMainFrame ,但是性质是有区别的。

3.文档类
从 CDocument 类派生出来,是应用程序进行数据定义和初始化的地方。

文档类的数据成员只有被说明成公有成员才能被视图类中的成员函数访问,其重要成员函数是OnNewDocument(),是进行数据成员初始化的地方。

程序开始时会自动调用该函数。

4.视图类
从 CView 中派生出来,是程序的用户界面,用于显示、打印存储在文档类对象中的数据,并管理与用户的交互。

重要成员函数有:
(1) GetDocument() 函数
该函数的返回值是指向与当前视图相关联的文档对象的指针,因而通过它可以访问文档类对象中的公有私有成员。

其一般使用形式为:
CTESTDoc* pDoc = GetDocument();
说明:
①在不同的应用程序中, pDoc 所指向的文档类对象的名称是不一样的。

若工程名称为 TEST ,则文档类的名称为 CTESTDoc ,若工程名为 ABC ,则文档类名为 CABCDoc ,则应用 CABCDoc* pDoc 定义。

②在 OnDraw函数中,这个语句是自动生成的。

在用户自定义的视图类成员函数中,如果需要访问文档类对象的公有数据成员,则应添加这样的语句。

(2) OnDraw() 函数
在视图类中, OnDraw() 是一个很重要的成员函数,用于实现输出。

一般来说,窗口中的内容都是 OnDraw() 输出的。

初始的 OnDraw()函数如下:
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument(); // 获得指向文档类对象的指针ASSERT_VALID(pDoc); // 检查 pDoc 是否有效,若无效结束程序
// TODO: add draw code for native data here
}
说明:
OnDraw()函数有两种调方法:
①自动调用:当窗口发生变化视图需要重新绘制时,应用程序会自动调用该函数。

②手工调用:当程序中的数据改变了需要重新显示时,可以通过调用 Invalidate() 和 InvalidateRect() 函数,引发对 Ondraw() 的调用。

需要注意的是,不是直接调用 Ondraw()函数。

Invalidate(TRUE) // 擦除窗口原有的内容,重新绘制。

Invalidate(FALSE) // 窗口原有的内容保留,再进行绘制。

InvalidateRect( 矩形 , TRUE) // 擦除矩形区域内原有内容,重新绘制这个区域。

InvalidateRect( 矩形 , FALSE) // 矩形区域内原有内容保留,再绘制这个区域。

2.2 图形和文字的输出
窗口中央的客户区是一个输出文字和图形的区域。

简单的文字和图形输出
1.文字输出
形式:
BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount);
说明:
从坐标 (x,y) 开始显示 lpszString 字符串,其中参数 nCount 指定字符串中的字节数。

2.画点
形式:
COLORREF SetPixel(int x, int y, COLORREF crColor);
COLORREF SetPixel(POINT point, COLORREF crColor);
说明:
① POINT 是 MFC 定义的结构类型,表示平面上的一个点。

它的两个重要元素是 x 和y ,分别表示一个点的 x 坐标值和 y 坐标值。

与 POINT 类似的是 CPoint 类,两个数据成员也是 x 和 y 。

② COLORREF 是一个 32 位整数类型,用于表示颜色,其第 0 、 1 、 2 字节分别存放了一种颜色的红、绿、蓝的值。

用三种颜色合成一种颜色的函数是 RGB ,例如:COLORREF C1=RGB(0, 0, 0) ;// 合成黑色
COLORREF C2=RGB(255, 255, 255); // 合成白色
COLORREF C3=RGB(255, 0, 0); // 合成红色
COLORREF C4=RGB(0, 255, 0); // 合成绿色
COLORREF C5=RGB(0, 0,255); // 合成蓝色
COLORREF C6=RGB(255, 255, 0); // 合成亮黄色
COLORREF C7=RGB(0,255, 255); // 合成青色
COLORREF C8=RGB(255, 0, 255); // 合成品红色
COLORREF C9=RGB(0, 0,128); // 合成深蓝色
COLORREF C10=RGB(0, 128, 0); // 合成深绿色
COLORREF C11=RGB(128, 128, 128); // 合成深灰色
COLORREF C12=RGB(128, 128, 128); // 合成浅灰色
③使用指定颜色 crColor 在坐标 (x,y) 或点 point 处画一个点,返回用于画点的颜色。

3.画线
画线需要调用两个函数:首先调用 MoveTo 函数确定线段的起点,然是调用 LineTo 确定线段的终点。

形式:
CPoint MoveTo(int x,int y);
CPoint MoveTo(POINT point);
BOOL LineTo(int x, int y);
BOOL LineTo(POINT point);
说明:(x, y)或 point 是线段的起点或终点坐标。

4.画矩形
形式:
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Rectangle(LPCRECT lpRect);
说明: (x1,y1) 和 (x2,y2) 是所画矩形的左上角和右下角坐标 , lpRect 是指向矩形的指针。

5.画椭圆
形式:
BOOL Ellipse(int x1, int y1, int x2, int y2);
BOOL Ellipse(LPCRECT lpRect);
说明: (x1,y1) 和 (x2,y2) 是椭圆外接矩形的左上角和右下角坐标 , lpRect 是指向外接矩形的指针。

6.获取客户区的大小
使用 Wnd 类的成员函数 GetClientRect 可以将客户区的坐标放入一个 CRect 类的对象中,然后调用成员函数计算到高度和宽度。

形式:
void GetClientRect( LPRECT lpRect ) const;
【例2-2】改变窗口时,矩形框和文字总是显示在窗口的中央。

void CTESTView::OnDraw(CDC* pDC)
{
CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect a,b;
int w=160;
int h=40;
GetClientRect(&a);
b.left=(a.Width()-w)/2;
b.top=(a.Height()-h)/2;
b.right=b.left+w;
b.bottom=b.top+h;
pDC->Rectangle(b);
pDC->TextOut(b.left+10,b.top+10,"同舟共济、自强不息!");
}
【例2-3】编写一个程序,绘制如图所示的 -2π~2π 之间的 sin 曲线。

因为需要使用 sin() 函数,所以在 TEST.CPP 的开始添加文件包含命令。

#include "math.h"
程序代码:
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rect;
GetClientRect(rect); //获得整个客户区的参数
int x0=rect.Width()/2; //客户区的横向中点
int y0=rect.Height()/2; //客户区的纵向中点
pDC->MoveTo(20,y0); //确定横线起点
pDC->LineTo(rect.Width()-20,y0); //确定横线终点
pDC->MoveTo(x0,20); //确定纵线起点
pDC->LineTo(x0,rect.Height()-20); //确定纵线终点
double step=3.14159/100;
for(int i=-200;i<200;i++ )
{
double x=x0+(i/300.0)*rect.Width()/2;
//上面表达式中除以300是为了控制宽度
double y=y0-sin(step*i)*rect.Height()/4;
//上面表达式中除以4,一半是为了控制高度
pDC->SetPixel(x,y,RGB(255,0,0));
//RGB(255,0,0)是表示红色
}
}
去掉窗口的“无标题”及设置新标题
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs
cs.style &=~FWS_ADDTOTITLE; //去掉无标题
m_strTitle="绘制sin曲线"; //设置新标题
return TRUE;
}
7.画笔和画刷
CPen 是 MFC 中的一个类,它封装了一个 Winndows GDI 画笔,并提供了用于操作画笔的若干函数。

画笔的创建和使用过程一般为:
CPen pen, *oldpen; // 定义画笔 pen 和指向画笔的指针 oldpen
pen.CreatePen(PS_SOLID,3,RGB(255,0,0)); // 创建一支红色能画实线 3 号粗细的画笔
oldpen=pDC->SelectObject(&pen); // 选用新的画笔 pen ,让 oldpen 指向旧的画笔
pDC->MoveTo(10,10);
pDC->LineTo(50,50);
pDC->SelectObject(oldpen); // 恢复使用旧的画笔
说明:
(1) CreatePen() 是 CPen 的成员函数,用于创建画笔。

其使用形式为:
BOOL CreatePen( int nPenStyle, int nWidth, COLORREF crColor ); 其中: nPenStyle 为所画线的样式,其取值见表; nWidth 线的宽度; crColor 为线的颜色。

(2) SelectObject() 是 CDC 类的成员函数,其作用是将一个新的 GDI 对象选入到设备环境中,新选用的对象取代原来的对象,然后返回指向原对象的指针。

函数原型为:CPen* SelectObject( CPen* pPen ); // 选用新的画笔
CBrush* SelectObject( CBrush* pBrush ); // 选用新的画刷
virtual CFont* SelectObject( CFont* pFont ); // 选用新的字体
CBitmap* SelectObject( CBitmap* pBitmap ); // 选用新的位图
画刷是用来填充图形的工具。

CBrush 是 MFC 中的一个类,它封装了一个 Windows GDI 画刷,并提供了用于操作画刷对象的若干方法。

画刷的创建和使用过程一般为:CBrush brush; // 定义画刷对象 brush
brush.CreateHatchBrush(HS_CROSS,RGB(0,255,0)); // 构造绿色十
字线风格的画刷
pDC->SelectObject(&brush); // 选择一个新的画刷
pDC->Ellipse(100,10,200,110); // 用新的画刷画圆
说明:
(1) CreateHatchBrush () 是 CBrush 的成员函数,用于创建画刷。

其使用形式为:
BOOL CreateHatchBrush(int nIndex , COLORREF crColor );
其中: nIndex 指定了阴影风格,其取值见表; crColor 为画刷的颜色。

(2)创建画刷的另一个函数是 CreateSolidBrush() ,其作用是创建用单一颜色填充的画刷。

其函数原形为:
BOOL CreateSolidBrush(COLORREF crColor );
【例2-4】编写一个程序,输出如图所示的图形。

void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CPen pen,*oldpen;
pen.CreatePen(PS_SOLID,3,RGB(0,0,255)); //创建画笔
oldpen=pDC->SelectObject(&pen); //选用新画笔,保原画笔
pDC->MoveTo(10,10);
pDC->LineTo(100,100); //画线
pDC->Ellipse(100,10,200,110); //画Ellipse
CBrush brush,*oldbrush;
brush.CreateHatchBrush(HS_CROSS,RGB(0,255,0)); //创建画刷
oldbrush=pDC->SelectObject(&brush); //选用新画刷,保原画刷
pDC->Ellipse(220,10,320,110); //画Ellipse
pDC->SelectObject(oldpen); //恢复原画笔
pDC->MoveTo(320,100);
pDC->LineTo(410,10); //画线
pen.DeleteObject(); //释放画笔资源
pDC->SelectObject(oldbrush); //恢复原画刷
brush.DeleteObject();
}
8.位图处理
CBitmap 是 MFC 中的一个类,它封装了 Winndows GDI 的位图处理,其成员函数主要是装载和操作位图。

下面通过一个实例说明显示位图的一般过程。

【例2.5】编一程序显示Windows 主目录中的Soap Bubbles.bmp,如图所示。

(1)导入位图
导入位图就是把存放在磁盘上的位图文件作为资源导入到应用程序中,方法是:
选择“插入 | 资源”命令,在弹出的“插入资源”对话框中,选定“ Bitmap ”,然后选择“导入”命令导入 Soap Bubbles.bmp。

这时在资源选项卡上会有Bitmap一项,其下有“IDB_BITMAP1”资源名字。

(2)装载位图
装载位图就是把应用程序位图资源装载到一个 CBitmap 对象中。

CBitmap b;
b.LoadBitmap(IDB_BITMAP1); //LoadBitmap 将位图资源装载到CBitmap 对象
(3)读取位图信息
BITMAP bm; // 说明 bm 为 BITMAP 结构类型的变量
b.GetBitmap(&bm); // 把位图的大小等信息读入 bm 中
int w=bm.bmWidth; // 把位图的宽度赋给 w
int h=bm.bmHeight; // 把位图的高度赋给h
(4)构造内存设备环境,并将位图装入该设备环境
CDC m;
m.CreateCompatibleDC(NULL);
m.SelectObject(&b);
(5)将位图从内存设备环境复制到真正的设备环境中
pDC->StretchBlt(0,0,(int)w*f,(int)h*f,&m,0,0,w,h,SRCCOPY);
说明:
BitBlt 是 CDC 类的成员函数,用于复制位图到真正的设备环境中。

其函数原型为:BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );
其中: (x,y) 为位图显示区域左上角的坐标; nWidth 和 nHeight 是显示区域的宽度和高度; pSrcDC 指向内存设备环境; (xSrc, ySrc) 为原图欲显示区域左上角的坐标;dwRop 指定复制方式,常用值为 SRCCOPY ,表示按原图复制。

void CMyView::OnDraw(CDC* pDC)
{ CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBitmap b;
b.LoadBitmap(IDB_BITMAP1);
BITMAP bm;
b.GetBitmap(&bm);
int w=bm.bmWidth;
int h=bm.bmHeight;
CDC m;
m.CreateCompatibleDC(NULL);
m.SelectObject(&b);
pDC->StretchBlt(0,0,w/2,h/2,&m,0,0,w,h,SRCCOPY);
}
2.3 定时器
在Visual C++中可以创建一个时钟,以一定的时间间隔发出定时器消息WM_TIMER。

利用定时器,可以制作闹钟、动画等。

创建定时器
创建定时器使用 SetTimer 函数。

其一般形式为:
UINT SetTimer(UINT nIDEvent , UINT nElapse , void * lpfnTimer ) 说明:
( 1 ) nIDEvent: 定时器标识,任何一个非 0 整数
( 2 ) nElapse :时间间隔,单位毫秒
( 3 ) lpfnTimer :一般设置为 NULL ,表示 WM_TIMER 消息加入应用程序的消息队列,由 CWnd 类对象处理 WM_TIMER 消息。

撤销定时器
撤销定时器使用 KillTimer 函数。

其一般形式为:
BOOL KillTimer (int nIDEvent );
说明: nIDEvent 为撤销的定时器标识。

【例2-6】用定时器控制蝴蝶在窗口中飞舞,如图所示。

(1)在视图类中定义数据成员
public:
int w,h,I; // 变量 w 存放位图宽度, h 存放高度, I 控制位图的交替
CBitmap b1,b2; // 说明两个存放位图的 CBitmap 对象
(2)在视图类构造函数分别装载两幅图到 CBitmap 对象
CTESTView::CTESTView()
{
// TODO: add construction code here
I=0;
b1.LoadBitmap(IDB_BITMAP1);
BITMAP BM;
b1.GetBitmap(&BM);
w=BM.bmWidth;
h=BM.bmHeight;
b2.LoadBitmap(IDB_BITMAP2);
}
(3)单击左键创建定时器,定时器标识为 1 ,以 200ms 的时间间隔发出定时器消息。

void CTESTView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default SetTimer(1,200,NULL);
CView::OnLButtonDown(nFlags, point);
}
(4)每隔 200ms 执行视图的定时器消息处理函数。

void CTESTView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default I=(I+1)%2; //I 为 0 显示第一幅图像, I 为 1 显示第二幅图像
Invalidate(FALSE); // 调用 OnDraw() 函数
CView::OnTimer(nIDEvent);
}
(5)在 OnDraw() 函数中交替显示两幅对象,达到动画的效果
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CDC m;
if (I==0)
{ m.CreateCompatibleDC(NULL);
m.SelectObject(&b1);
pDC->BitBlt(100,100,w,h,&m,0,0,SRCCOPY);
}
else
{ m.CreateCompatibleDC(NULL);
m.SelectObject(&b2);
pDC->BitBlt(100,100,w,h,&m,0,0,SRCCOPY);
}
}
(6)单击右键撤消定时器
void CTESTView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default KillTimer(1); // 撤消定时器
CView::OnRButtonDown(nFlags, point);
}
2.4 鼠标和键盘消息处理
鼠标和键盘是操纵计算机的主要工具。

对鼠标和键盘进行编程是程序设计人员必须掌握的基本技术。

鼠标消息处理
当用户进行鼠标操作时,便会发出如下的消息。

WM_LBUTTONDOWN :按下鼠标左键
WM_LBUTTONUP :释放鼠标左键
WM_LBUTTONDBLCLK :双击鼠标左键
WM_RBUTTONDOWN :按下鼠标右键
WM_RBUTTONUP :释放鼠标右键
WM_RBUTTONDBLCLK :双击鼠标右键
WM_MOUSEMOVE :鼠标移动
与上述鼠标消息相对应的消息处理成员函数为 ( 以 WM_LBUTTONDOWN 为例 ) :void CTest1View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default CView::OnLButtonDown(nFlags, point);
}
其中:
① nFlags 参数包含了 Shift 、 Ctrl 和 Alt 键的状态信息,如表 3 所示。

② point 的值是当前鼠标指针位置。

【例2-7】显示鼠标指针的位置
(1)在文档类中添加数据成员
public: CPoint a; //存放鼠标指针位置
(2)在OnNewDocument()中进行如下的初始化
a= CPoint(-1,-1); //程序开始运行时显示(-1,-1) (3)视图鼠标消息处理函数中把鼠标单击的位置存放在文档类的a中。

void CTESTView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
C TEST Doc *pDoc=GetDocument();
pDoc->a=point;
Invalidate();
CView::OnLButtonDown(nFlags, point);
}
(4)OnDraw()函数
在OnDraw()函数中输出鼠标单击的位置。

void CTESTView::OnDraw(CDC* pDC)
{
CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CString s;
s.Format("x=%d,y=%d",pDoc->a.x,pDoc->a.y);
pDC->TextOut(10, 10, s);
}
【例2-8】设计一个如图所示的画图程序。

要求按下鼠标右键画圆,按住鼠标左键移动画线。

(1)在文档类中添加数据成员
public:
CPoint c1, c2; // 存放一条线段两端的位置
int f1, f2;
CRect r;
(2)在 OnNewDocument() 中进行如下的初始化。

f1=0; //f1 为 1 表示处于画线状态,当前位置是一线段的起点
f2=0; //f2 为 1 表示需要画线
(3)编写视图类的各鼠标消息处理函数
// 按下左键画线开始,文档类的 f1 赋值 1 , c1 记录下当前鼠标位置。

void CTESTView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default CTESTDoc *p=GetDocument();
p->c1=point;
p->f1=1;
//Invalidate(FALSE);
CView::OnLButtonDown(nFlags, point);
}
// 移动鼠标时根据 f1 的值判断当前位置是否处于画线状态,
// 如果处于画线状态,则 f2 赋值 1 ,并把鼠标当前位置记录在 c2 中。

void CTESTView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default CTESTDoc *p=GetDocument();
if (p->f1==1)
{ p->f2=1;
p->c2=point;
Invalidate(FALSE);
}
CView::OnMouseMove(nFlags, point);
}
// 释放左键画线结束,文档类 f1 和 f2 赋值 0 。

void CTESTView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CTESTDoc *p=GetDocument();
p->f1=0;
p->f2=0;
CView::OnLButtonUp(nFlags, point);
}
// 视图类的 OnRButtonDown() 函数将以单击处为圆心半径为 30 的圆的数据存放到文档类的 r 中。

void CTESTView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CTESTDoc *p=GetDocument();
int radius;
radius=30;
p->r=CRect(point.x-radius,point.y-radius,point.x+radius,point. y+radius);
Invalidate(FALSE);
Cview::OnRButtonDown(nFlags, point);
}
//OnDraw() 函数绘制图形
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if (pDoc->f2==1)
{ pDC->MoveTo(pDoc->c1);
pDC->LineTo(pDoc->c2);
pDoc->c1=pDoc->c2;
}
pDC->Ellipse(pDoc->r);
}
键盘消息处理
当用户操作键盘时,视图类接收到由 Windows 发出的键盘消息。

重要的键盘消息是:(1) WM_KeyDown 事件:按下键盘上的一个键。

(2) WM_KeyUp 事件:释放键盘上的一个键。

与上述键盘消息相对应的消息处理成员函数为 ( 以 WM_KeyDown 为例 ) :
void CTESTView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
// TODO: Add your message handler code here and/or call default
Cview::OnKeyDown(nChar, nRepCnt, nFlags);
}
其中:
① nChar 为用户所操作的那个键的代码,它告诉消息处理函数用户所操作的物理键。

② nRepCnt 为按键的重复次数。

③ nFlags 为扫描码、转换键码和按键组合状态。

2.5 对话框
Visual C++ 提供了一组通用对话框类,每一个类对应一种标准对话框,可供程序员在编程时直接使用,如表所示。

使用通用对话框的过程如下:
( 1 )说明一个通用对话框类对象;
( 2 )调用其成员函数,设置属性;
( 3 )打开对话框;
( 4 )根据用户的选择进行编程。

2.5.1 文件对话框构造函数
CFileDialog( BOOL bOpenFileDialog , LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );
说明:
①若 bOpenFileDialog 为 TRUE ,则显示打开对话框,否则显示另存为对话框。

② lpszDefExt 指定缺省的扩展名。

③ lpszFileName 指定缺省的文件名。

④ lpszFilter 用于指明可供选择的文件类型和相应的扩展名。

它的属性值是由一组元素或用“ | ”符号分开的分别表示不同类型文件的多组元素组成。

例如,若要在打开文件类型列表框时显示如图所示的三种文件类型供用户选择,则 Filter 属性应设为:"Documents(*.DOC)|*.DOC|Text Files(*.TXT)|*.txt|All Files|*.* ||"
⑤其它参数一般使用缺省值
DoModal() 函数的功能是打开打话框。

其使用形式为:
对象 .DoModal()
GetPathName() 用于获取用户选择的包括路径在内的文件名。

其函数原形为:
CString GetPathName( ) const;
GetFileName () 用于获取用户选择的文件名,不包括路径。

其函数原形为:
CString GetPathName( ) const;
GetFileExt( ) 返回文件扩展名
GetFileTitle( ) 返回文件主名(不含扩展名)
【例2-9 】编写一个如图所示的图形浏览程序。

若用户单击左键,则弹出打开对话框,用户选择一个图片文件后就在客户区显示该图片。

(1)在文档类中添加一个公有数据成员 m_filename ,用于存放用户选择的包括路径在内的文件名。

public:
CString filename;
(2)在文档类的 OnNewDocument() 函数中对数据成员 filename 进行初始化。

filename= "" ;
(3)通过“建立类向导”为视图类添加 WM_LBUTTONDOWN 消息处理函数,并编写代码。

void CTESTView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler =code here and/or call default
CTESTDoc* pDoc=GetDocument();
CFileDialog dlg(TRUE,NULL,NULL,NULL,"BMP Files(*.BMP)|*.BMP||");
if ( dlg.DoModal()==IDOK )
{ pDoc->filename=dlg.GetPathName();
Invalidate();
}
CView::OnLButtonDown(nFlags, point);
}
(4)在 OnDraw() 函数中,显示位图。

void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if ( pDoc->filename!="" )
{ HBITMAP hbitmap;
hbitmap=(HBITMAP)LoadImage(NULL,pDoc->filename,
IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
CDC m;
CRect rect;
m.CreateCompatibleDC(pDC);
GetClientRect(rect);
m.SelectObject(hbitmap);
pDC->BitBlt(20,20,rect.Width()-20,rect.Height()-20,&m,0,0,SRCC OPY);
}
}
说明:加载磁盘上的一个 .bmp 文件应使用 LoadImage() 。

其函数原型为:HANDLE LoadImage(HINSTANCE hinst , LPCTSTR lpszName , UINT uType , int cxDesired , int cyDesired , UINT fuLoad );
其中: hinst 可以用 NULL , lpszName 是图像在磁盘上的文件名, uType 表
示图像的类型, fuLoad 的值为 LR_LOADFROMFILE 表示从文件中导入位图。

2.5.2 颜色对话框
颜色对话框属于 CColorDialog 类。

其一般使用形式为:
CColorDialog dlg; // 说明 dlg 为 CColorDialog 类的对象
dlg.DoModal(); // 打开颜色对话框
COLORREF c= dlg. GetColor(); // 获取用户选择的颜色并赋给 c 说明:
① GetColor() 是 CColorDialog 的成员函数,其返回值是用户选择的颜色。

②若用户选择了“取消”,没有选择颜色,则 GetColor() 的返回值是对话框的默认值。

【例2-10 】编写一个如图所示的应用程序。

若用户单击左键,则弹出颜色对话框,用户选择一种颜色后就以鼠标单击处为圆心画一个圆,圆的半径随机产生。

(1)在文档类中添加三个公有的数据成员:
CRect r[100]; // 存放圆的数据
COLORREF c[100]; // 存放对应数组 r 的圆的颜色
int n; // 目前圆的数量
(2)在文档类的 OnNewDocument() 函数中对数据成员 n 进行初始化。

n=0;
(3)通过“建立类向导”为视图类添加 WM_LBUTTONDOWN 消息处理函数,代码 : void CMy0601aView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CMy0601aDoc *pDoc=GetDocument();
CColorDialog dlg;
dlg.DoModal();
pDoc->c[pDoc ->n]=dlg.GetColor();
int r=rand()%50+10;
CRect rect(point.x-r,point.y-r,point.x+r,point.y+r);
pDoc->r[pDoc->n]=rect;
pDoc->n++;
Invalidate();
CView::OnLButtonDown(nFlags, point);
}
(4)在 OnDraw() 函数中,用数组 c 的颜色绘出所有的圆
void CMy0601aView::OnDraw(CDC* pDC)
{ CMy0601aDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush newbrush,*oldbrush;
for(int i=0;i<pDoc->n;i++)
{ newbrush.CreateSolidBrush(pDoc->c[i]);
oldbrush=pDC->SelectObject(&newbrush);
pDC->Ellipse(pDoc->r[i]);
pDC->SelectObject(oldbrush);
newbrush.DeleteObject(); // 删除画刷
}
}
2.5.3 字体对话框
字体对话框属于 CFontDialog 类。

其一般使用形式为:
CFontDialog dlg; // 说明 dlg 为 CFontDialog 类的对象
dlg.DoModal(); // 打开字体对话框
LOGFONT font; // 说明 font 为 LOGFONT 类型结构变量
dlg.GetCurrentFont (& font ); // 把用户选择的字体送入变量 font 中
COLORREF c= dlg. GetColor(); // 把用户选择的颜色赋给变量 c
CFont cf; // 说明 cf 为 Cfont 类的对象
cf.CreateFontIndirect(&font); // 用 font 中的数据构造字体
pDC->SelectObject(&cf); // 选用新字体
pDC->SetTextColor(); // 选用新颜色
pDC->TextOut(10,10," 同舟共济自强不息! "); // 用新的字体和颜色输出文字
说明:
① LOGFONT 是 Visual C++ 定义的结构类型,用于存放有关字体的参数。

② GetCurrentFont () 是字体对话框的成员函数,把字体数据送入 font 中。

【例2-11】编写一个如图所示的应用程序。

若用户单击左键,则弹出如图所示的字体对话框,然后用用户选择的字体后输出“同舟共济自强不息!”
(1)在文档类中添加两个公有的数据成员:
public:
LOGFONT font; // 存放有关字体的数据
COLORREF c; // 存放字体颜色
(2)通过“建立类向导”为视图类添加 WM_LBUTTONDOWN 消息处理函数,代码 : void CMy0601aView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CMy0601aDoc *pDoc=GetDocument();
CFontDialog dlg;
dlg.DoModal();
dlg.GetCurrentFont(&pDoc->font);
pDoc->c=dlg.GetColor();
Invalidate();
CView::OnLButtonDown(nFlags, point);
}
(3)在 OnDraw() 函数中,用选定的颜色和字体输出文字
void CMy0601aView::OnDraw(CDC* pDC)
{ CMy0601aDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here CFont cf;
cf.CreateFontIndirect(&pDoc->font);
pDC->SelectObject(&cf);
pDC->SetTextColor(pDoc->c);
pDC->TextOut(10,10,"同舟共济自强不息!");
}
2.5.4 调用自定义对话框
对话框是 Windows 及其应用程序的重要组成元素。

Windows 应用程序不仅可以使用通用对话框,而且还可以调用如第一章中设计的用户自定义对话框。

在文档 / 视图结构应用程序中,设计、调用用户自定义对话框的过程可分为三个阶段:(1)设计、测试对话框
(2)创建对话框类
(3)使用对话框
下面通过一个实例说明。

【例2-12】设计一个如图所示的应用程序。

若用户单击左键,则弹出输入成绩对话框,选择 OK 后平均成绩和总分在窗口的客户区输出。

(1)用 AppWizard 生成 SDI 应用程序框架;
(2)设计对话框
①选择“插入 | 资源”命令;
②在弹出“插入资源”对话框中,选定“ Dialog ”,然后选择“新建”;
③在 ResourceView 中展开 Dialog ,可以看到新增了 ID 为 IDD_DIALOG1 的资源双击该 ID ,则在窗口中间显示对话框设计界面,在这里进行对话框设计。

④测试对话框
选择“布局|TEST ”测试对话框。

若符合要求,则对话框设计就完成了。

(3)创建对话框类
①若双击对话框设计界面,或在对话框设计界面的快捷菜单中选择“建立类向导”,
②选择“ Create a new class ”,选择“ OK ”。

③在“ New Class ”对话框中输入对话框类名: CInput 。

将 5 个编辑框都连接到 int 类型的变量。

(4)调用对话框类
首先在视图类的实现文件 TESTView.cpp 中添加对话框类头文件的包含命令:#include "Input.h"
然后在文档类中添加一个公有的数据成员 total ,用于存放总分,并在文档类的OnNewDocument()中将其初始化为-1。

public:
double total;
(5)最后编写 OnLButtonDown() 和 OnDraw() 函数。

void CTESTView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CTESTDoc* pDoc=GetDocument();
CInput dlg;
if ( dlg.DoModal()==IDOK )
{ pDoc->total=dlg.m_English+dlg.m_Math+dlg.m_Chinese + dlg.m_Physics +dlg.m_Chemistry;
Invalidate();
}
CView::OnLButtonDown(nFlags, point);
}
void CTESTView::OnDraw(CDC* pDC)
{ CTESTDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CString str;
if ( pDoc->total!=-1 )
{ str.Format(" 平均成绩: %.1f",pDoc->total/5);
pDC->TextOut(10,10,str);
str.Format(" 总分: %.1f",pDoc->total);
pDC->TextOut(10,30,str);
}
}
2.6 菜单设计
菜单属于 CMenu 类。

Windows 应用程序的菜单由两部分组成:一是主菜单,一般位于顶层;二是弹出菜单,它是单击主菜单中的菜单项时弹出的子菜单。

设计菜单的主要操作有:
(1)显示菜单
在工作区中选定“ ResourceView ”,展开 Menu ,双击其中的 IDR_MAINFRAME ,可以看到由 AppWizard 生成的标准菜单。

(2)删除菜单项
先选定菜单项,然后按 Del 键。

(3)添加菜单项。

相关文档
最新文档