孙鑫VC++深入详解六到十章

第六章 菜单

一、创建菜单项

资源管理器→menu项→双击IDR_MAINFRAME打开菜单编辑器

pop-up类型的菜单是弹出式菜单,不能进行命令响应。

在这里插入图片描述

ID号的命名规则:

IDM菜单资源

IDC光标资源

IDI图标资源

二、为菜单项添加响应函数

打开ClassWizard【类向导】,

请添加图片描述

  1. 区别Message中的COMMAND和UPDATE_COMMAND_UI

    COMMAND处理该菜单对应的功能

    UPDATE_COMMAND_UI处理菜单应对的用户界面

  2. 菜单项响应顺序:view→doc→mainframe→App

  3. Windows消息的分类

    1、标准消息:除 WM_COMMAND 之外,所有以 WM_开头的消息都是标准消息。
    CWnd类,都可以接收到这类消息。

    2、命令消息:来自菜单、加速键或工具栏按钮的消息。这类消息都以 WM_COMMAND 形式呈现
    在 MFC 中,通过菜单项的标识(ID)来区分不同的命令消息;在 SDK 中,通过消息的wParam 参数识别。从 CCmdTarget 派生的类,都可以接收到这类消息。

    3、通告消息:由控件产生的消息,例如按钮的单击、列表框的选择等。目的是为了向其父窗口(通常是对话框)通知事件的发生。这类消息也是以 WM_COMMAND 形式呈现。从CCmdTarget 派生的类,都可以接收到这类消息。
    实际上,CWnd 类派生于 CCmdlTarget类。也就是说凡是从 CWnd 派生的类,它们既可以接收标准消息,也可以接收命令消息和通告消息。而对于那些从 CCmdTarget 派生的类,则只能接收命令消息和通告消息,不能接收标准消息。

  4. 菜单消息响应过程:mainframe类接收消息→view首先处理,若不行→doc→mainframe→App

三、基本菜单操作

1、了解菜单结构

在这里插入图片描述

程序的主菜单属于框架窗口,因此要在框架类的OnCreate中编程。

2、标记菜单

获取程序菜单栏:CWnd类 CMenu* GetMenu( ) const;

获取子菜单:CMenu类CMenu* GetSubMenu( int nPos ) const;参数是子菜单的索引号,注意分隔栏也占据索引号。

添加或移除标记:CMenu类UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );

Parameters
nIDCheckItem:Specifies the menu item to be checked, as determined by nCheck.
nCheckSpecifies :how to check the menu item and how to determine the position of the item in the menu. The nCheck parameter can be a combination of MF_CHECKED or MF_UNCHECKED with MF_BYPOSITION or MF_BYCOMMAND flags.
* MF_BYCOMMAND 指定第一个参数通过ID
* MF_BYPOSITION 指定第一个参数通过索引号
* MF_CHECKED 设置复选标记
* MF_UNCHECKED 移走复选标记

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

3、默认菜单项(粗体)

注意子菜单只能有一个默认菜单项。

CMenu类BOOL SetDefaultItem( UINT uItem, BOOL fByPos = FALSE );

GetMenu()->GetSubMenu(0)->SetDefaultItem(2,MF_BYPOSITION);

4、图形标记菜单

CMenu类:BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked );

pBmpUnchecked是取消选中时的位图;pBmpchecked是选中状态时的位图。

获取系统的信息度量:int GetSystemMetrics( int nIndex);其中,参数用于指定希望获取哪部分的系统信息。SM_CXMENUCHECK和SM_CYMENUCHECK用于获取菜单项标记图形的默认尺寸。

CString类的Format函数类似C中的printf。

CBitmap m_bitmap;//头文件全局变量

m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bitmap, &m_bitmap);

Ctring str;
str.Format("x = %d , y = %d", GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);



5、禁用菜单项

CMenu类:UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );

nEnable:

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.

MF_DISABLED Disables the menu item so that it cannot be selected but does not dim it.

MF_ENABLED Enables the menu item so that it can be selected and restores it from its dimmed state.

MF_GRAYED Disables the menu item so that it cannot be selected and dims it.

常把MF_GRAYEDMF_DISABLED放在一起使用。

发现不能正常禁用?

// NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of

// CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are

// needed, and CMenu::EnableMenuItem() will work as expected.

即在CMainFrame构造函数中添加m_bAutoMenuEnable = FALSE;

6、移除和装载菜单

CWnd类:BOOL SetMenu( CMenu* pMenu );CMenu* pMenu 指向一个新的菜单对象,若值为NULL,则移除当前菜单。

/*移除菜单*/
SetMenu(NULL);

/*装载菜单资源*/
Cmenu menu;//1、框架类成员变量2、若是局部变量,结尾一定要加Detach函数

menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();//menu是局部变量的情况,将菜单句柄与菜单对象分离

四、MFC菜单命令更新机制

菜单项状态的维护依赖于CN_UPDATE_COMMAND_UI消息,该消息智能用于菜单项,不能用于永久显示的顶级菜单(即弹出式菜单)。

在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加宏以捕获消息

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)//自动添加的
  ON_WM_CREATE()
  ON_UPDATE_COMMAND_UI(ID_FILE_NEW, &CMainFrame::OnUpdateFileNew)
END_MESSAGE_MAP()
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)//消息响应函数
{
  // TODO: 在此添加命令更新用户界面处理程序代码
  pCmdUI->Enable();
}
/*CCmdUI类决定一个菜单项是否可以使用(Enable),是否有标记(SetCheck),改变菜单项文本(SetText)*/
/*CCmdUI类有成员变量m_nID保存对象ID和m_nIndex保存对象索引*/

如果只是利用禁用菜单项,菜单项变灰禁用,但工具栏按钮依然能够使用。而如果使用命令更新机制,则都不能使用。

五、快捷菜单

(上下文菜单、右键菜单)

为CView类添加一个WM_CONTEXTMENU消息处理函数,当鼠标右键单击窗口时,就会调用该函数。

显示快捷菜单函数: BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );

void CmenuView::OnContextMenu(CWnd* pWnd, CPoint point)
{
  // TODO: 在此处添加消息处理程序代码
  CMenu popmenu;
  popmenu.LoadMenu(IDR_POPMENU1); //读取资源
  popmenu.GetSubMenu(0)->TrackPopupMenu(
  TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, this);
}
/*
TPM_LEFTALIGN定位弹出菜单,使其左侧与 x 指定的坐标对齐。*/

六、动态菜单操作

1、针对子菜单的动态操作

1、把一个新的子菜单添加到菜单栏末尾。CMenu类:BOOL AppendMenu ( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

CMenu类:CreateMenu()创建一个弹出菜单,并与CMenu对象关联

 CMenu m_menu;//成员变量
 
 m_menu.CreateMenu();
 GetMenu()->AppendMenu(MF_POPUP, (UINT)m_menu.m_hMenu, "TEST");
 
  

2、两个子菜单中间插入子菜单。CMenu类: BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );

