[toc]

基础控件的使用

  1. 创建和初始化
  2. 消息的响应
  3. 控制函数

按钮

  1. 使用 CButton 来绑定按钮控件,一般不需初始化
  2. 消息响应:只有按钮被单击的消息
  3. 控制函数:SetWindowText 设置按钮的标题

单选框

单选框也是按钮的一种,也有被点击的消息,响应方式和按钮一样

  1. 使用 CButton 绑定单选框控件
  2. 初始化:
    1. 单选框一般都是分组,同一组单选框的选中状态是互斥的
    2. 需要将每一组单选框第一个按钮的 Group 属性设置为 True
  3. 操作函数:
    1. 若单选框控件绑定 CButton 类的变量,可以使用 GetCheckSetCheck 来获取和设置单选框的选中状态
    2. 若单选框控件绑定 int 类型的变量,int 变量的值是一个分组中的单选框的序号,使用 UpdateData 时,int 型变量的值是几,分组中的第几个单选框就会被选中
    3. 只需将分组中第一个单选框添加 int 变量即可,修改 int 型变量的值在 UpdateData 时,MFC 会自动操作同一分组内其他的单选框

添加好单选框,设置好 Group 属性和添加变量后,再添加一个按钮1,双击按钮,自动生成 OnBnClickedButton1() 函数,就可以在其中编程

1
2
3
4
5
6
7
8
9
10
11
void CMFCApplication2Dlg::OnBnClickedButton1()
{
//修改第一组单选框的选中状态
UpdateData(TRUE);
CString buff;
buff.Format(L"选中%d", m_Radio1);
MessageBox(buff);
//修改第二组单选框为随机选中
m_Radio2 = rand() % 4;
UpdateData(FALSE);
}

复选框

  1. 初始化
  2. 消息:和按钮一样
  3. 操作函数:
    1. 绑定 CButton 类型的变量,可以使用 GetCheckSetCheck
    2. 绑定值类型的变量,可以使用 UpdateData 来获取和修改选中状态

同上,添加按钮2,双击,在函数内编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void CMFCApplication2Dlg::OnBnClickedButton2()
{
//对第一个复选框"aaa"添加的控件进行设置
if (m_Check1.GetCheck()) {
MessageBox(L"aaa");//若选中"aaa",则打印
}
UpdateData(TRUE);
//设置添加的值
CString buff;
if (m_Check_Value1)
buff = L"aaa";
if (m_Check_Value2)
buff += L" bbb";
if (m_Check_Value3)
buff += L" ccc";
if (!buff.IsEmpty())//判断是否为空
MessageBox(buff);
}

编辑框

  1. 初始化:在资源编辑视图的属性栏中设置

  2. 消息:编辑框文本被修改:EN_CHANGE

  3. 操作函数:可以在 OnInitDialog 函数中添加设置函数,也可在编辑框的属性中修改,初始化编辑框

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //设置编辑框的最大文本个数
    m_edit.SetLimitText(10000);
    //设置是否是只读
    m_edit.SetReadOnly(0);
    //密码风格,显示的字符默认是"*"
    m_edit.SetPasswordChar('@');
    //获取文本
    CString buff;
    m_edit.GetWindowText(buff);
    //获取一共有几行
    DWORD dwLineCount = m_edit.GetLineCount();
    //滚动到指定行
    m_edit.LineScroll(dwLineCount);

    1571988410119

    按钮3设置编辑框文本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void CMFCApplication2Dlg::OnBnClickedButton3()
    {
    //获取文本
    UpdateData(FALSE);
    CString buff;
    m_edit.GetWindowText(buff);
    MessageBox(buff);
    //获取行数
    DWORD dwLineCount = m_edit.GetLineCount();
    buff.Format(L"%d", dwLineCount);
    MessageBox(buff);
    }

列表框

  1. 初始化

  2. 消息:

    SEL_CHANGE:当前选中项被修改

  3. 操作函数:控件类型绑定(CListBox

    1. AddString:将一行字符串添加到列表框
    2. ResetContent:清空所有项
    3. GetCurSel:获取当前选中项的下标
    4. GetText:获取指定下标的文本
    5. 更多函数可以 F1 CListBox 类查看帮助文档

OnInitDialog() 函数中初始化

1
2
3
4
5
6
7
8
9
10
11
BOOL CMFCApplication2Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

m_ListBox1.AddString(L"AAA");
m_ListBox1.AddString(L"BBB");
m_ListBox1.AddString(L"CCC");
m_ListBox1.AddString(L"DDD");

return TRUE;
}

