MFC常用控件的使用

概括

物质 : 通过ID将代码与控件绑定,通过DoDataExchange实现数据交换

运动 : 重写消息控件实现交互,注意重写虚函数

pch.h

Pre-Compiled Header

在编译程序时,如果使用了pch.h,编译器会优先处理这个文件,并将它编译成一个中间文件。在编译其它源文件时,编译器会直接使用这个中间文件,而不再重新编译pch.h。这样可以减少编译时间,因为这些常用的头文件不需要每次都重新编译

MFC Microsoft Foundation Classes

驱动,后台用Win32,设计界面的用MFC

MVC model view controller 模型(doc,存数据),视图,控制

对于控件(图标)以ID来标识,每个页面由一个类来管理

实现机制

封装了Win32

执行阶段

构造类
create 将窗口与类绑定
vie 选择ShowWindow/DoModal  文本/对话框 (可否切换)
销毁窗口
析构类

消息和控件事件

建立键鼠消息等的映射机制,更加方便

类视图->属性->消息,就可以添加对应消息的程序了

IMPLEMENT_DYNCREATE(CPaintView, CView)

BEGIN_MESSAGE_MAP(CPaintView, CView)

//标准消息
ON_WM_MOUSEMOVE()
ON_WM_CHAR()
//命令消息,控件->响应事件
//通告消息,通知事件的发生,即按键的先后顺序,按下一个后另一个接受通知激活
ON_COMMAND(ID_LINE, &CPaintView::OnLine)
//用户自定义消息
    
END_MESSAGE_MAP()

MFC 类层次结构图 | Microsoft Learn

数据序列化 : 将数据扁平化 json(JavaScript Object Notation,轻量级数据传输格式)

1️⃣线性排列的数组,2️⃣数据如{x:100,y:200},根据实际需要弄成文本3️⃣直接复制内存

CObject

  1. 支持序列化 CArchive

  2. 提供运行时的类信息 _GetBaseClass(),GetThieClass()

​ 因为类在编译好后一般不包含类信息

  1. 支持动态创建以及对象诊断输出 如子窗口

AssertVaild() Dump()

关键类

流程

C是Class的意思

CWinapp main函数->thread(创建app后初始化线程)

CDocManager 用来维护一系列文档模板

CDocTemplate 拥有下面三个成员变量 类型为CRUNTIMECLASS

InitInstance->我们的代码

CDocument : 单文档/多文档/对话框 文档模板,负责储存数据

SDI SingleDocumentInterface MDI multiple Dlg dialog

CFrameWnd : 窗口框架 划分区域 菜单栏,工具栏,文件目录等

CView : 负责展示数据,把框架具体化

InitInstance 的部分功能

注 Init只是画图与绑定,运行是WinMain中最后的 nReturnCode = pThread->Run();

// 唯一的 CPaintApp 对象
CPaintApp theApp;

// CPaintApp 初始化

BOOL CPaintApp::InitInstance()
{
    
    
    //初始化自己
	CWinAppEx::InitInstance();
	//初始化socket
	if (!AfxSocketInit())
	{
    
    
		AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
		return FALSE;
	}

	// 初始化 OLE 库 activeX
	if (!AfxOleInit())
	{
    
    
		AfxMessageBox(IDP_OLE_INIT_FAILED);
		return FALSE;
	}
	//各种manager,afx是一个工作室的名字
	AfxEnableControlContainer();
	//解决win7的任务栏bug
	EnableTaskbarInteraction(FALSE);

	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));//注册表
	LoadStdProfileSettings(16);  // 加载标准 INI 文件选项(包括 MRU)

	InitContextMenuManager();

	InitKeyboardManager();

	InitTooltipManager();
	CMFCToolTipInfo ttParams;
	ttParams.m_bVislManagerTheme = TRUE;
	theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
		RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);

	// 注册应用程序的文档模板。  文档模板
	// 将用作文档、框架窗口和视图之间的连接
	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CPaintDoc),
		RUNTIME_CLASS(CMainFrame),    
		RUNTIME_CLASS(CPaintView));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);


	// 分析标准 shell 命令、DDE、打开文件操作的命令行
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// 启用“DDE 执行”
	EnableShellOpen();
	RegisterShellFileTypes(TRUE);


	// 调度在命令行中指定的命令。  如果
	// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	// 唯一的一个窗口已初始化,因此显示它并对其进行更新
	m_pMainWnd->ShowWindow(SW_SHOW);
	m_pMainWnd->UpdateWindow();
	// 仅当具有后缀时才调用 DragAcceptFiles
	//  在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
	// 启用拖/放
	m_pMainWnd->DragAcceptFiles();
	return TRUE;
}