用法类似AppendMenu

3、删除子菜单或菜单项。CMenu类:BOOL DeleteMenu( UINT nPosition, UINT nFlags );

//删除子菜单
  GetMenu()->DeleteMenu(0,MF_BYPOSITION );
//删除菜单项
GetMenu()->GetSubMenu(1)->DeleteMenu(0,MF_BYPOSITION );

2、针对菜单项的动态操作

1、插入菜单项同样使用AppendMenu和InsertMenu

  m_menu.AppendMenuA(MF_STRING, 111, "hello");//往弹出菜单TEST中添加菜单项
  GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING, 113, "hello1");//往文件子菜单末尾添加菜单项
  GetMenu()->GetSubMenu(0)->InsertMenu(1, MF_BYPOSITION | MF_STRING , 112 , "VC编程");

3、为 动态添加的菜单项 添加响应函数

//在头文件resource.h中创建菜单资源ID
#define  IDM_HELLO   111

//源文件中
m_menu.AppendMenuA(MF_STRING, IDM_HELLO, "hello");

//遵循消息映射机制:增加3处代码实现消息响应
//1、在响应这个菜单命令的类中添加响应函数原型,位于声明消息映射宏之上,(第11行所示)
protected:
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  afx_msg void OnHello();
  DECLARE_MESSAGE_MAP()
  
//2、在源文件的消息映射表中添加消息映射,消息映射宏是ON_COMMAND宏,(第17行所示)
//注意:消息映射表中,代码结尾不加分号
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  ON_WM_CREATE()
  ON_COMMAND(IDM_HELLO,OnHello)
END_MESSAGE_MAP()

//3、添加消息响应函数定义
void CMainFrame::OnHello()
{
  MessageBox("Hello");
}


第七章 对话框(一)

1、基本知识

控件:通常作为对话框的子窗口创建

对话框的种类:

模态对话框:对话框显示时,程序暂停执行,直到关闭对话框。

非模态:显示对话框时,可以执行其他任务。

2、创建和显示

CDialog对话框资源类

1.新建一个MFC类与该对话框关联:双击新建的对话框。

在这里插入图片描述

新建对话框资源后有两个按钮,功能一样(关闭对话框),返回值不同。

OK IDOK OnOK

Cancel IDCANCEL OnCancel

2.DoDataExchange():完成对话框数据的交换和校验。

3.在视类中

**CDialog::DoModal**创建并显示模态对话框

**CDialog::EndDialog**关闭模态对话框

3、创建动态按钮

1.视类→工具箱→对话框编辑器 创建一个按钮。

2.为该按钮添加功能

类向导→选择该对象对应的BN_CLICKED(按钮被单击的消息)消息

在CTestDlg类添加
private:
  CButton m_btn;//动态按钮对象
  BOOL isbtncreate;//判断动态按钮是否被创建,在构造函数中初始化为0

void CTestDlg::OnClickedBtnAdd()
{
  // TODO: 在此添加控件通知处理程序代码

  //动态创建按钮
  //static BOOL isbtncreate = 0;
  
  if (isbtncreate == 0)//判断动态按钮是否被创建,
  {
    //CButton::Create创建按钮
    m_btn.Create(_T("new"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), this, 123);
    isbtncreate = 1;
  }
  else
  {
    m_btn.DestroyWindow();//销毁窗口
    isbtncreate = 0;
  }
}

初始化变量的两种方法

1.创建成员变量,并在构造函数中初始化;

2.创建静态局部变量同时初始化。

4、对话框控件

1.静态文本控件

IDC_STATIC不能响应消息,可通过修改ID来响应消息

**CWnd::GetDlgItem**获取对话框子控件的指针

**CWnd::GetWindowText**获取窗口文本

**CWnd::SetWindowText**设置窗口文本

void CTestDlg::OnClickedNumber1()
{
  // TODO: 在此添加控件通知处理程序代码
  CString str;
  if (GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str == "数值1")
  //逗号表达式,返回最后一个表达式的值
  {
    GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1");
  }
  else
  {
    GetDlgItem(IDC_NUMBER1)->SetWindowText("数值1");
  }
}

最后在属性中选中通知(Notify)选项,否则不能向其父窗口发送鼠标事件。

2.编辑框控件

🌟GetDlgItem()获取编辑框框窗口对象的指针

1.法一:GetDlgItem→Get(Set)WindowText

void CTestDlg::OnClickedBtnAdd()
{
  // TODO: 在此添加控件通知处理程序代码

  //编辑框控件法一:  CWnd::GetDlgItem()->Get(Set)WindowText();获取(设置)指定窗口文本

  int num1, num2, num3;
  char ch1[10], ch2[10], ch3[10];
  
  GetDlgItem(IDC_EDIT1)->GetWindowText(ch1, 10);//(指向存放字符串内存的指针,最大字符数目)
  GetDlgItem(IDC_EDIT2)->GetWindowText(ch2, 10);
  num1 = atoi(ch1);//atoi字符串->数值
  num2 = atoi(ch2);
  num3 = num1 + num2;
  _itoa_s(num3, ch3, 10);//itoa数值->字符串,这里的10代表10进制
  GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);//将ch3中的字符串显示在编辑框中
 
 }

2.法二:Get(Set)DlgItemText

=GetDlgItem+Get(Set)WindowText

void CTestDlg::OnClickedBtnAdd()
{
  // TODO: 在此添加控件通知处理程序代码

 //编辑框控件法二:CWnd::Get(Set)DlgItemText()获取指定控件上的文本
  int num1 = 0, num2 = 0, num3 = 0;
  char ch1[10], ch2[10], ch3[10];

  GetDlgItemText(IDC_EDIT1, ch1,10);
  GetDlgItemText(IDC_EDIT2, ch2,10);//=GetDlgItem + GetWindowText
  
  num1 = atoi(ch1);
  num2 = atoi(ch2);
  num3 = num1 + num2;
  _itoa_s(num3, ch3, 10);
  
  SetDlgItemText(IDC_EDIT3, ch3);

} 

改写:

void CTestDlg::OnClickedBtnAdd()
{
  // TODO: 在此添加控件通知处理程序代码

 //编辑框控件法二:CWnd::Get(Set)DlgItemText()获取指定控件上的文本
  int num1 = 0, num2 = 0, num3 = 0;
//  char ch1[10], ch2[10], ch3[10];
  CString str1, str2, str3;
  GetDlgItemText(IDC_EDIT1, str1);
  GetDlgItemText(IDC_EDIT2, str2);
  num1 = atoi(str1);
  num2 = atoi(str2);
  num3 = num1 + num2;
//  str3 = (CString)num3;
//错误  C2440  “类型强制转换” : 无法从“int”转换为“CString”
//Format是CString类的一个成员函数,它通过格式操作使任意类型的数据转换成一个字符串。
  str3.Format("%d",num3);
  SetDlgItemText(IDC_EDIT3, str3);

} 

