《VC++深入详解–学习笔记》(7)对话框1

Filed Under (VC++ 学习笔记) by panmaoru on 12-12-2009

记得以前上学的时候听一个老师说过,计算机技术的核心是输入输出和二进制。Windows应用程序工作的基本流程就是从用户那里得到数据,经过相应的处理之后,再把处理结果输出到屏幕和其他的输出设备上。为了实现这个过程,就需要一个很重要的接口—对话框。

7.1对话框的基本知识

在MFC中,所有的控件类都是从CWnd类派生而来。以前做过一些其他开发技术的开发工作,相对其他IDE而言,VC下提供的控件很少,但是基本可以满足开发需要了。

对话框的种类主要分2中,模态对话框和非模态对话框。

  1. 模态对话框:指当前显示时,程序会暂停执行,知道关闭这个模态对话框后才能继续执行其他任务(个人感觉这个只是一个相对暂停的概念,暂停的只是针对用户操作部分,我们运行一个杀毒软件程序,杀毒过程中如果有一些参数设定,会出现一些模态对话框,但是这个时候杀毒程序还是会继续执行的)。
  2. 非模态对话框:对其他操作没有影响,这个容易理解些。

7.2 对话框的创建和显示

在Resources选项卡Dialog目录下,可以添加一个新的对话框。添加一个对话框之后,类视图可以看到也相应添加了一个新的类。在创建一个MFC单文档应用程序的时候,系统为默认创建一个CAboutDlg类,在Dialog文件夹下可以看到IDD_ABOUTBOX这个对话框。在View类和Frame类中操作对话框的时候,首先需要在cpp文件中引入该对话框的头文件。

显示一个对话框:

void CMyboleView::OnDialog()
{
       // TODO: Add your command handler code here
       CTestDlg dlg;
       dlg.DoModal();//DoModal是一个函数,刚不小心忘了括号,记清楚了
	
}

如果要显示一个非模态对话框,需要调用Dialog类的Create函数。MSDN中,Create类的声明如下:

BOOL Create( LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL );
BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );

由于非模态对话框显示的时候程序不会暂停等待用户操作,如果使用临时变量的话,对象的生命周期在OnDialog()执行完之后就结束,这样就无法正常显示,所以可采用创建对象指针的方式,由于动态创建的指针对象是分配在堆内存上,堆内存上的变量周期可应用程序相同。

       CTestDlg *pDlg=new CTestDlg;
       pDlg->Create(IDD_DIALOG1,this);
       pDlg->ShowWindow(SW_SHOW);

这里又涉及一个动态内存释放的问题。。。

7.3 动态创建按钮

参见MSDN,CButton的Create函数。