在类向导的命令中,根据控件类型找到函数进行编程实现功能

1571991287066

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void CMFCApplication2Dlg::OnSelchangeList1()
{
//获取当前选中项
int nIndex = m_ListBox1.GetCurSel();//获取下标
if (nIndex == -1)
return;
//获取列表框中指定行的文本
CString buff;
m_ListBox1.GetText(nIndex, buff);
MessageBox(buff);
//根据列表框1的内容,在列表框2中显示其他内容
switch (nIndex) {
case 0://清空所有选项
m_ListBox2.ResetContent();
m_ListBox2.AddString(L"Aaaa");
m_ListBox2.AddString(L"Abbb");
m_ListBox2.AddString(L"Accc");
m_ListBox2.AddString(L"Addd");
break;
case 1:
m_ListBox2.ResetContent();
m_ListBox2.AddString(L"Baaa");
m_ListBox2.AddString(L"Bbbb");
m_ListBox2.AddString(L"Bccc");
m_ListBox2.AddString(L"Bddd");
break;
case 2:
m_ListBox2.ResetContent();
m_ListBox2.AddString(L"Caaa");
m_ListBox2.AddString(L"Cbbb");
m_ListBox2.AddString(L"Cccc");
m_ListBox2.AddString(L"Cddd");
break;
case 3:
m_ListBox2.ResetContent();
m_ListBox2.AddString(L"Daaa");
m_ListBox2.AddString(L"Dbbb");
m_ListBox2.AddString(L"Dccc");
m_ListBox2.AddString(L"Dddd");
break;
default: break;
}
}

下拉框