3.**CWnd::Get(Set)DlgItemInt**返回指定控件的文本并将其转换为一个整数值。

void CTestDlg::OnClickedBtnAdd()
{
  int num1 = 0, num2 = 0, num3 = 0;
  
  num1 = GetDlgItemInt(IDC_EDIT1);
  num2 = GetDlgItemInt(IDC_EDIT2);
  num3 = num1 + num2;
  
  SetDlgItemInt(IDC_EDIT3, num3);
} 

4.控件与整型变量相关联(注意DoDataExchange和UpdateData一起用)

步骤:项目→类向导→成员变量→IDC_EDIT1→添加变量→类别(值)→变量名称→变量类型→其他(最大值、最小值等)

在这里插入图片描述
在这里插入图片描述

系统为我们创建的代码:

  /*1、头文件定义*/
  // 编辑框与数值变量相关联
  int m_num1;
  int m_num2;
  int m_num3;

  /*2、构造函数初始化*/
CTestDlg::CTestDlg(CWnd* pParent /*=nullptr*/)
  : CDialog(IDD_DIALOG1, pParent)
  //数值变量初始化
  , m_num1(0)
  , m_num2(0)
  , m_num3(0)
 {
 }
 
 /*3、DoDataExchange将对话框控件与类成员变量相关联,要配合UpDateDate使用*/
 void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);

  //控件与数值变量关联
  DDX_Text(pDX, IDC_EDIT1, m_num1);//DDX_前缀用于数据交换
  DDV_MinMaxInt(pDX, IDC_EDIT1, 0, 100);//设定数值范围,DDV_数据校验
  DDX_Text(pDX, IDC_EDIT2, m_num2);
  DDX_Text(pDX, IDC_EDIT3, m_num3); 

 } 

自己编写的代码:

  void CTestDlg::OnClickedBtnAdd()
{
  //法四:控件与数值变量相关联

  UpdateData();//和DoDataExchange配合使用,参数为TRUE(缺省):函数获取对话框数据
  m_num3 = m_num1 + m_num2;
  UpdateData(FALSE);//参数为FALSE:以变量的值来初始化对话框控件

}

自动消息提示框:

在这里插入图片描述

5.控件与控件变量相关联

系统自动添加

  /*1、头文件定义*/
  //编辑框与控件变量相关联
  CEdit m_edit1;
  CEdit m_edit2;
  CEdit m_edit3;

 
 /*2、DoDataExchange将对话框控件与类成员变量相关联,要配合UpDateDate使用*/
 void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);

   //控件与控件变量关联
  DDX_Control(pDX, IDC_EDIT1, m_edit1);
  DDX_Control(pDX, IDC_EDIT2, m_edit2);
  DDX_Control(pDX, IDC_EDIT3, m_edit3);
手动添加
  
  //法五:控件与控件变量相关联

  int num1 = 0, num2 = 0, num3 = 0;
  char ch1[10], ch2[10], ch3[10];
  
  m_edit1.GetWindowText(ch1, 10);
  m_edit2.GetWindowText(ch2, 10);
  
  num1 = atoi(ch1);
  num2 = atoi(ch2);
  num3 = num1 + num2;
  _itoa_s(num3, ch3, 10);
  
  m_edit3.SetWindowText(ch3);


6.**SendMessage()**发送消息

  int num1 = 0, num2 = 0, num3 = 0;
  char ch1[10], ch2[10], ch3[10];
  ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd, WM_GETTEXT, 10, (LPARAM)ch1);//获取编辑框窗口对象的指针
  ::SendMessage(m_edit2.m_hWnd, WM_GETTEXT, 10, (LPARAM)ch2);//第一个参数直接利用控件变量获取句柄
//Windows系统中,获取文本的消息是WM_GETTEXT,将系统指定窗口的文本复制到调用者提供的一个缓存中
//wParam指定复制的字符数,lParam保存窗口文本的缓存地址

  num1 = atoi(ch1);
  num2 = atoi(ch2);
  num3 = num1 + num2;
  _itoa_s(num3, ch3, 10);

  m_edit3.SendMessage(WM_SETTEXT, 0, (LPARAM)ch3);
//WM_SETTEXT设置窗口文本的消息,lParam指定设置窗口文本的字符串地址

7.**SendDlgItemMessage()**直接给对话框的子控件发送消息

  //法七:

  int num1 = 0, num2 = 0, num3 = 0;
  char ch1[10], ch2[10], ch3[10];
  SendDlgItemMessage(IDC_EDIT1, WM_GETTEXT, 10, (LPARAM)ch1);
  SendDlgItemMessage(IDC_EDIT2, WM_GETTEXT, 10, (LPARAM)ch2);

  num1 = atoi(ch1);
  num2 = atoi(ch2);
  num3 = num1 + num2;
  _itoa_s(num3, ch3, 10);

  SendDlgItemMessage(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)ch3);

补充:

  //设置复选内容
  SendDlgItemMessage(IDC_EDIT3, EM_SETSEL, 1, 3);
 // EM_GETSEL获得编辑框中的复选内容
 //EM_SETSEL设置……,EM表示Edit Control Message
 
 //wParam接收复选内容开始位置,lParam ……结束位置.
 //wParam=0,lParam=-1,表示所有文本
  m_edit3.SetFocus();//设置光标

5、对话框的伸缩

CWnd::SetWindowPos

**BOOL SetWindowPos (const CWnd**\* *pWndInsertAfter*, **int** *x*, **int** *y*, **int** *cx*, **int** *cy*, **UINT** *nFlags***)//** 设置窗口位置、大小、z次序等

在这里插入图片描述

CWnd::GetDlgItem

This method retrieves a pointer to the specified control or child window in a dialog box or other window. The pointer returned is usually cast to the type of control identified by nID.
CWnd GetDlgItem (int* nID )const;

CWnd::SetDlgItemText

void SetDlgItemText(int nID**, LPCTSTR** lpszString**);//** 将nID中的文本设置为lpszString

CWnd::GetWindowRect

void GetWindowRect(LPRECT lpRect**)const;//** 获得窗口尺寸

CRect::IsRectNull

CRect::IsRectEmpty