CButton myButton1, myButton2, myButton3, myButton4;
// Create a push button.
myButton1.Create(_T(\"My button\"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
   CRect(10,10,100,30), this, 1);//当前窗口
// Create a radio button.
myButton2.Create(_T(\"My button\"), WS_CHILD|WS_VISIBLE|BS_RADIOBUTTON,
   CRect(10,40,100,70), this, 2); //当前窗口
// Create an auto 3-state button.
myButton3.Create(_T(\"My button\"), WS_CHILD|WS_VISIBLE|BS_AUTO3STATE,
   CRect(10,70,100,100), GetParent( ), 3); //父窗口
// Create an auto check box.
myButton4.Create(_T(\"My button\"), WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
   CRect(10,100,100,130), GetParent( ), 4); //父窗口

7.4 访问控件

访问控件的方式很多,按照书上的例子简单的抄了几种,感觉这三种最简单,使用上也方便些。

void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       int num1,num2,num3;
       //char buf1[10],buf2[10],buf3[10];1,2
       //GetDlgItem(IDC_EDIT1)->GetWindowText(buf1,10);1
       //GetDlgItem(IDC_EDIT2)->GetWindowText(buf2,10);1
       //GetDlgItemText(IDC_EDIT1,buf1,10);2
       //GetDlgItemText(IDC_EDIT2,buf2,10);2
	
       //num1=atoi(buf1);1,2
       //num2=atoi(buf2);1,2
       num1=GetDlgItemInt(IDC_EDIT1);
       num2=GetDlgItemInt(IDC_EDIT2);
       num3=num1+num2;
       //itoa(num3,buf3,10);1,2
	
       //GetDlgItem(IDC_EDIT3)->SetWindowText(buf3);1
       //SetDlgItemText(IDC_EDIT3,buf3);2
       SetDlgItemInt(IDC_EDIT3,num3);
}

《VC++深入详解–学习笔记》(6)菜单

Filed Under (VC++ 学习笔记) by panmaoru on 10-12-2009

菜单栏,工具栏,状态栏是组成Windows程序图形界面的三个主要元素。大多数的Windows应用程序都提供了菜单,作为用户和程序之间的一种交互途径。

6.1 菜单栏

在设置菜单栏的菜单项的时候,Pop-up属性的为弹出菜单,这种菜单不能响应命令,没有ID号。

6.2 菜单命令的路由

相应菜单项命令的顺序是:视类(View),文档类(Doc),框架类(Frame),应用程序类(App)。

Windows消息的分类:

  1. 标准消息:除了WM_COMMAND消息之外,所有以WM开头的消息都是标准消息,从CWnd派生的类都可以响应这类消息。
  2. 命令消息:来至菜单、加速键和工具栏按钮的消息,这类消息都是以WM_COMMAND形式呈现,在MFC中,通过菜单项的标示ID来区分不同的命令消息,在SDK中,通过消息的wParam参数标示,从CCmdTarget派生的类都可以接收这类消息。
  3. 通告消息:有控件产生的消息,例如按钮的单击,列表框的选择等都会产生这类消息,目的是向其父窗口通知事件的发生。这类消息以WM_COMMAND形式发送,从CCmdTarget派生的类都可以接收这类消息。

CWnd派生于CCmdTarget,从CWnd派生的类可以接收所以类型的消息(View,Frame),其他从CCmdTarget派生的类只能接收命令消息和通告消息(Doc,App)。

6.3 基本菜单操作

Pop-up0 Pop-up1 Pop-up2 Pop-up3
0-0 1-0 2-0 3-0
0-1 1-1 2-1 3-1
0-2 1-2 2-2 3-2

可以通过CWnd的成员函数GetMenu来获取指向菜单栏的指针,对菜单子项的访问可以通过菜单的索引标识来实现。

操作一个菜单项的2种实例:

GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_OPEN,MF_BYCOMMAND | MF_CHECKED);

MSDN中关于MF_BYCOMMAND 和MF_BYPOSITION的解释:

MF_BYCOMMAND   Specifies that the parameter gives the command ID of the existing menu item. This is the default.

MF_BYPOSITION   Specifies that the parameter gives the position of the existing menu item. The first item is at position 0.

GetSubMenu有很多成员函数,用于菜单的操作。

6.3.1 快捷菜单

工程-添加到工程-Components and Controls,从这里可可以选择VC提供的一些组件和控件,添加一个POPUP MENU,选择所属于View类,此时View类中便添加了一个OnContextMenu函数。

void CMenuView::OnContextMenu(CWnd*, CPoint point)
{
       // CG: This block was added by the Pop-up Menu component
       {
              if (point.x == -1 && point.y == -1){
                     //keystroke invocation
                     CRect rect;
                     GetClientRect(rect);
                     ClientToScreen(rect);
                     point = rect.TopLeft();
                     point.Offset(5, 5);
              }
              CMenu menu;
              VERIFY(menu.LoadMenu(CG_IDR_POPUP_MENU_VIEW));
              CMenu* pPopup = menu.GetSubMenu(0);
              ASSERT(pPopup != NULL);
              CWnd* pWndPopupOwner = this;
              while (pWndPopupOwner->GetStyle() & WS_CHILD)
                     pWndPopupOwner = pWndPopupOwner->GetParent();
              pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
                     pWndPopupOwner);
       }
}

运行之后,在View类区域右键,会出现一个弹出窗口。

《VC++深入详解–学习笔记》(5)文本编程

Filed Under (VC++ 学习笔记) by panmaoru on 09-12-2009

本章简单介绍了一些关于文本编程的知识,其中比较重要的几个概念:插入符,窗口重绘,字符输入,定时器。

5.1 插入符

插入符Caret用来提示用户当前相应的位置。如果想要在程序中创建插入符,可以用CWnd类的CreateSolidCaret函数来完成,函数原型如下。

Void CreateSolidCaret(int nWidth,int nHeight);//两个参数分别表示插入符的宽度和高度

在一个MFC的单文档应用程序中,对文本的操作通常在View类中实现,要在View窗口创建之后再创建一个插入符,需要使用OnCreate函数。插入符的宽度通常为当前设备表述表中字符的平均宽度的1/8,高度和字符高度相同。

int CTextView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
       if (CView::OnCreate(lpCreateStruct) == -1)
              return -1;
       // TODO: Add your specialized creation code here
       CClientDC dc(this);
       TEXTMETRIC tm;
       dc.GetTextMetrics(&tm);
       CreateSolidCaret(tm.tmAveCharWidth/8,tm.tmHeight);
       ShowCaret();
       return 0;
}