下拉框也称组合框,是编辑框和列表框的组合

  1. 初始化:有一个列表是否排序的属性

  2. 消息:ON_CBN_SELENDCANCEL 当前选中项改变

  3. 操作函数:

    1. AddString:向类表框添加文本
    2. GetCurSel:获取列表框的当前选中项
    3. GetLBText:获取列表框指定行的文本
    4. ResetContent:清空列表框的所有节点
    5. SetCurSel:设置列表框的当前选项
    6. SetEditSel:设置编辑框的文本

    初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    BOOL CMFCApplication2Dlg::OnInitDialog()
    {
    CDialogEx::OnInitDialog();

    m_ComBox1.AddString(L"河西");
    m_ComBox1.AddString(L"河北");
    m_ComBox1.AddString(L"河南");
    m_ComBox1.AddString(L"河东");
    m_ComBox1.SetCurSel(0);

    return TRUE;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void CMFCApplication2Dlg::OnSelchangeCombo1()
    {
    int nIndex = m_ComBox1.GetCurSel();
    if (nIndex == -1)
    return;
    CString buff;
    m_ComBox1.GetLBText(nIndex, buff);
    switch (nIndex) {
    case 0:
    m_ComBox2.ResetContent();
    m_ComBox2.AddString(L"111");
    m_ComBox2.AddString(L"112");
    m_ComBox2.AddString(L"113");
    break;
    case 1:
    m_ComBox2.ResetContent();
    m_ComBox2.AddString(L"211");
    m_ComBox2.AddString(L"212");
    m_ComBox2.AddString(L"213");
    break;
    case 2:
    m_ComBox2.ResetContent();
    m_ComBox2.AddString(L"311");
    m_ComBox2.AddString(L"312");
    m_ComBox2.AddString(L"313");
    break;
    case 3:
    m_ComBox2.ResetContent();
    m_ComBox2.AddString(L"411");
    m_ComBox2.AddString(L"412");
    m_ComBox2.AddString(L"413");
    break;
    default: break;
    }
    m_ComBox2.SetCurSel(0);
    }

静态文本

标签

一般不需要绑定变量,不需响应消息,只是文本提示。若想动态修改标签的文本,可以得到标签控件的窗口句柄,使用 SetWindowText 来修改

图片控件

  1. 初始化:

    1. 修改资源 ID(类向导不会自动修改静态文本类的控件资源 ID)
    2. type 属性修改成 bitmap
    3. center image 改为 TRUE,图片控件不能缩放要显示的图片。若图比控件大,控件会自动将大小修改成和图片一样大,该属性设为 TRUE 之后,控件大小就不会自动跟随图片大小,而是固定大小并只显示图片的部分内容
  2. 消息:不需处理

  3. 操作函数:

    绑定控件类型(CStatic

  4. 给图片控件设置图片

    1. 使用资源

      将一张位图添加到资源中,在图片控件的 Image 属性中填写位图的资源 ID,调用 CStatic.SetBitmap 函数将位图资源句柄设置进去

      1571995457852

      调用 CStatic.SetBitmap 函数,将位图的资源句柄设置进去

      1
      2
      3
      4
      5
      6
      7
      void CMFCApplication2Dlg::OnBnClickedButton4()
      {
      static int index = 0;
      m_Pic.SetBitmap(LoadBitmap(AfxGetInstanceHandle(),
      (TCHAR*)(index % 2 ? IDB_BITMAP1 : IDB_BITMAP2)));
      ++index;
      }

      另设一个按钮修改图片,从磁盘中获取图片

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      void CMFCApplication2Dlg::OnBnClickedButton5()
      {
      CFileDialog dlg(TRUE);
      dlg.DoModal();
      CString path = dlg.GetPathName();
      if (path.IsEmpty())
      return;
      CImage img;// 使用CImage类加载图片
      img.Load(path);
      // 将CImage类对象作为图片对象设置到图片控件中
      m_Pic.SetBitmap(img);
      }
  5. 加载 GIF 动图

    默认的 CStatic 类不支持这个功能,需要自定义,网上有 CPictureEx 类。添加图片控件添加变量时变量的类型使用 CPicturEx 类,在对话框初始化函数中,使用 CPicturEx.Load 函数加载 GIF 图片,使用 CPicturEx.Draw 函数来绘制 GIF 图片

    OnInitDialog() 函数内添加

    1
    2
    //初始化gif动图,直接传图片的路径
    m_Gif.Load(L"123.gif");
    1
    2
    3
    4
    5
    6
    7
    8
    void CMFCApplication2Dlg::OnBnClickedButton6()
    {
    m_Gif.Draw();
    }
    void CMFCApplication2Dlg::OnBnClickedButton7()
    {
    m_Gif.Stop();
    }

滑块

  1. 初始化

    设置水平或垂直风格

    1572001890150

    设置滑块取指范围:SetRange()

  2. 消息:

    1. 若滑块是水平风格,向父窗口发送 WM_HSCROLL 消息
    2. 若滑块是垂直风格,向父窗口发送 WM_VSCROLL 消息
  3. 操作函数:控件类型绑定(CSliderCtrl

    获取当前位置:GetPos

    设置当前位置:SetPos

进度条

  1. 进度条实际就是一个在界面上修改位置的滑块,通过 SetPos 来修改进度条的进度
  2. 进度条和滑块外观不一样,但也有水平和垂直
  3. 进度条和滑块一模一样
    1. 通过 SetRange 设置范围
    2. 通过 SetPos 来设置位置
    3. 通过 GetPos 来获取当前的位置

代码和滑块的写在同一个函数里,水平和垂直函数是在类向导的消息中

1572005853571

1572006020754

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void CMFCApplication2Dlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CDialogEx::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CMFCApplication2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
//获取滑块当前位置
DWORD dwPos = m_Slider1.GetPos();
//修改垂直滑块的位置
m_Slider2.SetPos(dwPos);
//修改进度条的位置
m_Progress1.SetPos(dwPos);
m_Progress2.SetPos(dwPos);

CString buff;
buff.Format(L"%d", dwPos);
m_Text.SetWindowText(buff);
}

1572006775159

菜单栏

  1. 添加一个菜单资源
  2. 使用 CMenu 来加载菜单
  3. OnInitDialog 函数中使用 SetMen 函数来设置程序的菜单栏
1
2
3
4
//加载菜单栏
m_menu.LoadMenu(IDR_MENU1);
//设置菜单栏
SetMenu(&m_menu);

菜单栏的响应消息:

1572008041544

1572008116690

1
2
3
4
void CMenuDlg::OnFileOpen32771()
{
MessageBox(L"打开文件");
}

每一个菜单都这样操作

弹出菜单

不能弹出根菜单

状态栏

OnInitDialog 函数中初始化

1
2
3
4
5
6
7
8
9
10
11
//创建状态栏
m_StatusBar.Create(this);
//设置指示器的ID
UINT ids[] = { 1000, 1001 };
m_StatusBar.SetIndicators(ids, 2);
//设置面板宽度
m_StatusBar.SetPaneInfo(0, 0, 0, 120);
m_StatusBar.SetPaneInfo(1, 1, 0, 80);
//设置面板的文本
m_StatusBar.SetPaneText(0, L"123456789");
m_StatusBar.SetPaneText(1, L"abcdefghi");

还需在 WM_SIZE 的消息回调函数中来自动修改状态栏的位置

1
2
3
4
5
6
7
void CMenuDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
//重新跳转状态栏和工具栏在窗口上的位置
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST, 0);
}