void CTestDlg::OnBnClickedButton1()
{
  // TODO: 在此添加控件通知处理程序代码
  CString str;
  if (GetDlgItemText(IDC_BUTTON1,str),str == "收缩<<")
  {
    SetDlgItemText(IDC_BUTTON1, "括展>>");
  }
  else
  {
    SetDlgItemText(IDC_BUTTON1, "收缩<<");
  }

  static CRect rectlarge;//原对话框尺寸
  static CRect rectsmall;//切割后的尺寸
  if (rectlarge.IsRectNull())//判断矩形区域是否为空
  {
    CRect rectseparator;//分割线的尺寸
    GetDlgItem(IDC_SEPARATOR)->GetWindowRect(rectseparator);//获得分割线的尺寸
    GetWindowRect(rectlarge);//获得原对话框的尺寸
    rectsmall.top = rectlarge.top;
    rectsmall.left = rectlarge.left;
    rectsmall.right = rectlarge.right;
    rectsmall.bottom = rectseparator.bottom;//将对话框底部的尺寸更改
  }
  if (str == "收缩<<")
  {
    SetWindowPos(NULL, 0, 0, rectsmall.Width(), rectsmall.Height(), SWP_NOMOVE | SWP_NOZORDER);//设置窗口的位置和大小
  }
  else
  {
    SetWindowPos(NULL, 0, 0, rectlarge.Width(), rectlarge.Height(), SWP_NOMOVE | SWP_NOZORDER);
  }
}

6、输入焦点的传递

void CTestDlg::OnBnClickedOk()//注释掉默认的OnOK
{
  GetNextDlgTabItem(GetFocus())->SetFocus();//按照TAB顺序循环查找【格式】->【TAB顺序】
//  CDialog::OnOK();
}

WNDPROC prevproc;//窗口过程类型,接收先前的窗口过程。窗口过程函数是全局函数,里面的函数不能再用CWnd的成员函数,只能用API函数
LRESULT CALLBACK WinEnterProc//windowproc函数
(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
  if (uMsg == WM_CHAR && wParam == 0x0d)
  {
//    ::SetFocus(::GetNextWindow(hwnd, GW_HWNDNEXT));
//    ::SetFocus(::GetWindow(hwnd, GW_HWNDNEXT));
    ::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd), hwnd, 0));//SetFocus设置焦点,GetFocus得到焦点
                                  //GetNextWindow、GetWindow获得下一个窗口的句柄
                                  //GetNextDlgTabItem按照TAB顺序转移焦点
//    return 1;
  }
  else//按下的按键不是回车,让先前的窗口过程来处理
  {
    return prevproc(hwnd,uMsg,wParam,lParam);
  }
}

BOOL CTestDlg::OnInitDialog()//对话框及其子控件创建完成将要显示之前调用。虚函数。

  CDialog::OnInitDialog();

  // TODO:  在此添加额外的初始化
  //SetWindowLong修改指定窗口的属性,本例修改已指定的过程函数
  prevproc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd, GWL_WNDPROC/*设置新的窗口过程地址*/, (LONG)WinEnterProc/*新的窗口过程地址*/);

  return TRUE;  // return TRUE unless you set the focus to a control
          // 异常: OCX 属性页应返回 FALSE
}

7、默认按钮的说明

收缩按钮设置为默认按钮,即在其属性对话框中选择default button选项,就不会再由CTestDlg类的OnOK函数来响应。
当用户按下回车键时,windows将查看对话框中是否存在默认按钮, 如果没有默认按钮,就会调用虚拟的OnOK函数, 即没有默认ok按钮。这个默认OnOK函数的ID是IDOK。而不是IDC_OK.

第八章 对话框2

1、逃跑按钮

逃跑按钮创建过程
1、创建新类(专注于逃跑功能)
- 存储地址的变量
- OnMouseMove()
2、CTestDlg.h控件与控件变量关联,即创建了新类的对象
3、CTestDlg.cpp的OnInitDialog()中两个对象存储对方首地址
4、鼠标移动OnMouseMove()接收WM\_MOUSEMOVE消息,执行程序

2、属性表单的创建

一个属性表单是由一个或多个属性页组成,属性页对应MFC中的CPropertyPage类,一个属性页的标题就是选项卡的名称。

步骤:

1、创建属性页

在这里插入图片描述

2、添加控件

组框Group Box

单选按钮Radio Button

复选框Check Box

列表框控件List Box

静态文本控件Static Box

组合框Combo Box

simple,能输入,总是显示列表框

dropdown,能输入只有单击下拉列表后,列表框才弹出

droplist,只读,只能从下来列表选择内容

3、为对话框资源创建新类

1.创建新类步骤:参考1,在“添加MFC类”中添加类名与ID号一致,基类中选择相应的类型

属性页CPropertyPage

属性表单CPropertySheet

2.在属性表单(CPropSheet)中,创建三个属性页对象,并在构造函数中初始化。用AddPage将属性页添加进属性表单,构造函数指明了2中实例化对象的方法(ID/字符串)

CPropSheet::AddPage将属性页添加到属性表单中

OnPropertysheet::DoModal()创建模态属性表单

  /*定义*/
  //自定义成员变量
  PROP1 m_prop1;
  PROP2 m_prop2;
  PROP3 m_prop3;

 /*初始化*/
 CPropSheet::CPropSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
  :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)//用ID号来构造属性表单
{
  //初始化属性页
  AddPage(&m_prop1);//CPropSheet::AddPage将属性页添加到属性表单中
  AddPage(&m_prop2);
  AddPage(&m_prop3);
}

CPropSheet::CPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
  :CPropertySheet(pszCaption, pParentWnd, iSelectPage)//用字符串来构造属性表单
{
  AddPage(&m_prop1);
  AddPage(&m_prop2);
  AddPage(&m_prop3);
} 

3.在视类中创建菜单命令响应函数,用字符串实例化属性表单对象,并用DoModal创建模态属性表单

void CpropView::OnPropertysheet()
{
  // TODO: 在此添加命令处理程序代码
  CPropSheet propsheet("属性表单");//使用字符串构造属性表单对象
//  propsheet.SetWizardMode();//创建向导样式时添加
  propsheet.DoModal();//创建模态属性表单
}

3、向导的创建

1、更改向导底部按钮CPropertySheet::SetWizardButtons

BOOL PROP1::OnSetActive()//右键PROP1类->类向导->虚函数->OnSetActive->编辑代码
{
  // TODO: 在此添加专用代码和/或调用基类
  CPropertySheet* psheet = (CPropertySheet*)GetParent();//属性页的父窗口属性表单,并将CWnd*转换为CPropertySheet*
  psheet->SetWizardButtons(PSWIZB_NEXT);//CPropertySheet::SetWizardButtons设置对话框上的按钮

  return CPropertyPage::OnSetActive();
}

PROP2,PROP3过程类似,SetWizardButtons参数不同。

1、设置第一个属性页

将第一个单选按钮设置为Group属性以添加成员变量。第一个单选按钮设置为Group后,之后的按钮和这个按钮属于同一组,直到遇到下一个Group。同一组内的控件关联的成员变量的值依次为1、2、3…

