2.1.2 字體設置工具欄
在字體設置工具欄中也有一個靜態文本框和二個組合框,一個是字體大小的組合框,它可以用普通的CComboBox實現。字體名稱選擇組合框,如果考慮簡單一些的做法也可以用CComboBox,這種方法只是需要枚舉出系統的字體名稱就可以了。爲了使界面更加美觀和方便使用,我們這裏用了CComboBox的派生類,建立了一個更美觀、完善的字體選擇組合框,先看看效果:
圖2-2
這個組合框與普通的CComboBox有兩個區別:一個是下拉列表的寬度不是固定的,和本身的控件不一樣;另一個是每個字體名稱前有不同的圖標了,有的沒有圖標。下面將詳細介紹如何實現這個組合框。
首先我們需要定義一個工具類,用來保存系統字體信息,這個工具類包含有字體的名稱、字體類型、字體圖像索引。
class CFontInfo
{
public:
CFontInfo(){}
~CFontInfo(){}
public:
int GetImage() const { return m_nImage; }
void SetImage(int nImage) { m_nImage = nImage; }
CString GetFontName() const { return m_szName ; }
void SetFontName(CString str) { m_szName = str; }
int GetFontType() const { return m_nFontType; }
void SetFontType(int Type) { m_nFontType = Type; }
private:
CString m_szName;
int m_nFontType;
int m_nImage;
};
我先給出字體組合框的頭文件內容,然後介紹每個函數的作用:
class CFontComboBox : public CComboBox
{
public:
CFontComboBox();
virtual ~CFontComboBox();
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
void FillFontList();
static int CALLBACK EnumFontProc(ENUMLOGFONTEX *lpelfe,
NEWTEXTMETRICEX *lpntme,
DWORD FontType,
LPARAM lParam);
static bool CompareFontName(const CFontInfo *pInfo1, const CFontInfo *pInfo2);
HBITMAP m_FontBmp;
protected:
afx_msg void OnDropdown();
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
public:
std::vector<CFontInfo *> m_pFontVec;
};
CFontComboBox頭文件裏先用vector保存了一個字體工具類的列表,表示所有字體的信息,這裏採用vector是爲了方便用戶選擇某個字體後可以根據索引進行快速的隨機訪問,而list是不支持隨機訪問的。
FillFontList函數是枚舉出系統的字體並添加到組合框。枚舉的時候需要定義回調函數,也就是我們在頭文件看到的EnumFontProc靜態函數。在枚舉出所有的字體後,我們再根據字體名稱進行排序,排序直接使用標準程序庫的sort算法,因爲排序的對象是CFontInfo,所以需要我們定義一個排序的比較函數:CompareFontName。函數的參數就是兩個CFontInfo對象。然後把排序後的字體名稱用AddString方法加入到組合框。在字體組合框名稱前面還有字體圖片,這個圖片是系統保存的,以ID=38保存在COMDLG32.DLL中,我們只需要加載到這個圖片,然後根據字體信息中圖片類型就可以畫出來。
void CFontComboBox::FillFontList()
{
//根據桌面DC的屬性枚舉系統的字體信息
CDC* pDesktopDC = GetDesktopWindow()->GetWindowDC(); //取DC
HDC hdc = pDesktopDC->GetSafeHdc();
LOGFONT lf;
::ZeroMemory(&lf, sizeof(lf));
lf.lfCharSet = DEFAULT_CHARSET;
::EnumFontFamilies(hdc,NULL, (FONTENUMPROC)EnumFontProc,(LPARAM)this);
GetDesktopWindow()->ReleaseDC(pDesktopDC); //釋放DC
//運用STL算法進行自定義的對象排序
std::sort(m_pFontVec.begin(), m_pFontVec.end(), CFontComboBox::CompareFontName);
InitStorage(300, LF_FACESIZE);
//加入字體名稱到組合框
for(int N=0; N<m_pFontVec.size(); ++N)
AddString(m_pFontVec[N]->GetFontName());
//加載COMDLG32.DLL
HMODULE hModule = ::LoadLibraryEx(_T("COMDLG32.DLL"), NULL,
DONT_RESOLVE_DLL_REFERENCES);
ASSERT (hModule != NULL);
//加載成功後從這個DLL中加載字體圖片
m_FontBmp = (HBITMAP)::LoadImage(hModule, MAKEINTRESOURCE(38),
IMAGE_BITMAP, 100, 24, LR_DEFAULTCOLOR);
ASSERT(m_FontBmp != NULL);
::FreeLibrary(hModule);
}
也許你已經注意到了加載DLL的LoadLibraryEx函數,沒有直接用LoadLibrary。因爲它不能設置DONT_RESOLVE_DLL_REFERENCES參數,這個參數有什麼意義呢。根據DLL的結構,加載DLL時會調用DLL的DllMain進行初始化,釋放的時候會調用DllMain進行釋放工作。而DONT_RESOLVE_DLL_REFERENCES參數就是不允許調用DllMain函數,我們這裏只是需要DLL中的一張圖片不需要任何的其他東西,所以傳遞這個參數能加快DLL的加載和釋放速度。避免不必要的資源消耗。