工具栏

自己画工具图

  1. OnInitDialog 函数中加载

    1
    2
    3
    //加载资源
    m_ToolBar.Create(this);
    m_ToolBar.LoadToolBar(IDR_TOOLBAR1);
  2. 响应点击消息

    1. 先得到工具栏按钮 ID

      1572010128820

  1. 响应虚函数 WM_COMMAND 消息

  2. wParam 保存的就是按钮 ID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL CAboutDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
UINT id = LOWORD(wParam);
switch (id) {
case ID_BUTTON32775:// 状态栏按钮的ID
MessageBox(L"开");
break;
case ID_BUTTON32776:
MessageBox(L"关");
break;
case ID_BUTTON32777:
MessageBox(L"合");
break;
case ID_BUTTON32778:
MessageBox(L"启");
break;
default: break;
}
return CDialogEx:: OnCommand(wParam, lParam);
}

外部传入图片,在 OnInitDialog 函数中

1
2
3
4
5
6
7
8
9
10
//第二个工具栏
m_ToolBar2.Create(this);
//加载位图(位图必须是一个16*15的按钮)
m_ToolBar2.LoadBitmap(IDB_BITMAP1);
//设置每个按钮的ID
UINT toolBtnIds[] = { 2000, 2001,2002 };
m_ToolBar2.SetButtons(toolBtnIds, 3);
//重新跳转状态栏和工具栏在窗口上的位置
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST, 0);

通用控件

列表控件

列表框应该先添加列,再添加行,然后设置每一个单元格显示的内容。属性调为 report 模式

  1. 初始化:设置窗口风格

    1572076330852

  2. 消息:

    1. NM_CLICK:左键单击
    2. NM_DBCLICK:左键双击
    3. NM_RCLICK:右键单击
    4. NM_RDBCLICK:右键双击
    5. NM_RETURN:回车
  3. 操作函数:

    函数 说明
    InsertColumn 插入一列
    DeleteColumn 删除一列
    InsertItem 插入一行
    DeleteItem 删掉一行
    GetItemText 获取某一个单元格的文本
    SetItemText 设置某一个单元格的文本
    GetItemData 获取单元格中的值
    SetItemData 用int型数据设置单元格文本
    GetItemCount 获取行数
  4. 获取被点击的节点:

    初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES);

    //插入列
    m_List.InsertColumn(0,//列号
    L"进程名",//列标题
    0,//对齐风格
    120//列宽
    );
    m_List.InsertColumn(1, L"PID", 0, 120);
    m_List.InsertColumn(2, L"路径", 0, 120);
    //插入节点(插入时, 只能设置这行节点的第0列的文本)
    // 想要设置其它列需要通过SetItemText
    m_List.InsertItem(0, L"QQ.exe");
    //设置第0行第1列的文本
    m_List.SetItemText(0, 1, L"1122");
    m_List.SetItemText(0, 2, L"D:\\QQ.exe");
    m_List.SetItemData(0, (DWORD)L"aaaaaaaaaa");

    m_List.InsertItem(1, L"wechat.exe");
    m_List.SetItemText(1, 1, L"1133");
    m_List.SetItemText(1, 2, L"D:\\wechat.exe");
    m_List.SetItemData(1, (DWORD)L"bbbbbbbbbbb");
    //获取ID信息
    ProcessInfo* procInfo = new ProcessInfo;
    m_List.SetItemData(1, (DWORD_PTR)procInfo);

