打算实现一个自己的C++程序,具体思路是:
不应用MFC提供的框架;
一个界面对应一个配置文件;
将界面中的所有控件信息写在配置文件中,通过读取配置文件即可知道界面中所有控件的信息,这样的好处是可以动态的修改界面而不需要修改程序;
配置文件的信息:位置信息,控件类型,控件文本,控件ID,控件主键;
提供界面基类,界面的基类完成创建界面控件。
我现在还不会用C++读取配置文件,所以现在配置文件用文本格式。
1:首先写一个存储控件信息的结构体:
{
int nLeft; // 左
int nRight; // 右
int nTop; // 上
int nBottom; // 下
int nID; // 控件ID
char * chType; // 控件类型
char * chText; // 控件文本
char * chKey; // 控件主键
};
由此可知:配置文件的格式:
主键=左;右;上;下;控件ID;控件类型;控件文本
例如:
ID_BTN_START=1;40;1;20;1;button;六子的按钮
2:在写类之前,先设置stdafx.h:
#define STDAFX_H_
#include < afxwin.h >
#include < afxext.h >
#include < afxdisp.h >
#include < afxdtctl.h >
#include < afxcmn.h >
#include < windows.h >
#endif
3:接着写一个读取配置信息的类:
class MFCINi
{
public :
MFCINi();
virtual ~ MFCINi();
public :
/* *------------------------------------------------------------------------------------
* 名 称: <Open>
* 说 明: <打开配置文件>
* 参 数: <MFCINi &ini>:<配置信息对象>
* <const CString &strPath>:<配置文件路径>
* 返 回: <true:打开成功;false:打开失败>
*--------------------------------------------------------------------------------------- */
bool Open(MFCINi & ini, const CString & strPath);
/* *------------------------------------------------------------------------------------
* 名 称: <Close>
* 说 明: <关闭文件,释放占用的内存>
* 参 数:
* 返 回:
*--------------------------------------------------------------------------------------- */
void Close();
/* *------------------------------------------------------------------------------------
* 名 称: <Read>
* 说 明: <根据配置键,取得配置项>
* 参 数: <char* item>:<配置键>
* 返 回: <读出的配置信息>
*--------------------------------------------------------------------------------------- */
char * Read( char * item);
/* *------------------------------------------------------------------------------------
* 名 称: <GetAppPath>
* 说 明: <取得应用程序的路径(bug路径)>
* 参 数: <CString &strAppPath>:<存储取得的路径的字符串>
* 返 回: <配置项的值>
*--------------------------------------------------------------------------------------- */
static void GetAppPath(CString & strAppPath);
char ** m_chItems; // INi文件配置项
char ** m_chItemValues; // 项目值
int m_nItemCount; // INI文件总配置项数
private :
FILE * m_pf; // INI文件指针
};
在读取配置文件的类中,提供一个读取当前程序目录的函数GetAppPath。
实现代码:
#include " MFCINi.h "
/ /
// Construction/Destruction
/ /
// 取得应用程序的路径
void MFCINi::GetAppPath(CString & strAppPath)
{
GetModuleFileName(NULL, strAppPath.GetBuffer(MAX_PATH), MAX_PATH);
strAppPath.ReleaseBuffer();
int nLength = 0 ;
char ch = NULL;
ch = ' / ' ;
nLength = strAppPath.GetLength();
strAppPath = strAppPath.Left(nLength - 1 );
nLength = strAppPath.ReverseFind(ch);
strAppPath = strAppPath.Left(nLength + 1 );
}
MFCINi::MFCINi()
{
m_nItemCount = 0 ;
m_chItems = NULL;
m_chItemValues = NULL;
m_pf = NULL;
}
MFCINi:: ~ MFCINi()
{
Close();
}
// 打开文件
bool MFCINi::Open(MFCINi & ini, const CString & strPath)
{
CString strIniPath;
CString strAppPath;
MFCINi::GetAppPath(strAppPath);
strIniPath.Format( " %s%s " , strAppPath, strPath);
m_pf = fopen(strIniPath, " a+ " );
if (m_pf == NULL)
{
return false ;
}
m_chItemValues = ( char ** )malloc(ITEM_COUNT * sizeof ( char ** ));
m_chItems = ( char ** )malloc(ITEM_COUNT * sizeof ( char ** ));
for ( int i = 0 ; i < ITEM_COUNT; i ++ )
{
m_chItemValues[i] = NULL;
m_chItems[i] = NULL;
}
char chReading[ 500 ];
char * pchSeparator = NULL;
while (m_nItemCount < ITEM_COUNT)
{
int nEOF = fscanf(m_pf, " %s " , chReading);
if (nEOF == EOF)
{
// 读取成功
return true ;
}
pchSeparator = strstr(chReading, " = " );
int nCharCountName = pchSeparator - chReading;
if (nCharCountName < 0 )
{
continue ;
}
int nCharCountValue = strlen(chReading) - nCharCountName - 1 ;
chReading[nCharCountName] = 0 ;
// 复制数据
m_chItems[m_nItemCount] = ( char * )malloc(nCharCountName + 1 );
strcpy(m_chItems[m_nItemCount], chReading);
m_chItemValues[m_nItemCount] = ( char * )malloc(nCharCountValue + 1 );
strcpy(m_chItemValues[m_nItemCount], chReading + nCharCountName + 1 );
m_nItemCount ++ ;
}
return true ;
}
// 读取配置项
char * MFCINi::Read( char * nItem)
{
if (m_nItemCount == 0 )
{
return NULL;
}
int i = 0 ;
while (strcmp(m_chItems[i], nItem) != 0 )
{
i ++ ;
if (i == m_nItemCount)
{
return NULL;
}
}
return m_chItemValues[i];
}
void MFCINi::Close()
{
if (m_pf != NULL)
{
fclose(m_pf);
m_pf = NULL;
}
for ( int i = 0 ; i < m_nItemCount; i ++ )
{
// 释放第i个配置项内存
if (m_chItems[i] != NULL)
{
free(m_chItems[i]);
m_chItems[i] = NULL;
}
// 释放第i个值内存
if (m_chItemValues[i] != NULL)
{
free(m_chItemValues[i]);
m_chItemValues[i] = NULL;
}
}
// 释放配置项内存
if (m_chItems != NULL)
{
free(m_chItems);
m_chItems = NULL;
}
// 释放值内存
if (m_chItemValues != NULL)
{
free(m_chItemValues);
m_chItemValues = NULL;
}
m_nItemCount = 0 ;
}
本来想把GetAppPath函数写成普通函数的,后来考虑到这是个基本函数,所以就把它写成了静态函数。
4:先把程序运行起来吧,写一个主函数,顺便测测配置类好不好用:
在与debug平行的目录中添加一个配置文件“../NaviIniHR.ini”
#include " MFCINi.h "
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CString path = " ../NaviIniHR.ini " ;
MFCINi ini;
ini.Open(ini, path);
char * chValue = ini.Read( " ID_BTN_NAVI_GUIDE " );
AfxMessageBox(chValue);
return 1 ;
}
5:设置控件样式结构体:CreateWndStruct
{
int nWndType; // 窗体类型
DWORD dwstyle; // 窗体样式
DWORD dwExStyle; // 扩展样式
LPCTSTR lpszClass; // 对应类
LPCTSTR lpszName; // 名称
DWORD style; // 样式
HWND hwndParent;
HMENU hMenu;
HINSTANCE hInstance;
LPVOID lpCreateParams;
};
6:然后设置界面基类,基类名称:CSearchBaseDialog:
因为配置文件信息结构体与控件样式结构体只有该基类用,所以把这两个结构体写在基类中。
{
struct ConfigInfo
{
int nLeft; // 左
int nRight; // 右
int nTop; // 上
int nBottom; // 下
int nID; // 控件ID
char * chType; // 控件类型
char * chText; // 控件文本
char * chKey; // 控件主键
};
struct CreateWndStruct
{
int nWndType; // 窗体类型
DWORD dwstyle; // 窗体样式
DWORD dwExStyle; // 扩展样式
LPCTSTR lpszClass; // 对应类
LPCTSTR lpszName; // 名称
DWORD style; // 样式
HWND hwndParent;
HMENU hMenu;
HINSTANCE hInstance;
LPVOID lpCreateParams;
};
public :
CSearchBaseDialog();
virtual ~ CSearchBaseDialog();
private :
/* *------------------------------------------------------------------------------------
* 名 称: <CreateControls>
* 说 明: <根据配置信息,创建界面的所有控件>
* 参 数:
* 返 回:
*--------------------------------------------------------------------------------------- */
void CreateControls();
/* *------------------------------------------------------------------------------------
* 名 称: <CreateControls>
* 说 明: <根据信息,创建界面的控件>
* 参 数: <char *chKey>:<控件的配置主键>
* <char *chValue>:<控件的配置信息>
* 返 回: <创建控件的句柄>
*--------------------------------------------------------------------------------------- */
HWND CreateControl( char * chKey, char * chValue);
/* *------------------------------------------------------------------------------------
* 名 称: <GetCreateWndStyle>
* 说 明: <取得创建控件的样式>
* 参 数: <CreateWndStruct &createWndStruct>:<控件样式>
* <char *chKey>:<控件的配置主键>
* <char *chValue>:<控件的配置信息>
* 返 回:
*--------------------------------------------------------------------------------------- */
void GetCreateWndStyle(CreateWndStruct & createWndStruct, char * chKey, char * chValue);
private :
CString m_strFile; // 配置文件路径
HWND * m_hwndControls; // 窗体中的控件
HWND * m_hWnd; // 当前句柄
};
实现代码:
#include " stdafx.h "
#include " SearchBaseDialog.h "
#include " MFCINi.h "
/ /
// Construction/Destruction
/ /
CSearchBaseDialog::CSearchBaseDialog()
{
* m_hWnd = NULL;
m_strFile = " ../NaviIniHR.ini " ;
}
CSearchBaseDialog:: ~ CSearchBaseDialog()
{
if (m_hwndControls != NULL)
{
delete[] m_hwndControls;
}
}
void CSearchBaseDialog::CreateControls()
{
MFCINi ini;
ini.Open(ini, m_strFile);
m_hwndControls = new HWND[ini.m_nItemCount];
for ( int i = 0 ; i < ini.m_nItemCount; i ++ )
{
m_hwndControls[i] = CreateControl(ini.m_chItems[i], ini.m_chItemValues[i]);
}
}
HWND CSearchBaseDialog::CreateControl( char * chKey, char * chValue)
{
CreateWndStruct createWndStruct;
GetCreateWndStyle(createWndStruct, chKey, chValue);
HWND wnd = CreateWindowEx(
createWndStruct.dwExStyle,
createWndStruct.lpszClass,
createWndStruct.lpszName,
createWndStruct.style,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
createWndStruct.hwndParent,
createWndStruct.hMenu,
createWndStruct.hInstance,
createWndStruct.lpCreateParams);
return wnd;
}
void CSearchBaseDialog::GetCreateWndStyle(CreateWndStruct & createWndStruct, char * chKey, char * chValue)
{
ConfigInfo info = { 0 , 0 , 0 , 0 , 0 , "" , "" , "" };
info.chKey = chKey;
sscanf(chValue, ( " %d;%d;%d;%d;%d;%s;%s " ),
info.nLeft, info.nRight, info.nTop, info.nBottom,
info.nID, info.chType, info.chText);
createWndStruct.dwExStyle = 0 ;
createWndStruct.lpszClass = TEXT(info.chType);
createWndStruct.lpszName = TEXT( "" );
createWndStruct.style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW;
createWndStruct.hwndParent = * m_hWnd;
createWndStruct.hInstance = ::AfxGetResourceHandle();
createWndStruct.hMenu = NULL;
createWndStruct.lpCreateParams = NULL;
createWndStruct.hMenu = (HMENU)info.nID;
if (strcmp(info.chType, " button " ) == 0 )
{
createWndStruct.nWndType = 1 ;
createWndStruct.dwstyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW;
}
else if (strcmp(info.chType, " static " ) == 0 )
{
createWndStruct.nWndType = 3 ;
createWndStruct.dwstyle = WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE | WS_BORDER;
}
else if (strcmp(info.chType, " edit " ) == 0 )
{
createWndStruct.nWndType = 2 ;
createWndStruct.dwstyle = WS_CHILD | WS_VISIBLE | ES_LEFT;
}
else
{
createWndStruct.nWndType = 1 ;
createWndStruct.dwstyle = WS_CHILD | WS_VISIBLE | WS_EX_CLIENTEDGE | WS_BORDER;
}
}
本来是没有GetCreateWndStyle函数的,后来发现CreateControl函数太大了,而且即生成控件样式,又创建控件,弄得这个函数的主题很不明确,所以从CreateControl函数中分出了一个专门设置样式的函数CreateWndStruct。
本来想把ConfigInfo, CreateWndStruct两个结构体放在实现代码里的,后来由于添加了GetCreateWndStyle函数,需要CreateWndStruct做参数类型,所以把它们放到了声明文件中。