ID绑定

CView层

添加响应函数和变量时,也是类的操作

.h中定义, .cpp中进行绑定,对变量利用初始化列表,函数就自己双击控件定义

在xxxdlg.cpp中

与响应函数绑定

void CRunBTNDlg::DoDataExchange(CDataExchange* pDX)
{
    
    
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BTN1, BTN1);
	DDX_Control(pDX, IDC_BTN2, BTN2);
}

与变量绑定

BEGIN_MESSAGE_MAP(CRunBTNDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN1, &CRunBTNDlg::OnBnClickedBtn1)
	ON_BN_CLICKED(IDC_BTN2, &CRunBTNDlg::OnBnClickedBtn2)
END_MESSAGE_MAP()

变量的数据更新

UpdateData();//将编辑框的数据获取到变量
UpdateData(FALSE);//将变量的数据更新到编辑框

基于文档

线和画刷

利用DC device context 设备环境,定义了一系列关于绘图的东西

.h

protected:
	//用于绘制线条起点,当前,终点,以及按下后的状态
	CPoint m_start;
	CPoint m_cur;
	CPoint m_stop;
	BOOL m_status;

.cpp

void CPaintView::OnDraw(CDC* pDC)//重绘时调用
{
    
    
	CPaintDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	//更换后记得还原,否则在类析构后越界访问
	CPen pen(PS_DOT,1,RGB(0,255,0));//只有1的时候能看出来
	CPen* pOldPen = pDC->SelectObject(&pen);
	CBrush brush(RGB(255, 0, 0));
	CBrush* pOldBrush = pDC->SelectObject(&brush);
	if (m_status) {
    
    
		pDC->FillRect(CRect(m_start, m_cur), pOldBrush);
		//pDC->MoveTo(m_start);
		//pDC->LineTo(m_cur);
	}
	else {
    
    
		pDC->FillRect(CRect(m_start, m_stop), &brush);
		//pDC->MoveTo(m_start);
		//pDC->LineTo(m_stop);
	}
	pDC->SelectObject(pOldPen);
	pDC->SelectObject(pOldBrush);
}


//下面三个是捕获消息的

void CPaintView::OnLButtonDown(UINT nFlags, CPoint point)
{
    
    
	m_start = point;
	m_status = TRUE;
	CView::OnLButtonDown(nFlags, point); //消息传递
}


void CPaintView::OnLButtonUp(UINT nFlags, CPoint point)
{
    
    
	m_stop = point;
	InvalidateRect(NULL);
	m_status = FALSE;
	CView::OnLButtonUp(nFlags, point);
}


void CPaintView::OnMouseMove(UINT nFlags, CPoint point)
{
    
    
	if (m_status) {
    
    
		InvalidateRect(NULL);
		m_cur = point;
	}
	CView::OnMouseMove(nFlags, point);
}

文本和光标

OnDraw部分

	//解决TextOut只能显示单行文本
	CString sub = _T("");
	int y=0;
	for (int i = 0; i < m_strText.GetLength(); i++)
	{
    
    
		if (m_strText.GetAt(i) == '\r'|| m_strText.GetAt(i) == '\n')//'\n'来处理复制粘贴
		{
    
    
			pDC->TextOut(0, y, sub);
			CSize sz = pDC->GetTextExtent(sub);
			sub.Empty();
			y += sz.cy+3;//获取字符宽度,并加上行间距
			continue;
		}
		sub += m_strText.GetAt(i);
	}
	if (sub.IsEmpty() == false) 
		pDC->TextOut(0, y, sub);

	CSize sz = pDC->GetTextExtent(sub);//获取扩展信息
	//CPoint pt;
	//pt.y = y;
	//pt.x = sz.cx;
	SetCaretPos(CPoint(sz.cx+2,y));
	//::SetCaretPos(sz.cx+2,y);//系统api

消息捕获部分

int CPaintView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    
    
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	CClientDC dc(this);
	TEXTMETRIC tm;//text metric度量
	dc.GetTextMetrics(&tm);
	CreateSolidCaret(3, tm.tmHeight);
	ShowCaret();
	return 0;
}


void CPaintView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    
    
	CClientDC dc(this);
	m_strText += (TCHAR)nChar;
	InvalidateRect(NULL);
	CView::OnChar(nChar, nRepCnt, nFlags);
}

菜单栏

资源视图->Menu->IDR_MAINFRAME

描述文字+快捷键 画图(&P) Alt+P 单独 新建(&N)\tCtrl+N ctrl+N组合

右键添加相应 选择C*View类列表

