MFC 进行界面设计与编程

总体思路

由UI设计界面背景图片、相关按钮图片等,然后在代码中创建关联控件变量。对于无需变化的背景、按钮,可以不设控件变量关联。一般有以下几个步骤:
1.UI设计界面;
2.创建需要变动的控件变量与之关联,并设置透明;
3.加载图片;
4.创建控件相应消息响应函数。

控件开发

系统控件

创建

CStatic ctrl_record_;
CRect rt_record_(614, 196, 794, 376);

//创建控件ID,后续可以根据此ID找到控件GetDlgItem(IDC_INPUT_ID)->Invalidate(TRUE)
//pWnd->GetDlgCtrlID() == IDC_RECORD
#define IDC_RECORD        0x10001

//SS_NOTIFY--能够接收消息
ctrl_record_.Create(NULL, WS_CHILD | SS_CENTER | SS_CENTERIMAGE | WS_VISIBLE | SS_NOTIFY , rt_record_, this, IDC_RECORD);

透明&消息响应

//定义消息响应函数
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);

//消息映射
BEGIN_MESSAGE_MAP(CRiskEstimateDemoDlg, CDialogEx)
	ON_WM_CTLCOLOR()
	ON_STN_CLICKED(IDC_RECORD, &CRiskEstimateDemoDlg::OnRecordClicked)
END_MESSAGE_MAP()

//设置控件透明
HBRUSH CRiskEstimateDemoDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
	// TODO:  在此更改 DC 的任何特性
	CFont font;
	// TODO:  如果默认的不是所需画笔,则返回另一个画笔
	if (nCtlColor == CTLCOLOR_STATIC)
	{

		if (pWnd->GetDlgCtrlID() == IDC_DISPLAY_FACE 
			|| pWnd->GetDlgCtrlID() == IDC_RECORD
			)
		{
			font.CreatePointFont(150, _T("Microsoft YaHei"));
			pDC->SelectObject(&font);
			pDC->SetBkMode(TRANSPARENT); //透明
			pDC->SetTextColor(0xffffff);
			return (HBRUSH)GetStockObject(NULL_BRUSH);
		}
	}
	return hbr;
}

图片加载

//resource.h
#define IDB_PNG_BACKGROUND              110

//*.rc
IDB_PNG_BACKGROUND      PNG                     "res\\bg.png"

//加载各个控件图片,拖动会重刷
BOOL CRiskEstimateDemoDlg::OnEraseBkgnd(CDC* pDC)
{
	CDC *pDc = GetDC();
	//加载背景图
	LoadImageForCtrl(pDc, IDB_PNG_BACKGROUND, rt_background_.left, rt_background_.top);
	if (pDc)
		ReleaseDC(pDc);
	return TRUE;
}

自定义继承控件

自定义控件可以解决频繁刷新整个界面闪烁的问题.

class CMyStatic : public CStatic
{
	DECLARE_DYNAMIC(CMyStatic)

public:
	CMyStatic();
	virtual ~CMyStatic();
	virtual CMyStatic& SetText(const CString& strText);
	virtual CMyStatic& SetMyFont(int nSize,const CString& strFont);
	virtual CMyStatic& SetTextColor(COLORREF crText);


protected:
	afx_msg LRESULT OnSetText(WPARAM,LPARAM);
	afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	DECLARE_MESSAGE_MAP()

public:
	void SetImage(UINT id);


private:
	BOOL GetImageById(CImage *pImage, UINT nResID, LPCTSTR lpTyp);
	BOOL GetImageInfo(CImage *pImage, UINT nResID, LPCTSTR lpTyp);
	void SetByteForImage(CImage &image);
	void Update();

private:
	CBitmap		bitmap_;
	CFont		font_;
	COLORREF	text_color_;
	BOOL		bitmap_used_;
	static image_id_ stat_image_[IMAGE_NUM];
};

ListBox 具有 LBS_OWNERDRAW 样式是没有文字的,除非再添加 LBS_HASSTRINGS 样式,并处理 WM_DRAWITEM 去绘制文字,否则肯定不会显示文字。
参考:让ListBox控件每一行显示不同的颜色

为MFC中的ListBox添加水平滚动条
参考:为MFC中的ListBox添加水平滚动条

双缓冲区解决闪烁