单击【下一步】,调用OnWizardNext()虚函数

/*PROP1.h*/
  int m_occupation;

/*PROP1.cpp*/ 
//构造函数
PROP1::PROP1()
  : CPropertyPage(IDD_PROP1)
  , m_occupation(-1)
{
}

//数据交换
void PROP1::DoDataExchange(CDataExchange* pDX)
{
  CPropertyPage::DoDataExchange(pDX);
  DDX_Radio(pDX, IDC_RADIO1, m_occupation);//单选按钮和成员变量之间的数据交换
}

//判断
LRESULT PROP1::OnWizardNext()//单击【下一步】,调用OnWizardNext()虚函数
{
  // TODO: 在此添加专用代码和/或调用基类
  UpdateData();//系统通过调用UpdateData()来调用DoDataExchange以完成控件与变量的数据交换
               //TRUE从控件得到成员变量的值。 FAUSE用成员变量的值初始化控件
  if (m_occupation == -1)//用户没有选择职业
  {
    MessageBox("请选择职业!");
    return -1;//OnWizardNext()返回0,进入下一个属性
              //返回-1,禁止属性页变更
  }
  if (m_workaddress == "" )//工作地点为空
  {
    MessageBox("请选择工作地点!");
    return -1;
  }
  return CPropertyPage::OnWizardNext();//进入下一个页面
}

BOOL PROP1::OnInitDialog()
{
  CPropertyPage::OnInitDialog();

  // TODO:  在此添加额外的初始化
  CListBox* listbox = (CListBox*)GetDlgItem(IDC_LIST1);
  listbox->AddString("北京");//CListBox::AddString添加字符串到列表框
  listbox->AddString("天津");
  listbox->AddString("上海");

  return TRUE;  // return TRUE unless you set the focus to a control
          // 异常: OCX 属性页应返回 FALSE
}

2、设置第二个属性页

操作类似第一个属性页

LRESULT PROP2::OnWizardNext()
{
  // TODO: 在此添加专用代码和/或调用基类
  UpdateData();
  if (m_football || m_basketball || m_volleyball || m_swim)
  {
    return CPropertyPage::OnWizardNext();
  }
  else
  {
    MessageBox("请选择那你的兴趣爱好!");
    return -1;//返回-1,禁止属性页变更
  }
}

3、设置第三个属性页

CComboBox::AddString向组合框的列表框中添加字符串选项

CComboBox::SetCurSel选择列表框中的一个字符串,并将其显示在该组合框的编辑框中

BOOL PROP3::OnInitDialog()
{
  CPropertyPage::OnInitDialog();
  //组合框控件由一个编辑框和一个列表框组成
  //CComboBox::AddString向组合框的列表框中添加字符串选项
  //CComboBox::SetCurSel(set current selection)选择列表框中的一个字符串,并将其显示在该组合框的编辑框中
  CComboBox* combobox = (CComboBox*)GetDlgItem(IDC_COMBO1);
  combobox->AddString("1000元以下");
  combobox->AddString("1000元-2000元");
  combobox->AddString("2000元-3000元");
  combobox->AddString("3000元以上");//取消属性框中的排序功能以关闭自动排序
  combobox->SetCurSel(0);

  return TRUE;  // return TRUE unless you set the focus to a control
          // 异常: OCX 属性页应返回 FALSE
}

CComboBox::GetCurSel得到当前选项的索引值

CComboBox::GetLBText得到对应索引值的文本并存储在字符串中

/*提取用户的薪资选项,并存储在m_strsalary中*/
BOOL PROP3::OnWizardFinish()
{
  // TODO: 在此添加专用代码和/或调用基类
  int index;
  CComboBox* combobox = (CComboBox*)GetDlgItem(IDC_COMBO1);
  index = combobox ->GetCurSel();//得到当前选项的索引值
  combobox->GetLBText(index, m_strsalary);//得到对应索引值的文本并存储在字符串m_strsalary中

  return CPropertyPage::OnWizardFinish();
}

4、将结果在视类显示

memset初始化数组

Invalidate();//让视类窗口无效,从而引起重绘,然后在OnDraw中完成信息的输

  /*propView.h*/
  //自定义成员变量
  int m_ioccupation;  //职业
  CString m_strworkaddress;//工作地点
  BOOL m_blike[4];//爱好
  CString m_strsalary;//薪资水平



  /*propView.cpp*/
  //构造函数中初始化
CpropView::CpropView() noexcept
{
  // TODO: 在此处添加构造代码
  m_ioccupation = -1;  //职业,对于一个组,未选为-1
  m_strworkaddress = "";//工作地点
  memset(m_blike , 0 , sizeof(m_blike));//爱好,将m_blike内存的sizeof(m_blike)个字节设为0
  m_strsalary = "";//薪资水平
}


/*一般情况下,CPropertySheet 类的 DoModal 函数的返回值是 IDOK 或 IDCANCEL。
但是如果属性表单已经被创建为向导了,那么该函数的返回值将是 ID_WIZFINISH 或 IDCANCEL.
因此,在程序中应该对属性表单对象的 DoModal 函数的返回值进行判断,
如果返回的是【完成】按钮的 ID:ID_WIZFINISH,那么才进行输出处理。*/
void CpropView::OnPropertysheet()//菜单项响应函数
{
  // TODO: 在此添加命令处理程序代码
  CPropSheet propsheet("属性表单");//使用字符串构造属性表单对象
  propsheet.SetWizardMode();//设置向导样式
//  propsheet.DoModal();//创建模态属性表单
  if (ID_WIZFINISH == propsheet.DoModal())//属性表单被创建向导,DoModal()返回ID_WIZFINISH
  {
    m_ioccupation = propsheet.m_prop1.m_occupation;//向导.属性页.属性页成员
    m_strworkaddress = propsheet.m_prop1.m_workaddress;
    m_blike[0] = propsheet.m_prop2.m_football;
    m_blike[1] = propsheet.m_prop2.m_basketball;
    m_blike[2] = propsheet.m_prop2.m_volleyball;
    m_blike[3] = propsheet.m_prop2.m_swim;
    m_strsalary = propsheet.m_prop3.m_strsalary;
    Invalidate();//让视类窗口无效,从而引起重绘,然后在OnDraw中完成信息的输出
  }
}