重载函数的响应优先级 按类View>Doc>Frame>App

调试技巧 输出日志 TRACE("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);

工具栏

资源视图->Toolbar->IDR_MAINFRAME(_256)两个

删除 : 按住不动,拖到外面

ID可以与菜单栏一样,就会触发同样的函数

基于对话框

显示窗口

ChtdMFCdllApp theApp;
ChtdMFCdllApp* pTheApp;	
CWndMain* m_WndMain;

m_WndMain = new CWndMain();
m_WndMain->Create(IDD_MAIN);
m_WndMain->ShowWindow(true);

拖控件,写响应事件

按住ctrl进行批量操作,对齐时以最后一个为准(黑体边框)

AfxMessageBox

 AfxMessageBox(L"afx", MB_OK | MB_ICONINFORMATION, 0);

MessageBox

MessageBox(_T("Ok/Cancel"), _T("标题"),MB_CANCELTRYCONTINUE);
MessageBoxA(0,"hello world","hello",0);
//MB_CANCELTRYCONTINUE
//MB_ICONERROR弹出错误

TRACE

不支持宽字节,进行转化

CString temp = m_list.GetItemText(i, j);
char text[256];
memset(text, 0, sizeof(text));
size_t total;
wcstombs_s(&total, text, sizeof(text), temp, temp.GetLength());
TRACE("%s\n", text);

新建窗口

窗口都是通过建立一个类来管理,各种函数通过重写来弄(初始化,映射)

创建子窗口 : 添加类(从Dialog继承)

资源视图->Dialog->添加资源

模态和非模态窗口的创建

动态创建的好处 : 在使用时再创建,不提前占用资源

当创建时间太长时,可以在开始的时候开一个线程默默创建

#include "CSonWnd.h"

CSonWnd dlg;
void CDlgDlg::OnBnClickedSonWnd()
{
    
    
	//CSonWnd dlg;
	//dlg.DoModal();
    //模态对话框,子窗口关闭后才能继续操作父窗口,代码会卡在这里

	if(dlg.m_hWnd==NULL)
		dlg.Create(IDD_DIG_EDY, this);
	dlg.ShowWindow(SW_SHOW);//SW_HIDE
	//非模态,因为不阻塞,要保证创建后不消失,就需要在全局进行声明
}

窗口大小

按钮

protected:
	 CButton m_button;

void CSonWnd::OnBnClickedOk()
{
    
    
	if (m_button.m_hWnd == NULL)
		m_button.Create(_T("dynamic"), BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD, CRect(100, 100, 200, 150), this, 9999);
}

编辑框

多行显示: 行为->multiline,want return, 外观->vertical scroll 这三个设成true

格式化输入: 外观->小写/大写/数字

事件: 输入改变检查(代码基本的语法),屏蔽关键词(脏话)

  • 读取
	CWnd* pEdit01 = GetDlgItem(IDC_EDIT_ONE);

	pEdit01->SetWindowText(_T("100"));
	SetDlgItemText(IDC_EDIT_ONE, _T("200"));

	CString strText;
	pEdit01->GetWindowText(strText);
	GetDlgItemText(IDC_EDIT_ONE, strText);

重写控件 ,逃跑按钮

Cbutton的Zorder更高

思路 mousemove和按钮位置后进行调整,

失败 因为按钮Zorder大于主窗口,会捕获mousemove,但是又不会处理,所以需要重载类来响应

CRect curRect;
GetWindowRect(curRect);
curRect.bottom; curRect.top;curRect.Width()

类向导->添加类(右上角)->MFC类->父类为CButton(用来替换原本为CButton类的)

然后添加成员函数mousemove用来响应

void BTN::OnMouseMove(UINT nFlags, CPoint point)
{
    
    
	ShowWindow(SW_HIDE);
	Other->ShowWindow(SW_SHOW);
    
	CButton::OnMouseMove(nFlags, point);
}

属性页

Dlg->button->sheet->page

相关控件

  • 单选框(radio)

ctrl+D查看顺序(默认是创建顺序,Tab顺序),可以通过点击来改变

单选框第一个需要设置成group属性

  • 复选框(checked)

在添加变量时可以添加数组,而不是一个一个添加

memset(m_skill, 0, sizeof(m_skill));

  • 列表框(list ) 类视图->重写OnInitDialog

属性中把排序可以关掉,因为对中文排序规则有点奇怪

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

	// TODO:  在此添加额外的初始化
    //两种添加方式
	CListBox* pListBox = (CListBox*)GetDlgItem(IDC_LIST1);
	if (pListBox) {
    
    
		pListBox->AddString(_T("ALI"));
		pListBox->AddString(_T("HUAWEI"));
		pListBox->AddString(_T("JD"));
         m_com.AddString(_T("APPLE"));
	}


	return TRUE; 
}
  • 下拉框 (Combo) 属性->行为->Data->以分号隔开