5.2窗口重绘

View类窗口产生和重绘时都要调用OnDraw函数。

5.3字符输入

1.消息捕获

字符输入属于WM_CHAR消息,可以通过捕获WM_CHAR消息来获取键盘输入信息。

2.应该在插入符的位置输出键盘输入信息,输入之前需要先设定插入符位置,输出的时候相应修改插入符位置,以便下次输入输出,在View类区域的任何位置点击鼠标,设定插入符位置。

void CTextView::OnLButtonDown(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       m_ptOrigin=point;
       SetCaretPos(point);
       //SetCaretPos(m_ptSecond);一个类中同时只能有一个插入符
       m_strLine.Empty();
       CView::OnLButtonDown(nFlags, point);
}

3.回车与退行,字体

void CTextView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
       // TODO: Add your message handler code here and/or call default
       CClientDC dc(this);       //设置字体
       CFont font;
       font.CreatePointFont(200,\"华文楷体\",NULL);
       CFont *pOldFont=dc.SelectObject(&font);
       TEXTMETRIC tm;
       dc.GetTextMetrics(&tm);
       if(0x0d==nChar)//回车
       {
              m_strLine.Empty();
              m_ptOrigin.y+=tm.tmHeight;
       }
       else if(0x08==nChar)//退行
       {
              COLORREF clr=dc.SetTextColor(dc.GetBkColor());
              dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
              m_strLine=m_strLine.Left(m_strLine.GetLength()-1);
              dc.SetTextColor(clr);
       }
       else
       {
              m_strLine+=nChar;
       }
       //输出文本之前先重定位插入符的位置
       CSize cs=dc.GetTextExtent(m_strLine);
       CPoint pt;
       pt.x=m_ptOrigin.x+cs.cx;
       pt.y=m_ptOrigin.y;
       SetCaretPos(pt);
       dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
       dc.SelectObject(pOldFont);
       CView::OnChar(nChar, nRepCnt, nFlags);
}

5.4定时器Timer

CWnd类的SetTimer成员函数可以设置定时器,该函数的声明形式如下表示:

UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer) (HWND, UINT, UINT, DWORD) );

添加一个WM_Timer函数