基本使用:

1
2
3
4
5
6
7
8
9
10
11
12
void CMFCApplication2Dlg::OnBnClickedButton1()
{
m_List.DeleteItem(0);//删除第一行
}
void CMFCApplication2Dlg::OnBnClickedButton2()//添加一行
{
int nCount = m_List.GetItemCount();//获取行数
m_List.InsertItem(nCount, L"huorong.exe");
m_List.SetItemText(nCount, 1, L"1144");
m_List.SetItemText(nCount, 2, L"E:\\huorong.exe");
m_List.SetItemData(nCount, (DWORD)L"cccccccccccc");
}

列表框的封装:

先创建一个 MFC 程序,在对话框资源中拖一个列表框,为其添加一个 MFC 类,基类是 CListCtrl,此类名是 CMyListCtrl,然后再添加变量,变量类型是 CMyListCtrl。在 CMyListCtrl 类中添加结构体和函数

1
2
3
4
5
6
7
8
9
10
11
12
#include<vector>
using std::vector;
typedef struct _COLUMNINFO {//列信息
TCHAR* szName;//列名
int nCx;//列宽
}COLUMNINFO, *pCOLUMNINFO;
typedef struct _ITEMINFO {//行信息
vector<TCHAR*> vecItemInfo;
}ITEMINFO, *pITEMINFO;
typedef struct _LISTINFO {//整个表的信息
vector<ITEMINFO> vecListInfo;
}LISTINFO, *pLISTINFO;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//初始化列数据
void CMyListCtrl::InitList(vector<COLUMNINFO>& vecColumnInfo)
{
for (int i = 0; i < vecColumnInfo.size(); i++) {
InsertColumn(i, vecColumnInfo[i].szName, 0, vecColumnInfo[i].nCx);
}
}
//设置每行数据
void CMyListCtrl::SetListInfo(LISTINFO& ListInfo) {
for (size_t i = 0; i < ListInfo.vecListInfo.size(); i++) {
InsertItem(i, L"");//插入一行
for (size_t j = 0; j < ListInfo.vecListInfo[i].vecItemInfo.size(); j++) {//设置每行单元格内容
SetItemText(i, j, ListInfo.vecListInfo[i].vecItemInfo[j]);
}
}
}

在主对话框中添加和显示数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void CMFCApplication1Dlg::OnBnClickedButton1()//添加列数据按钮
{
//构造数据
COLUMNINFO stcColumn;
stcColumn.nCx = 100;
stcColumn.szName = L"姓名";
vector<COLUMNINFO> vecInfo;
vecInfo.push_back(stcColumn);

stcColumn.nCx = 100;
stcColumn.szName = L"年龄";
vecInfo.push_back(stcColumn);

stcColumn.nCx = 150;
stcColumn.szName = L"学号";
vecInfo.push_back(stcColumn);

stcColumn.nCx = 150;
stcColumn.szName = L"籍贯";
vecInfo.push_back(stcColumn);
//显示数据
m_objList.InitList(vecInfo);
}
void CMFCApplication1Dlg::OnBnClickedButton2()//显示所有行数据按钮
{
LISTINFO stcListInfo;
ITEMINFO stcItemInfo;
//构造数据
stcItemInfo.vecItemInfo.push_back(L"小明");
stcItemInfo.vecItemInfo.push_back(L"18");
stcItemInfo.vecItemInfo.push_back(L"0");
stcItemInfo.vecItemInfo.push_back(L"北京");
//放入一行数据
stcListInfo.vecListInfo.push_back(stcItemInfo);
stcItemInfo.vecItemInfo.clear();//清空数据

stcItemInfo.vecItemInfo.push_back(L"小宏");
stcItemInfo.vecItemInfo.push_back(L"19");
stcItemInfo.vecItemInfo.push_back(L"1");
stcItemInfo.vecItemInfo.push_back(L"天津");
stcListInfo.vecListInfo.push_back(stcItemInfo);
stcItemInfo.vecItemInfo.clear();

stcItemInfo.vecItemInfo.push_back(L"小白");
stcItemInfo.vecItemInfo.push_back(L"20");
stcItemInfo.vecItemInfo.push_back(L"2");
stcItemInfo.vecItemInfo.push_back(L"杭州");
stcListInfo.vecListInfo.push_back(stcItemInfo);
//显示数据
m_objList.SetListInfo(stcListInfo);
}