//加载各个控件图片,拖动会重刷
BOOL CRiskEstimateDemoDlg::OnEraseBkgnd(CDC* pDC)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	CDC *pDc = GetDC();
	if (pDc == NULL){
		return FALSE;
	}
	CDC memDC; //首先定义一个显示设备对象
	CBitmap memBitMap;//定义一个位图对象
	CImage image; //背景图片用来定义位图大小
	CBitmap *pOldBmp;

	int width = rt_background_.Width();
	int height = rt_background_.Height();

	memDC.CreateCompatibleDC(pDc);
	//ASSERT(memDC.CreateCompatibleDC(pDc) != NULL);//这时还不能绘图,因为没有地方画
	//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小,也可以自己定义
	memBitMap.CreateCompatibleBitmap(pDc, width, height);
	//ASSERT(memBitMap.CreateCompatibleBitmap(pDc, width, height) != NULL);
	pOldBmp = memDC.SelectObject(&memBitMap);//将位图选入到内存显示设备中
	//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
	ASSERT(pOldBmp != NULL);
	if (pOldBmp == NULL){
		return FALSE;
	}
	//memDC.FillSolidRect(0, 0, image.GetWidth(), image.GetHeight(), RGB(255, 255, 255));

	//绘图
	DrawCtrl(memDC);

	pDc->BitBlt(0, 0, width, height, &memDC, 0, 0, SRCCOPY);
	//绘图完成后的清理,把前面的pOldBit选回来.在删除MemBitmap之前要先从设备中移除它
	memDC.SelectObject(pOldBmp);
	DeleteObject(&memDC);
	memBitMap.DeleteObject();

	if (pDc)
		ReleaseDC(pDc);
	return TRUE;
}

MFC调用Bat文件

	if (flag_record_)
	{
		//system("e:\\Workspaces\\Git\\FKDemo\\FKDemo\\record.bat");
		WinExec("record.bat 001.flv", SW_HIDE);
	}
	else
	{
		WinExec("kill.bat", SW_HIDE);
	}

record.bat


@echo off

ffmpeg -f dshow -rtbufsize 200M -i video="Logitech HD Webcam C310":audio="Âó¿Ë·ç (HD Webcam C310)" -pix_fmt yuv420p -ar 48000 -vcodec libx264 -crf 23 -preset veryslow -x264opts b-adapt=2:bframes=0:aq-strength=1:psy-rd=0.8,0 -vsync vfr -acodec aac -bsf:a aac_adtstoasc -f flv %1
pause
exit

kill.bat

@echo off
taskkill /f /im ffmpeg.exe

常见知识点

文件夹操作

遍历文件1

BrowseCurrentAllFile(CString strDir)
{
     if(strDir == _T("")){
         return;
     }
     else
     {
         if(strDir.Right(1) != _T("//"))
              strDir += L"//";
         strDir =strDir+_T("*.*");
     }
     CFileFind finder;
     CString strPath;
     BOOL bWorking = finder.FindFile(strDir);
     while(bWorking)
     {
         bWorking = finder.FindNextFile();
         strPath = finder.GetFilePath();
         if(finder.IsDirectory() && !finder.IsDots())
              BrowseCurrentAllFile(strPath); //递归调用
         else if(!finder.IsDirectory() && !finder.IsDots())
         {
              //strPaht就是所要获取的文件路径
         }
     }
}
 
//调用方式:
BrowseCurrentAllFile(_T("D://test"));

遍历文件2

BOOL ProcessImage(cv::Mat& image){

	CString strDir(PLOT_DATA_IMG_PATH);
	strDir += "*.jpg";
	CString fileFullName;
	CFileFind finder;
	BOOL bWorking = finder.FindFile(strDir);
	if (!bWorking)
	{
		return FALSE;
	}
	bWorking = finder.FindNextFile();
	fileFullName = finder.GetFilePath();
	string s;
	s = (LPCSTR)(CStringA)(fileFullName);
	image = cv::imread(s);

	DeleteFile(fileFullName); //删除文件
	return TRUE;
}

目标文件是否存在

#include <shlwapi.h>
#pragma comment(lib,"Shlwapi.lib") //如果没有这行,会出现link错误
    if (PathFileExists(strDBPath))
    {
           //存在
    }
    else  CreateDirectory(html_path_out1, NULL);  //文件夹不存在
//Cstring.Replace('/','\\');    //将地址中的'/'替换为'\\'

浏览文件夹目录

	// 设置过滤器     
	TCHAR szFilter[] = _T("启动文件(*.avi)|*.avi|所有文件(*.*)|*.*||");
	// 构造打开文件对话框     
	CFileDialog fileDlg(TRUE, _T("exe"), NULL, 0, szFilter, this);
	if (IDOK == fileDlg.DoModal())
	{
		CString str	= fileDlg.GetPathName();
		replay_video_path_ = (LPCSTR)(CStringA)(str);
		ctrl_list_msg_.AddString(str);
	}
	else {
		return;
	}