多页面切换

创建页面

资源视图,新建对话框

添加MFC类用来管理页面,继承自CPropertyPage

类名应该与页面名相同IDD_PAGE_01应该用PAGE_01

切换页面

通过创建CPropertySheet的继承类来实现切换

.h

#include "PROP_01.h"
#include "PROP_02.h"
public:
	PROP_01 m_prop1;
	PROP_02 m_prop2;

.cpp

有两个构造函数,都要添加

AddPage(&m_prop1);
AddPage(&m_prop2);
  • 跳转到多页面切换

其实都可以做成数组形式,代码就更简洁

void CRunBTNDlg::OnBnClickedQueryBtn()
{
    
    
	CPropSheet dlg(_T("调查"), this);
	dlg.SetWizardMode();//wizard 向导 下一步上一步
	if (ID_WIZFINISH == dlg.DoModal())
	{
    
    //创建成功后,将数据返回到父窗口
        
		CString strMsg = _T("your lang: ");
        //单选
		switch (dlg.m_prop1.m_lang)
		{
    
    
		case 0:strMsg += "C++";break;
         case 1:strMsg += "Java";break;
		case 2:strMsg += "Python";break;
		}
        //多选
        CString strSkill[4]={
    
    _T("数据结构"),_T("计算机组成原理"),_T("操作系统"),_T("计算机网络")}
        for(int i=0;i<4;i++) 
            if(dlg.m_prop1.m_skill[i])
                strMsg += strSkill[i] + _T(",");
		MessageBox(strMsg);
	}
}
  • sheet相关按钮自定义

重写每一页类的OnSetActive()

BOOL PROP_01::OnSetActive()
{
    
    
    //第一页只需要next    //PSWIZB_BACK | PSWIZB_FINISH 用于中间页和最后一页
	((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_NEXT);
	//去掉帮助按钮
    ((CPropertySheet*)GetParent())->GetDlgItem(IDHELP)->ShowWindow(SW_HIDE);
	return CPropertyPage::OnSetActive();
}
  • 检查是否选择
LRESULT PROP_01::OnWizardNext()//wizard向导,巫师
{
    
    
	UpdateData(TRUE);
    //TRUE表示将控件的值传到变量 FALSE相反
    //检查单选框
	if (m_lang == -1){
    
    
		MessageBox(_T("请选择开发语言"));
		return -1;
	}
    //检查复选框
    bool skillchoose = false;
    for(auto x : skill)
		skillchoose |= x;
    if(!skillchoose){
    
    
         MessageBox(_T("请选择技能"));
		return -1;
    }
    //检查列表框
    if (m_company == ""){
    
    
         MessageBox(_T("请选择公司"));
		return -1;
    }
	return CPropertyPage::OnWizardNext();
}

其他常用控件

列表框

当设置为多选时,添加控件和响应函数

void PROP_01::OnBnClickedOk()
{
    
    
	CString strText;
	int total = m_com.GetSelCount();
	if (total == 0) {
    
    MessageBox(_T("没有选择公司"));return;}
	else {
    
    
		int* index = new int[total];
		m_com.GetSelItems(total, index);
		CString strTemp;
		for (int i = 0; i < total; i++)
		{
    
    
			m_com.GetText(index[i], strTemp);
			strText += strTemp + _T(" ");
		}
		delete[] index;
		MessageBox(strText);
	}
}

下拉框

添加项 : 属性->行为->Data->以分号隔开

是编辑框和列表框的部分集合

m_simple.GetCurSel();
GetLBText();//LB ListBox

进度条

添加控件,利用定时器来定时获取后台数据,循环的i,j映射到(0,100)

int m_pro_pos;//全局的进度,用来存放i,j的映射

//初始化
{
    
    //m_progress为控件名,设置范围和定时器
m_progress.SetRange(0, 100);//默认范围为int,可设置为SetRange32
SetTimer(99, 500, NULL);//(ID,触发间隔,NULL),可以设多个定时器,触发间隔不低于30帧,误差小
}

void PROP_01::OnTimer(UINT_PTR nIDEvent)
{
    
    //添加消息中的timer
	if (nIDEvent == 99)
		m_progress.SetPos(m_pro_pos);
	CPropertyPage::OnTimer(nIDEvent);
    if(m_pro_pos==100)KillTimer(99);
    /*if (nIDEvent == 99)//进度条自增
	{
		m_progress.SetPos(m_pro_pos);
		if (m_pro_pos < 100)m_pro_pos++;
	}*/
    //int lower,upper
    //m_progress.SetRange(lower, upper);
}

marquee 选取框 循环的进度,表示加载中,设为True后,初始化时填下面代码

m_progress.SetMarquee(TRUE, 50);

报表控件

对窗口大小和报表样式,都是先获得CRECT或DWORD,设置后再赋值回去

mTab.GetClientRect(&rect);
Pages[i]->MoveWindow(&rect);

DWORD extStyle = ExeList.GetExtendedStyle();
ExeList.SetExtendedStyle(extStyle);

list control 类似任务管理器那种

外观->always show selection/->true 视图->report 单选/多选

行为->排序/none

添加控件 CListCtrl m_list;后,在初始化时

	//背景色和文字背景
	m_list.SetBkColor(RGB(0,0,255));
	m_list.SetTextBkColor(RGB(0,0,255));
	//整体风格
	DWORD extStyle = m_list.GetExtendedStyle();
	//LVS  --  List View Style
	extStyle |= LVS_EX_FULLROWSELECT;//选择一行
	extStyle |= LVS_EX_GRIDLINES;//网格背景
	extStyle |= LVS_EX_CHECKBOXES;//勾选
	m_list.SetExtendedStyle(extStyle);
	//设置列
	//list view coloumn format
	m_list.InsertColumn(0, _T("序号"), LVCFMT_LEFT, 50);
	m_list.InsertColumn(1, _T("IP"), LVCFMT_LEFT, 150);
	m_list.InsertColumn(2, _T("ID"), LVCFMT_LEFT, 100);
	//添加行
	m_list.InsertItem(0, _T("0"));//(新建一行
	m_list.SetItemText(0, 1, _T("192.168.0.1"));//(插入某行的第几个
	m_list.SetItemText(0, 2, _T("2e4a4f"));

获取数据

	int LineCount = m_list.GetItemCount();
	CHeaderCtrl* pHeader = m_list.GetHeaderCtrl();
	int ColumnCount = pHeader->GetItemCount();
	for (int i = 0; i < LineCount; i++) 
	{
    
    
		//获取选中状态
		if (m_list.GetCheck(i)) {
    
    TRACE("选中");}
		else TRACE("未选中");
		for (int j = 0; j < ColumnCount; j++)
		{
    
    
			CString temp = m_list.GetItemText(i, j);
			MessageBox(temp);
		}
	}

获取双击状态

void CWndINJ::OnNMDblclkList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    
    //获取选中状态  NM : notification message  HDR : handler
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	*pResult = 0;
	//只有下面一句,前面是自动生成的
	int index = pNMItemActivate->iItem;
}