//窗口重绘
void CpropView::OnDraw(CDC* pDC)
{
  CpropDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;

  // TODO: 在此处为本机数据添加绘制代码
  CFont font;
  font.CreatePointFont(100, "宋体",pDC);//创建字体
  CFont* pfont;
  pfont = pDC->SelectObject(&font);//将字体选入设备描述表,并保存旧字体

  //职位
  CString position;
  position = "你的职业:";
  switch (m_ioccupation)
  {
  case 0:
    position += "程序员";
    break;
  case 1:
    position += "系统工程师";
    break;
  case 2:
    position += "项目经理";
    break;
  }
  pDC->TextOut(0, 0, position);//将文本展示在视类窗口

  TEXTMETRIC tm;
  pDC->GetTextMetrics(&tm);

  //工作地点
  CString workplace;
  workplace = "你的工作地点:";
  workplace += m_strworkaddress;
  pDC->TextOut(0, tm.tmHeight, workplace);

  //兴趣爱好
  CString hobby;
  hobby = "你的爱好:";
  if (m_blike[0])
    hobby += "足球 ";
  if (m_blike[1])
    hobby += "篮球 ";
  if (m_blike[2])
    hobby += "排球 ";
  if (m_blike[3])
    hobby += "游泳 ";
  pDC->TextOut(0, tm.tmHeight*2, hobby);

  //薪资
  CString salary;
  salary = "你的薪资水平:";
  salary += m_strsalary;
  pDC->TextOut(0, tm.tmHeight*3, salary);

  pDC->SelectObject(pfont);
}
  

第九章 定制应用程序外观

4、工具栏编程ToolBar

1.在工具栏上添加、删除按钮

创建按钮:按钮和菜单项用一样的ID

按钮之间的分隔符:用鼠标把按钮向右拖动一段距离

删除按钮:将按钮脱出工具栏

2.创建工具栏

分析系统创建的工具栏

cmainframe头文件中
CToolBar          m_wndToolBar;

cmainframe.cpp中
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
......
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
    !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
  {
    TRACE0("未能创建工具栏\n");
    return -1;      // 未能创建
  }

 ....
  // TODO: 如果不需要可停靠工具栏,则删除这三行
  m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);//工具栏对象的成员函数EnableDocking,表示工具栏对象可以停靠
  //CBRS_ALIGN_ANY允许停靠在任意位置
  EnableDocking(CBRS_ALIGN_ANY);//CFrameWnd对象的EnableDocking成员函数,表示主框架窗口可以被停靠
  DockControlBar(&m_wndToolBar);//工具栏停靠在主框架窗口上

创建自定义工具栏(参照系统工具栏)

1.新建ToolBar资源

2.构造ToolBar对象

3.调用Create函数创建工具栏,并将其与ToolBar对象关联

4.LoadToolBar加载工具栏

5.设置停靠位置

头文件  
CToolBar    m_newtoolbar;//2.构造ToolBar对象
  
源文件
  if (!m_newtoolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
    !m_newtoolbar.LoadToolBar(IDR_TOOLBAR1))
//3.调用Create函数创建工具栏,并将其与ToolBar对象关联
//4.LoadToolBar加载工具栏
  {
    TRACE0("未能创建工具栏\n");
    return -1;      // 未能创建
  }
  
  //5.设置停靠位置
  m_newtoolbar.EnableDocking(CBRS_ALIGN_ANY);
  DockControlBar(&m_newtoolbar);

改进方法**CFrameWnd::ShowControlBar**隐藏或显示指定的控制条

void CMainFrame::OnViewNewtoolbar()
{ 
/*   
if (!m_newtoolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
    !m_newtoolbar.LoadToolBar(IDR_TOOLBAR1))
  {
    TRACE0("未能创建工具栏\n");
    return -1;      // 未能创建
  
  m_newtoolbar.EnableDocking(CBRS_ALIGN_ANY);
  DockControlBar(&m_newtoolbar);   
  */
          
  ShowControlBar(&m_newtoolbar, !m_newtoolbar.IsWindowVisible(), 1);
}


void CMainFrame::OnUpdateViewNewtoolbar(CCmdUI* pCmdUI)
{
  // 在菜单项前添加标记,见第六章
  pCmdUI->SetCheck(m_newtoolbar.IsWindowVisible());
}


5、状态栏StatusBar

步骤:

1.在资源视图String Table中添加响应的ID

2.在源文件中将响应的ID添加进indicator(状态行指示器)中

系统创建的状态栏

  CStatusBar        m_wndStatusBar;//定义默认状态栏对象

static UINT indicators[] =
{
  ID_SEPARATOR,           // 状态行指示器
  ID_INDICATOR_CAPS,
  ID_INDICATOR_NUM,
  ID_INDICATOR_SCRL,
//  IDS_TIMER,
//  IDS_PROGRESS,
}; 

//OnCreate()中
 if (!m_wndStatusBar.Create(this))//创建状态栏对象
  {
    TRACE0("未能创建状态栏\n");
    return -1;      // 未能创建
  }
  m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));//设置状态指示器(数组设置状态栏内容,数组元素个数)

  
在OnTimer()中添加
//在状态栏显示动态变化的系统时间
  CTime time;//接收系统时间
  CString str;//系统时间文本
  CClientDC dc(this);//调用GetTextExtent()
  CSize sz;//接收时间字符串大小
  int indicatorindex =  0;//indicator索引

  time = CTime::GetCurrentTime();//获取系统时间保存进time中
  str = time.Format("%H:%M:%S");//将time中的时间格式化
  indicatorindex = m_wndStatusBar.CommandToIndex(IDS_TIMER);//获取ID在indicator中的索引
  sz = dc.GetTextExtent(str);//获取文本显示宽度 
  
  m_wndStatusBar.SetPaneInfo(indicatorindex, IDS_TIMER, SBPS_NORMAL, sz.cx);//设置状态栏右侧小窗口属性
  m_wndStatusBar.SetPaneText(indicatorindex, str);//将str显示到indicator数组对应的小窗口

6、进度栏

# CProgressCtrl::Create//进度栏类CProgressCtrl
BOOL Create( DWORD dwStyle**,** const RECT& rect**,** CWnd* pParentWnd**,** UINT nID**);//** 创建进度栏

# CProgressCtrl::SetPos
int SetPos( int nPos**);//** 设置进度栏当前进度

# CStatusBar::GetItemRect
void GetItemRect(int nIndex**,LPRECT** lpRect**)const;//** 得到状态栏小窗格大小

🌟补充消息响应过程

1.头文件声明(定义)函数afx_msg LRESULT OnProgress(WPARAM, LPARAM);

2.消息与函数关联ON_MESSAGE(UM_PROGRESS, &OnProgress)

3.实现函数LRESULT CMainFrame::OnProgress(WPARAM wParam, LPARAM lParam)

