1. 程式人生 > >MFC 進行介面設計與程式設計

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控制元件每一行顯示不同的顏色

雙緩衝區解決閃爍

//載入各個控制元件圖片,拖動會重刷
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 遮蔽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"執行緒已經停止" );   
}  

常見錯誤

Error Cxxx

1.Error C1003: error count exceeds 100
A:在自定義原始檔中要加入#include “stdafx.h”,且要加在首行。

應用程式無法正常啟動0xc000007b解決方法

問題原因
依賴的庫檔案錯誤,需要排查更換。

解決方法
使用depends.exe檢測錯誤的依賴庫。並更換。

參考