通过右键菜单获取每一列的信息,用到函数 OnRangeCmds(UINT nId),需添加消息映射 ON_COMMAND_RANGE(ID_32771, ID_32774, &CMFCApplication1Dlg::OnRangeCmds),菜单 ID 编号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void CMFCApplication1Dlg::OnRangeCmds(UINT nId) {
switch (nId) {
case ID_32771: {
CString m_Text = m_objList.GetItemText(m_nSelListRow, 0);
MessageBox(m_Text);}
break;
case ID_32772:{
CString m_Text = m_objList.GetItemText(m_nSelListRow, 1);
MessageBox(m_Text);}
break;
case ID_32773:{
CString m_Text = m_objList.GetItemText(m_nSelListRow, 2);
MessageBox(m_Text);}
break;
case ID_32774:{
CString m_Text = m_objList.GetItemText(m_nSelListRow, 3);
MessageBox(m_Text);}
break;
}
}
//右键函数需将行号传给一个变量
void CMFCApplication1Dlg::OnRclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
*pResult = 0;
POINT pt = {};
GetCursorPos(&pt);
m_nSelListRow = pNMItemActivate->iItem;//行
pNMItemActivate->iSubItem;//列
//获取菜单资源,弹出菜单
CMenu* pMenu = new CMenu;
pMenu->LoadMenuW(IDR_MENU1);
CMenu* pSubMenu = pMenu->GetSubMenu(0);
pSubMenu->TrackPopupMenu(0, pt.x, pt.y, this);
}

将磁盘信息显示在列表框里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//初始化列表控件
//初始化风格
// LVS_EX_FULLROWSELECT - 整行选中
// LVS_EX_GRIDLINES - 网格
// LVS_EX_CHECKBOXES - 节点的复选框
m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES);