void CMainFrame::OnPaint()//窗口大小发生变化->窗口重绘->重新获得rect
{
  CPaintDC dc(this); // device context for painting
                     // TODO: 在此处添加消息处理程序代码
                     // 不为绘图消息调用 CFrameWnd::OnPaint()
             
  CRect rect;
  m_wndStatusBar.GetItemRect(5, &rect);//获得窗格大小并储存在rect中
  if (!m_progress.m_hWnd)//如果句柄没有值,说明对象没有创建
  {
    m_progress.Create(WS_VISIBLE | WS_CHILD | PBS_SMOOTH, rect, &m_wndStatusBar, 123);//创建进度栏,WS_CHILD:The window is a child window. 
  }
  else
  {
    m_progress.MoveWindow(rect);//移动窗格,The CRect object or RECT structure that specifies the new size and position. 
  }
  m_progress.SetPos(50);//设置进度
}


7、在状态栏上显示鼠标当前位置

void CstyleView::OnMouseMove(UINT nFlags, CPoint point)
//视类中捕获鼠标移动的消息WM_MOUSEMOVE
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  CString str;
  str.Format("x = %d  y = %d",point.x, point.y);//格式化鼠标位置
  ((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
  /*上述代码中,首先格式化鼠标当前位置的信息。然后为了把该信息显示在
状态栏的第一个窗格上,需要获取状态栏对象。而状态栏对象是在框架类窗口中定义的,同时,
框架类窗口是视类窗口的父窗口,因此在视类对象中通过调用 GetParent 函数就可以得到
视类的父窗口,即框架窗口。因为该函数返回的是一个CWnd 类型的指针,而这里需要的
是 CMainFrame 类型的指针,所以需要进行一个转换。然后利用框架窗口对象去调用该对
象内部的状态栏成员变量:m_wndStatusBar,以得到状态栏对象,*/

  /*因为上述代码用到了框架类的类型,所以要在头文件中包含框架类头文件*/
  
  CView::OnMouseMove(nFlags, point);
 } 

十、绘图控制

1、简单绘图

CPen pen

SelectObject

GetStockObject

FromHandle

SetPixel

Rectangle

Ellipse

/*头文件*/
//自定义成员变量
private:
  UINT m_nDrawType;//标记用户选择那种图形
  CPoint m_ptOrigin;//保存鼠标左键按下的点

/*构造函数*/
CGraphicView::CGraphicView() noexcept
{
  // TODO: 在此处添加构造代码
  m_nDrawType = 0;//初始化绘图图形类型
  m_ptOrigin = 0;
}

/*视类消息处理程序*/
void CGraphicView::OnDot()//点
{
  // TODO: 在此添加命令处理程序代码
  m_nDrawType = 1;
}


void CGraphicView::OnLine()//直线
{
  // TODO: 在此添加命令处理程序代码
  m_nDrawType = 2;
}


void CGraphicView::OnRectangle()//矩形
{
  // TODO: 在此添加命令处理程序代码
  m_nDrawType = 3;
}


void CGraphicView::OnEllipse()//椭圆
{
  // TODO: 在此添加命令处理程序代码
  m_nDrawType = 4;
}


void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point)
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  m_ptOrigin = point;//保存鼠标左键按下的位置
  
  CView::OnLButtonDown(nFlags, point);
}


void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)//鼠标左键抬起,实现绘图功能
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  CClientDC dc(this);//绘图操作首先要有DC对象
  CPen pen(PS_SOLID, 10, RGB(255,0,0));//线条的颜色由DC中的画笔颜色确定,构造CPen对象并指定其颜色
  dc.SelectObject(&pen);//将画笔选入设备描述表
                        //加载资源用LoadBitmap等,CDC对象用SelectObject(&xxx)  
  CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//GetStockObject调用NULL_BRUSH创建透明画刷
                                                                          //CBrush的静态成员变量FromHandle将画刷句柄转化为指向画刷对象的指针
                                                                          //FromHandle的参数是HBRUSH类型,强制类型转化
  dc.SelectObject(pBrush);//将画刷选入设备描述表

  switch (m_nDrawType)
  {
  case 1:
    dc.SetPixel(point, RGB(255,0,0));//在指定的点设置一个像素
    break;
  case 2:
    dc.MoveTo(m_ptOrigin);
    dc.LineTo(point);
    break;
  case 3:
    dc.Rectangle(CRect(m_ptOrigin,point));//利用CRect对象绘制矩形
    break;
  case 4:
    dc.Ellipse(CRect(m_ptOrigin, point));//绘制椭圆
  default:
    break;
  }
  CView::OnLButtonUp(nFlags, point);
}


注意将CDC对象加入设备miaoshub;

注意静态成员函数的调用方式:要声明是哪个类的。

2、线宽线型(设置对话框)

1、将设置对话框创建一个新的对话框类

2、在主菜单添加设置菜单项并设置消息响应函数

3、控件关联无符号整型变量,View类创建同类型变量以保存其值

void CGraphicView::OnSetting()
{
  // TODO: 在此添加命令处理程序代码
  CSettingDlg dlg;//实例化设置对话框对象
  dlg.m_nLineWidth = m_nLineWidth;//保持之前设置过的线宽
  dlg.m_nLineStyle = m_nLineStyle;//保持之前设置过的线型
  if (IDOK == dlg.DoModal())
  {
    m_nLineWidth = dlg.m_nLineWidth;//保存新设置的值
    m_nLineStyle = dlg.m_nLineStyle;//保存设置的线型,WINGDI.h定义了一些符号常量,包括线型
                                    //本例设置的对话框的线型排序正好按照WINGDI.h的顺序定义
  }
}

3、颜色对话框

CColorDialog默认颜色对话框类了解(m_cc、CHOOSECOLOR、rgbResult、Flags (CC_RGBINIT))

void CGraphicView::OnColor()
{
  // TODO: 在此添加命令处理程序代码
  CColorDialog colordlg;//CColorDialog颜色对话框类
  colordlg.m_cc.Flags |= CC_RGBINIT;//设置颜色对话框初始选择的颜色,需要设置该对话框的CC_RGBINIT标记
  /*实际上,当在创建 CColorDialog 对象 dlg 时,它的数据成员 m_cc 中的 Flags 成员已经具有了一些初始的默认标记。
  当我们将 CC_RGBINTT 标记直接赋给 Flags 成员时,就相当于将 Flags 成员初始默认的标记都去掉了。
  这里不能给 Flags 标记直接赋值,应利用或操作(|)将CC_RGBINIT 标记与 Flags 先前的标记组合起来。*/
  colordlg.m_cc.rgbResult = m_ctr;//保持之前设置过的颜色
  if (IDOK == colordlg.DoModal())//创建颜色对话框
  {
    m_ctr = colordlg.m_cc.rgbResult;//CColorDialog类有一个结构体类型的变量m_cc
                                    //CHOOSECOLOR结构体的rgbResult变量保存了用户选择的颜色
                                    //将用户选择的颜色保存在m_ctr中
  }
}

4、字体对话框

DeleteObject释放这个字体资源

CFont::CreateFontIndirect利用lpLogFont指向的LOGFONT结构体中的一些特征初始化CFont对象