MFC接收命令行参数的三种方法

/* 方法一:将获取到 "C:\test\app.exe  -1 -2" */
CString sCmdline = ::GetCommandLine(); 
AfxMessageBox(sCmdline); 
  
/*方法二:将获取到 将依次得到"C:\test\app.exe","-1", "-2"*/
char buff[128] = { 0 }
for (int i = 0; i < __argc; i++) 
{ 
    sprintf_s(buff, 128, "%ws", __targv[6]);
    g_Config.kBusinessEnd = atoi(buff)
} 

/*方法三:将获取到 将获取到 "-1 -2 ",AfxGetApp()->m_lpCmdLine 只包含参数*/
CString sCmdline = AfxGetApp()->m_lpCmdLine; 

DEBUG模式测试如何设置:
菜单的: 项目->属性->配置属性->调试->命令行参数

命令行参数里可以直接写你的参数,例如在命令行是:test.exe per1 per2 , 这样在这里就直接写:per1 per2

MFC获取时间的几种方法

参考:MFC获取时间的几种方法

MFC 屏蔽ESC和ENTER键关闭对话框

VC MFC 屏蔽ESC和ENTER键关闭对话框

方法一:

窗体头文件中加入:

1 protected:
2     virtual BOOL PreTranslateMessage(MSG* pMsg);  // PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的
3 public:
4     virtual void OnOK();
 

在CPP中加入:

复制代码
 1 BOOL CColorDlgDlg::PreTranslateMessage(MSG* pMsg)
 2 {
 3     //屏蔽ESC关闭窗体/
 4     if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE ) return TRUE;
 5     //屏蔽回车关闭窗体,但会导致回车在窗体上失效.
 6     //if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN && pMsg->wParam) return TRUE;
 7     else
 8         return CDialog::PreTranslateMessage(pMsg);
 9 }
10 void CColorDlgDlg::OnOK()
11 {
12     //CDialogEx::OnOK();
13 }
复制代码
 

 

方法二:

窗体头文件中加入:

 

public:
    virtual void OnOK();
    virtual void OnCancel();
    afx_msg void OnClose(); //响应关闭事件!
 

在CPP中加入:

复制代码
void CFirstFZDlg::OnOK()
{
    return;
}
void CFirstFZDlg::OnCancel()
{
    return;
}
void CFirstFZDlg::OnClose()
{
    // TODO:  在此添加消息处理程序代码和/或调用默认值
    
    CDialogEx::OnCancel();
    //CDialogEx::OnClose();
}
复制代码
 

MSG 结构体定义如下:
typedef struct tagMSG {     // msg  
   HWND hwnd;   // 窗口句柄
   UINT message;  // 消息
   WPARAM wParam;  // 消息附加信息,根据消息而定
   LPARAM lParam;  // 消息附加信息,根据消息而定
   DWORD time;  // 消息发送时间
   POINT pt;  // 消息发送时指针的位置(屏幕坐标)
} MSG;

完全退出线程

UINT thread_testexit( PVOID pParam )  
{  
    while( g_bExtiThread )  
    {  
        Sleep(1000);  
        static int i = 0;  
        CString str;str.Format( L"%d",i++);  
        //AfxGetApp()->GetMainWnd()->SetWindowText( str );  
    }  
    return 0;  
}  
void Ctmfc1Dlg::OnBnClickedButton1()  
{  
    // TODO: 在此添加控件通知处理程序代码  
    pWinThreadtestexit = AfxBeginThread( thread_testexit, 0 );  
}  
void Ctmfc1Dlg::OnBnClickedButton2()  
{  
    // TODO: 在此添加控件通知处理程序代码   
    g_bExtiThread = 0;  
    WaitForSingleObject( pWinThreadtestexit->m_hThread, INFINITE );  
    SetWindowText( L"线程已经停止" );   
}  

参考:MFC 线程的退出方法

常见错误

Error Cxxx

1.Error C1003: error count exceeds 100
A:在自定义源文件中要加入#include “stdafx.h”,且要加在首行。

应用程序无法正常启动0xc000007b解决方法

问题原因
依赖的库文件错误,需要排查更换。

解决方法
使用depends.exe检测错误的依赖库。并更换。

参考

1、(极好)VC++判断文件或文件夹是否存在

  • 2
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值