MFC 進行介面設計與程式設計
阿新 • • 發佈:2019-02-07
總體思路
由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檢測錯誤的依賴庫。並更換。