Invalidate()引起窗口重绘

头文件
CFont m_font;//字体对象
CString m_strFontName;//保存所选字体的名称

源文件
void CGraphicView::OnFont()
{
  // TODO: 在此添加命令处理程序代码
  CFontDialog fontdlg;//字体对话框对象
  if (IDOK == fontdlg.DoModal())
  {
    if (m_font.m_hObject)
    /*利用CGdiobject 对象的数据成员 m_hObject 来判断 m_font 对象是否已经与某个字体资源相关联了,
    该变量保存了与CGdiObject对象相关联的Windows GDI 的资源句柄。
    如果已经有关联了,则调用 DeleteObject 释放这个字体资源*/
    {
      m_font.DeleteObject();
    }
    m_font.CreateFontIndirect(fontdlg.m_cf.lpLogFont);//CFont::CreateFontIndirect利用lpLogFont指向的LOGFONT结构体中的一些特征初始化CFont对象
    m_strFontName = fontdlg.m_cf.lpLogFont->lfFaceName;//lfFaceName保存了字体名字
    Invalidate();
    /*窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,
    那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。
    MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。
    视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。*/
    /*系统会在多个不同的时机发送WM_PAINT消息:
    当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,
    这些动作都是由系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;
    大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,
    这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。*/
  }
}

5、示例对话框

void CSettingDlg::OnChangeLineWidth()
//对编辑框控件中的文本改变时,会向父窗口发送EN_CHANGE消息
{

  Invalidate();//让窗口重绘以在OnPaint中完成示例线条的绘制
}


void CSettingDlg::OnClickedRadio1()
//单击Radio Button时,按钮会向对话框发送BN_CLICKED消息
{
  Invalidate();
}

void CSettingDlg::OnClickedRadio2()
{
  // TODO: 在此添加控件通知处理程序代码
  Invalidate();
}

void CSettingDlg::OnClickedRadio3()
{
  // TODO: 在此添加控件通知处理程序代码
  Invalidate();
}


void CSettingDlg::OnPaint()
{
  CPaintDC dc(this); // device context for painting
             // TODO: 在此处添加消息处理程序代码
             // 不为绘图消息调用 CDialog::OnPaint()
  UpdateData();//当控件与一个成员变量相关联时,如果想让控件上的值反应到成员变量上,必须调用UpdateData()
  CPen pen(m_nLineStyle, m_nLineWidth, m_clr);
  dc.SelectObject(&pen);

  CRect rect;
  GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);
  /*要想在组框中绘图,那么首先要得到组框的矩形区域范围。
  这可以通过调用GetDlgltem 函数来得到指向组框窗口对象的指针,
  然后利用 GetWindowRect 函数获得组框窗口矩形区域的大小,参数是指向CRect或RECT结构体的变量,接收屏幕坐标
  需要提醒读者注意的是,这里不能直接调用 GetWindowRect 函数,否则得到的将是对话框的矩形区域大小。*/
  ScreenToClient(&rect);//得到矩形区域的大小后,将其原点从屏幕坐标转化为客户坐标(即将应用程序坐标转化为设置对话框坐标)

  dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);//组框左上角的y值+矩形区域高度的一半,即将线条移动到示例窗口中间
  dc.LineTo(rect.right - 20, rect.top + rect.Height() / 2);
}

6、改变对话框及其控件的背景和文本WM_CTLCOLOR

1、改变对话框及其控件的背景

//头文件声明
  CBrush m_brush;//画刷

//构造函数初始化
  m_clr = RGB(255,0,0);
  m_brush.CreateSolidBrush(RGB(0,0,255));//利用CreateSolidBrush函数将m_brush初始化画刷颜色

//消息响应函数
HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)//每一个控件(对话框和子控件)在绘制时都发送WM_CTLCOLOR消息
{
  HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

  // TODO:  在此更改 DC 的任何特性

  // TODO:  如果默认的不是所需画笔,则返回另一个画笔
  if (IDC_LINE_STYLE == pWnd->GetDlgCtrlID())//判断是否为线型组框
  {
    pDC->SetTextColor(RGB(255,0,0));//设置文本颜色为红色
    pDC->SetBkMode(TRANSPARENT);//将控件上的文字背景设置为透明
    return m_brush;//如果想要改变背景颜色,只需要自定义一个画刷,然后让OnColor函数返回这个画刷句柄即可
  }
  if (IDC_LINE_WIDTH == pWnd->GetDlgCtrlID())
  {
    pDC->SetTextColor(RGB(255, 0, 0));
//    pDC->SetBkMode(TRANSPARENT);
    pDC->SetBkColor(RGB(255, 0, 0));//设置窗口背景色
    return m_brush;
  }
  return hbr;
}

2、改变控件的文本

//头文件声明
private:
  CFont m_font;//改变控件上的文本字体

 //构造函数初始化
  m_font.CreatePointFont(200,"华文行楷");//初始化字体

 //消息响应函数
  HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)//每一个控件(对话框和子控件)在绘制时都发送WM_CTLCOLOR消息
{
  HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

  // TODO:  在此更改 DC 的任何特性

  // TODO:  如果默认的不是所需画笔,则返回另一个画笔
  if (IDC_TEXT == pWnd->GetDlgCtrlID())
  {
    pDC->SelectObject(&m_font);//将字体选入设备描述表->改变字体
  }
  return hbr;
}

3、改变按钮的背景色及文本

7、位图的显示

BOOL CGraphicView::OnEraseBkgnd(CDC* pDC)//擦除背景时,系统发送WM_ERASEBKGND消息
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  CBitmap bitmap;
  bitmap.LoadBitmap(IDB_BITMAP1);
  BITMAP bmp;//定义BITMAP结构体变量
  bitmap.GetBitmap(&bmp);//CBitmap::GetBitmap用位图信息填充BITMAP结构体

  CDC dcCompatible;//创建兼容DC
  dcCompatible.CreateCompatibleDC(pDC);//兼容DC(源DC)和当前DC兼容

  dcCompatible.SelectObject(&bitmap);//将位图选入兼容DC,从而确定兼容DC显示表面的大小

  CRect rect;
  GetClientRect(&rect);//获得目的DC客户区大小
//  pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0, SRCCOPY);//BitBlt()将源DC的位图1:1复制到目的DC
  pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcCompatible, 0, 0,bmp.bmWidth,bmp.bmHeight, SRCCOPY);
  //StretchBlt复制位图并实现拉伸或压缩;bmp.bmWidth,bmp.bmHeight存储源矩形的宽度和高度
  return TRUE;//已经擦除过窗口背景了,返回非零值
//  return CView::OnEraseBkgnd(pDC);//将窗口背景擦除
}

//构造函数初始化

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾宝玉怒撞不周山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值