//插入列
m_List.InsertColumn(0, L"盘符", 0, 80);
m_List.InsertColumn(1, L"卷标题", 0, 100);
m_List.InsertColumn(2, L"类型", 0, 80);
m_List.InsertColumn(3, L"", 0, 100);
m_List.InsertColumn(4, L"剩余空间", 0, 100);
m_List.InsertColumn(5, L"总空间", 0, 100);
//获取盘符
TCHAR buff[MAX_PATH];
GetLogicalDriveStrings(MAX_PATH, buff);
TCHAR* p = buff;
int i = 0;
TCHAR volName[100];
TCHAR fsName[100];
TCHAR* pDriverType[] = {L"未知", L"无效",L"可移动磁盘",
L"硬盘",L"远程磁盘",L"光盘",L"RAM" };
while (*p != 0) {
m_List.InsertItem(i, p);
//获取卷标题
GetVolumeInformation(p,//盘符
volName,//卷标题
sizeof(volName),
0, 0, 0,
fsName,//文件系统名
sizeof(fsName));
CString t;
t.Format(L"%s(%s)", volName, fsName);
m_List.SetItemText(i, 1, t);
//获取磁盘类型
DWORD type = GetDriveType(p);
m_List.SetItemText(i, 2, pDriverType[type]);

DWORD dwSectorsPerCluster = 0;// 没个簇的扇区的个数
DWORD dwBytesPerSector = 0;// 扇区字节数
DWORD dwNumberOfFreeClusters = 0;// 空闲空间的簇的个数
DWORD dwTotalNumberOfClusters = 0;// 总空间簇的个数
//获取空间
GetDiskFreeSpace(p, &dwSectorsPerCluster, &dwBytesPerSector,
&dwNumberOfFreeClusters, &dwTotalNumberOfClusters);
DWORD64 dwFreeSize = (DWORD64)dwNumberOfFreeClusters * dwSectorsPerCluster * dwBytesPerSector;
DWORD64 dwTotalSize = (DWORD64)dwTotalNumberOfClusters * dwSectorsPerCluster * dwBytesPerSector;;
DWORD64 dwUseSize = dwTotalSize - dwFreeSize;
//已用空间
t.Format(L"%.2lfGB", (double)dwUseSize / 1024 / 1024 / 1024);
m_List.SetItemText(i, 3, t);
//可用空间
t.Format(L"%.2lfGb", (double)dwFreeSize / 1024 / 1024 / 1024);
m_List.SetItemText(i, 4, t);
//总空间
t.Format(L"%.2lfGb", (double)dwTotalSize / 1024 / 1024 / 1024);
m_List.SetItemText(i, 5, t);

++i;
p += _tcslen(p) + 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//右击列表产生菜单,先要创建菜单资源
void CMFCApplication2Dlg::OnRclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
*pResult = 0;

CString buff;
buff = m_List.GetItemText(pNMItemActivate->iItem,//行
pNMItemActivate->iSubItem);//列
//保存行号
m_iItem = pNMItemActivate->iItem;

CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pSubMenu = menu.GetSubMenu(0);
CPoint pos;
GetCursorPos(&pos);
pSubMenu->TrackPopupMenu(0, pos.x, pos.y, this);
}
//左击列表获取每行信息
void CMFCApplication2Dlg::OnClickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
*pResult = 0;
// HitTest可以根据鼠标的位置来找到
// 节点的行(是判断鼠标坐标是落在哪
// 一行上)
// 鼠标坐标必须转换成列表控件的客户区坐标
CPoint pos;
GetCursorPos(&pos);
//必须是通过列表控件所绑定的变量来调用ScreenToClient函数
m_List.ScreenToClient(&pos);
int nIndex = m_List.HitTest(pos);
if (nIndex != -1) {
CString buff;
buff = m_List.GetItemText(nIndex, 0);
MessageBox(buff);
}
}
void CMFCApplication2Dlg::OnFileGetpid()//此函数在类向导中的ID_FILE_GETPID里
{
//得到被右键菜单操作的列表节点
CString buff;
buff = m_List.GetItemText(m_iItem, 0);
TCHAR* p = (TCHAR*)m_List.GetItemData(m_iItem);
buff += L"附加数据:";
buff += p;
MessageBox(buff);
}

树控件

  1. 初始化

  2. 消息:和列表控件一样

  3. 操作函数

    InsertItem 插入一项
    DeleteAllItems 删除所有项
    DeleteItem 删除某一项
    SelectItem 选择某一项
    GetItemText 获取某个节点的文本
    GetNextSiblingItem 获取下个兄弟节点
    GetPrevSiblingItem 获取上个兄弟节点
    GetParentItem 获取父节点
    GetRootItem 获取根节点
    GetSelectedItem 获取被选择的项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BOOL CTreeViewDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
HTREEITEM hItem1;
hItem1 = m_Tree.InsertItem(L"AAA",//节点的文本
TVI_ROOT,//父节点
TVI_LAST//兄弟节点间的位置
);
m_Tree.InsertItem(L"aaa", hItem1, TVI_LAST);
m_Tree.InsertItem(L"bbb", hItem1, TVI_LAST);

HTREEITEM hItem2;
hItem2 = m_Tree.InsertItem(L"一一一", TVI_ROOT, TVI_LAST);
m_Tree.InsertItem(L"111", hItem2, TVI_LAST);
m_Tree.InsertItem(L"222", hItem2, TVI_LAST);
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CTreeViewDlg::OnClickTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
*pResult = 0;
//通过鼠标坐标来获取节点的句柄
CPoint pos;
GetCursorPos(&pos);
m_Tree.ScreenToClient(&pos);
HTREEITEM hItem = m_Tree.HitTest(pos);
if (hItem == NULL)
return;
CString buff = m_Tree.GetItemText(hItem);
MessageBox(buff);
//m_Tree.SetItemText(hItem, L"......");
}

并将消息映射 ON_NOTIFY(NM_CLICK, IDC_TREE1, &CTreeViewDlg::OnClickTree1) 中的 NM_CLICK 改为 NM_RCLICK

选项卡控件

选项卡依托于父窗口,选项卡控件是一个窗口,选项卡中的页面也是独立窗口,是选项卡的子窗口