void CTextView::OnTimer(UINT nIDEvent)
{
       // TODO: Add your message handler code here and/or call default
       MessageBox(\"。。。\");
       CView::OnTimer(nIDEvent);
}

并在View类的OnCreate函数内设置一个定时器SetTimer

SetTimer(1,1,NULL);

运行,开启声音,彷如机关枪扫射的感觉,没几秒钟,程序就自动卡死了。

《VC++深入详解–学习笔记》(4)简单绘图

Filed Under (VC++ 学习笔记) by panmaoru on 09-12-2009

本章介绍了MFC的消息映射机制,探讨发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的处理函数进行响应的。讨论设备描述表及其封装类CDC的应用,结合画图程序进行分析。

4.1消息映射机制

为视类添加一个鼠标左键按下的消息,在源文件中会添加3处代码。

1.[DrawView.h]

protected:
       //{{AFX_MSG(CDrawView)
       afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
       //}}AFX_MSG
       DECLARE_MESSAGE_MAP()

此次为一个OnLButtonDown函数的声明。

2. [DrawView.cpp]

BEGIN_MESSAGE_MAP(CDrawView, CView)
       //{{AFX_MSG_MAP(CDrawView)
       ON_WM_LBUTTONDOWN()
       //}}AFX_MSG_MAP
END_MESSAGE_MAP()//消息映射表

此处为将一个鼠标左键按下的消息和一个消息响应函数关联起来。

3. [DrawView.cpp]

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       //MessageBox(\"OK\");
       CView::OnLButtonDown(nFlags, point);
}

OnLButtonDown函数实现。

MFC消息映射机制的具体实现方法是:在每个能接收和处理消息的类中(View,Frame。。)定义了一个消息和消息静态函数对照表,即消息映射表,在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理所有消息及其对应的消息处理函数的地址都列在这个对应的静态表中,当有消息需要处理时,程序只需要搜索改消息静态表,查看表中是否有改信息,就知道该类能否处理该消息,如果能,则通过静态表找到与之对应的消息处理函数。

在CDrawView类中,CDrawView对象相关一个窗口,当然有它的窗口句柄,该句柄与CDrawView对象的一个指针CDrawView*存在一一对应的关系。

4.2绘制线条

步骤1:定义一个CPoint全局变量,存放鼠标按下去的时候的起点信息;

步骤2:在鼠标按下去消息处记录鼠标位置;

步骤3:记录鼠标弹起时的位置,并在2个位置之间划一条线。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       HDC hdc;
       hdc=::GetDC(m_hWnd);
       MoveToEx(hdc,m_point.x,m_point.y,NULL);
       LineTo(hdc,point.x,point.y);
       ::ReleaseDC(m_hWnd,hdc);
       CView::OnLButtonUp(nFlags, point);
}

4.2.2MFC的CDC绘制线条

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

4.2.3MFC的CClientDC类绘制线条

       CClientDC dc(this/*GetParent()//在父框架上绘图*/);
       //CPen pen(PS_SOLID,1,RGB(255,0,0)); 新建一个画笔对象
       // CPen * oldPen=dc.SelectObject(&pen); 将画笔对象选人设备描述表,并将原先的保存
       dc.MoveTo(m_point.x,m_point.y);
       dc.LineTo(point.x,point.y);
       //dc.SelectObject(oldPen); //将原先的画笔对象选择回

4.2.4MFC的CWindowDC类绘制线条

       CWindowDC dc(GetDesktopWindow());
       dc.MoveTo(m_point.x,m_point.y);
       dc.LineTo(point.x,point.y);

4.3静态数据成员与函数

静态数据成员和函数属于类的本身,在类加载的时候为其分配空间。非静态成员函数和变量属于对象的方法和数据。静态成员函数只能访问静态数据成员。

《VC++深入详解–学习笔记》(3)MFC框架程序剖析

Filed Under (VC++ 学习笔记) by panmaoru on 08-12-2009

第三章讲的是MFC框架程序剖析,结合着视频和书看了一遍之后,只能有一个粗略的了解。先记下一个概念:MFC(Microsoft Function Class ,微软基础类库)。

MFC AppWizard

一个辅助生成源代码的向导工具,可以帮助我们自动生成基于MFC的源代码。第一章提到了创建一个Win32应用程序需要经过下列步骤。

  • 编写WinMain函数
  • 设计窗口类(WNDCLASS);
  • 注册窗口类(RegisterClass);
  • 创建窗口(CreateWindow);
  • 显示并更新窗口(ShowWindow|UpdateWindow);
  • 编写消息循环;
  • 编写窗口过程函数。