树形控件

如左边的文件树

public:
	CTreeCtrl m_tree;
	CImageList m_icons;

//创建图像列表来管理图像
m_icons.Create(IDB_TREE, 32, 2, 0);
m_tree.SetImageList(&m_icons, TVSIL_NORMAL);

//文件的内容,句柄用以关联子结点
HTREEITEM hRoot = m_tree.InsertItem(_T("root"),0,1);
HTREEITEM hLeaf1 = m_tree.InsertItem(_T("leaf"),1,1,hRoot);

Tab control

属性->动态布局->调整大小类型-> 两者

然后添加每个tab对话框,注意将边框设为None,外观->样式->child

创建子窗口,因为有很多子窗口,就把该过程封装成函数

bool CGameHackerDlg::InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _PageName)
{
    
    
	if (CurPage >= MAX_PAGE)return false;
	else
	{
    
    

		//用数组的方式显示窗口
		Pages[CurPage] = wnd;
		Pages[CurPage]->Create(IDD_WND);
		Pages[CurPage]->SetParent(this);
		//PageINJ.Create(IDD_PAGE_1, this);//有问题
		Pages[CurPage]->ShowWindow(false);

		//更改窗口的位置,防止切换页面时子窗口覆盖,(⊙﹏⊙)
		CRect rect;
		mTab.GetClientRect(&rect);
		rect.top += 48;
		rect.left += 6;
		Pages[CurPage]->MoveWindow(&rect);

		mTab.InsertItem(CurPage, _PageName);
		CurPage++;
		return true;
	}
}

获取消息 mTab.GetCurSel()

void CGameHackerDlg::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
    
    
	*pResult = 0;
	int n = mTab.GetCurSel();
	for (int i = 0; i < CurPage; i++)
		Pages[i]->ShowWindow(i == n);
}

猜你喜欢

转载自blog.csdn.net/killsime/article/details/135133755