消息反射机制:MFC 独有的机制,让控件能够处理控件发给父窗口的消息

子类化 Tab 控件

  1. 继承 CTabCtr

  2. 添加成员函数:addTab 添加一个选项卡

    选项卡标题,选项卡子窗口

  3. 处理 TCN_SELCHANG 发射消息

    1. 将所有选项卡的子窗口的大小设置成 Tab 控件的客户区大小
    2. 将所有选项卡的子窗口的坐标移动到 Tab 控件的客户区
    3. 将所有的选项卡的子窗口隐藏
    4. 将当前选项卡对应的子窗口显示

先创建一个 MFC 程序,在主对话框资源上拖一个 Table Ctrl,类向导中点击添加 MFC 类,基类选择 CTabCtrl,自己类名为 CMyTabCtrl。为主对话框中的 Table 控件添加控件变量,将变量类型修改为自己定义的 CMyTabCtrl,然后变量名为 m_MyTable。之后在资源视图中添加两个对话框资源,分别设置对话框属性,将 style 设为 child,将 border 设为 NONE,然后为这两个对话框添加类,基类为 CDialogEx,类名分别为 CDiaA 和 CDiaB。

在自己建的类 CMyTableCtrl 中添加函数和变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//在.h文件中添加头文件 #include<vector> 和私有变量 std::vector<CWnd*>m_subWnd;

void CMyTableCtrl::addTab(const CString & title, CWnd * pSubWnd)
{
//添加一个选项卡控件的标题栏
InsertItem(m_subWnd.size(), title);
m_subWnd.push_back(pSubWnd);
pSubWnd->SetParent(this);
//将子窗口大小修改成Tab控件客户区的大小
//并将子窗口坐标移动到客户区左上角
CRect rt;
GetClientRect(rt);
rt.DeflateRect(1, 22, 3, 2);
pSubWnd->MoveWindow(rt);
}
void CMyTableCtrl::OnTcnSelchange(NMHDR *pNMHDR, LRESULT *pResult)
{
*pResult = 0;
//处理TCN_SELCHANG发射消息
CRect rt;
//获取tab控件的大小消息
GetClientRect(rt);
rt.DeflateRect(1, 22, 3, 2);
for (auto& wnd : m_subWnd) {
//将所有选项卡的子窗口大小设置成Tab控件的客户区大小
//将所有选项卡的子窗口坐标移动Tab控件的客户区
wnd->MoveWindow(rt);
//将所有选项卡的子窗口隐藏
wnd->ShowWindow(SW_HIDE);
}
//将所有选项卡对应的子窗口显示
int nIndex = this->GetCurSel();
m_subWnd[nIndex]->ShowWindow(SW_SHOW);
}

使用变参函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void CMyTableCtrl::InitTab(DWORD nCount, ...) {
va_list vl;
va_start(vl, nCount);//变参在nCount之后
for (size_t i = 0; i < nCount; i++) {
//获取一组参数
TCHAR* pName = va_arg(vl, TCHAR*);
CDialogEx* pDlg = va_arg(vl, CDialogEx*);
DWORD dwId = va_arg(vl, DWORD);
//添加选项
InsertItem(i, pName);
//创建窗口
pDlg->Create(dwId, this);
//移动到合适位置覆盖住选项卡
CRect rc;
GetClientRect(rc);
rc.DeflateRect(1, 23, 1, 1);
pDlg->MoveWindow(rc);
//隐藏自己
pDlg->ShowWindow(SW_HIDE);
//添加到vector
m_subWnd.push_back(pDlg);
}
m_subWnd[0]->ShowWindow(SW_SHOW);
va_end(vl);
}

在主对话框中初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL CTabCtrlDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
/*CProcess* pProcDlg = new CProcess;
pProcDlg->Create(IDD_PROCESS);
m_Tab.addTab(L"进程", pProcDlg);
CThread* pThreadDlg = new CThread;
pThreadDlg->Create(IDD_THREAD);
m_Tab.addTab(L"线程", pThreadDlg);*/

m_Tab.InitTab(2,
L"进程", new CProcess(), IDD_PROCESS,
L"线程", new CThread(), IDD_THREAD);

return TRUE;
}