而基于MFC AppWizard的应用程序的在生成上和Win32相似,只是对其中的一些过程进行了封装,并提供了一些自定义。

全局变量和对象

Main函数是程序的入口函数,而在程序入口main函数加载之前,系统就会为全局变量和对象分配地址空间。

窗口类,窗口类对象和窗口

窗口是屏幕上的一块儿矩形区域;窗口类是封装了对窗口的一系列操作的类,比如注册窗口,创建窗口,显示窗口,销毁窗口等等;窗口对象是窗口类的一个实例。

C++窗口类对象和窗口并不是一回事,他们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄,窗口销毁时,与之对象的C++窗口的类对象是否销毁要看其生命周期是否结束。但是如果窗口类对象销毁,那与之对应的窗口也将销毁。

动态创建一个按钮

在MFC提供的资源类中,有些类的构造直接通过其构造函数就可以完成,也就是说,这些对象的构造函数包含这个对象的初始化操作。但是有些对象的产生出了调用构造函数之外,还需要一些函数来进行初始化的工作。

  • 在MainFrame头文件中添加一个CButton对象
    private: CButton m_btn;
  • 调用Create函数,创建窗口。
    //BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
    m_btn.Create(\"Test\",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,200,200),this,1);
    m_btn.ShowWindow(SW_SHOWNORMAL);

《VC++深入详解–学习笔记》(2)掌握C++

Filed Under (VC++ 学习笔记) by panmaoru on 01-12-2009

题外话

打开掌握C++这一章节,首先想到的是以前我非常喜欢的一个英语教师老罗说的一句话:一个人如果连自己的母语都学不好,那他很难学好一门外语。接触程序开发差不多有4,5年的时间了,现在看来,我是那个没有学好母语的人。

大学期间学的第一门程序设计语言是C++,成果可以用惨烈来形容,课程结束之后留下的印象大概就是:指针太繁琐了,C++博大精深啊!再后来便喜欢上了一种叫做asp.net的开发技术,因为它其中使用的C#语言是没有指针的。很不幸的是做asp.net开发这几年我只是粗略的翻过《C#入门经典-第三版》和《C#高级编程-第四版》,至于程序员必备的MSDN,我基本上都没安装过,罢了,从零开始,学习C++。

看《VC++深入详解》这本书之前,每天大概2个小时,花了半个月的把《C++ Primer-第四版》看了一遍,因为实际动手较少,只能说有一个初略的了解,对C就完全没有概念了,只能猜测下C和C++在基本的表达式,语句和控制流程等基础语法方面是类同的。

面向对象

C++是面向对象的程序设计语言,带类的C,比C多出了面向对象的特性,“面向对象”这个概念够大了,面向对象的主要特点有

  •  封装性:把数据和操作数据的函数组织在一起,不仅使程序更加紧凑,而且提高了类的内部数据的安全性;
  • 继承性:是一个类具有另外一个类的属性(数据)和行为(函数),增加了程序的可拓展性和代码的复用;
  • 多态性:一个接口,不同的实现(很难理解,很难解释)。

结构和类

写一个简单的C++程序:

//看完书本中的第一张,VC中很多结构的定义,烦
	
#include <iostream.h>
	
struct point//换成class
	
{
	
       int x;
	
       int y;
	
};
	
void main()
	
{
	
       point pt;
	
       pt.x=10;
	
       pt.y=12;
	
       cout<<\"x=\"<<pt.x<<\"y=\"<<pt.y<<endl;
	
}

程序运行,结果是正常的,输出赋值的x和y的值。

如果把struct换成class,用类来定义就会有问题,这里是由于访问权限的原因struct的数据成员默认是public的,class默认是private。刚开始不明白,后来想想,大概是类的封装特性吧,为了保护类的数据和类的成员函数。