高DPI下控件新葡京平臺搭建位置錯亂問題簡單粗暴的解決方法
反復對比計算不同DPI下的控件大小及位置,發現實在是摸不透WINDOWS 對高DPI下的控件是如何調整其位置及大小的,完全沒有固定的比例,所以壇友的解決方案只能將部件控件的位置予以恢復,大小比原來小了許多,另一些則位置也不正確。 在其它論壇及博客等也查看了些類似的文章,始終是無法解決。感覺有個網友說的很正確:說能完全解決高DPI界面錯亂問題的都是牛鬼蛇神! 問題總得解決,思路還是讓高DPI下控件恢復到默認DPI時位置及大小,即然無法按比例調整,何不我就記錄下控件原有位置! 說幹就幹。第一次,在窗口初始化完成後,我調用一個函數,枚舉所有子窗口,記錄下其位置,生成一個表格,然後保存到文件中,之後再把這個文件加入軟件。以後每次啟動軟件,就按這個表格調整窗口所有控件的大小及位置。界面終於不再錯亂了。 至於字體,按比例調整是沒有問題的,所以字體信息無需記錄。 當然這個解決方案不是完美的,因為控件大小及字體一直是默認DPI下的大小,所以在高DPI下顯得與整個桌面不協調,就是比桌面上其它軟件的字體要小。這個要解決也行,就是把所有控件按自定的比例縮放。不過我嫌麻煩,沒去做。 其次就是,由系統設置的一些窗口沒有調整,比如標題框、彈出的對話框、右鍵菜單等,所以在你的軟件上會有些字大,有些字小。 但至少不錯亂了! 附枚舉窗口及調整字體的代碼:
#ifdef DEBUG_WINDOW_LIST
//控件列表
typedef struct structWindowItem
{
UINT CtrlId;
CHAR szClassName[STRING_SPACE];
RECT Location;
}WNDITEM;
typedef struct structWindowList
{
HWND ParentHwnd;
UINT Count;
WNDITEM Item[WINDOWS_MAX];
}WNDLIST;
WNDLIST strWindowList;
#define STRING_SPACE 256
//
// 枚舉子窗口
//
int ChildWindowList(HWND hwnd)
strWindowList.ParentHwnd = hwnd;
strWindowList.Count = 0;
memset(strWindowList.Item, 0, sizeof(strWindowList.Item));
::EnumChildWindows(hwnd, ChildWindowProcess, NULL);
//將子窗口參數保存到文件
#if 0
CStdioFile cFileList;
if(cFileList.Open(_T("window.list"), CFile::modeCreate|CFile::modeWrite|CFile::shareDenyNone|CFile::typeText))
int nLength;
char szBuffer[STRING_SPACE];
char szClassName[STRING_SPACE];
cFileList.WriteString(_T("const WNDITEM WindowsList[] = \n{\n"));
for(UINT nIndex = 0; nIndex < strWindowList.Count; nIndex++)
{
memset(szBuffer, 0, sizeof(szBuffer));
memset(szClassName, 0x20, sizeof(szBuffer));
nLength = strlen(strWindowList.Item[nIndex].szClassName);
szClassName[0] = _T(‘\"‘);
memcpy(szClassName + 1, strWindowList.Item[nIndex].szClassName, nLength);
szClassName[nLength + 1] = _T(‘\"‘);
szClassName[nLength + 2] = _T(‘,‘);
szClassName[15] = 0;
sprintf_s(szBuffer, _T(" {%4d, %s {%4d, %4d, %4d, %4d}},\n"),
strWindowList.Item[nIndex].CtrlId,
szClassName,
strWindowList.Item[nIndex].Location.left,
strWindowList.Item[nIndex].Location.top,
strWindowList.Item[nIndex].Location.right,
strWindowList.Item[nIndex].Location.bottom);
cFileList.WriteString(szBuffer);
}
cFileList.WriteString(_T("};\n"));
cFileList.Close();
}
#endif
return strWindowList.Count;
}
//
// 枚舉子窗口調用
//
BOOL CALLBACK ChildWindowProcess(HWND hwnd, LPARAM lParam)
{
int nIndex;
if((hwnd != NULL) && (strWindowList.Count < WINDOWS_MAX))
{
nIndex = strWindowList.Count;
//ID
strWindowList.Item[nIndex].CtrlId = ::GetDlgCtrlID(hwnd);
//類名
::GetClassName(hwnd, strWindowList.Item[nIndex].szClassName, STRING_SPACE);
//位置
::GetWindowRect(hwnd, &strWindowList.Item[nIndex].Location);
//轉換為窗口內坐標
CWnd *pWnd = CWnd::FromHandle(strWindowList.ParentHwnd);
pWnd->ScreenToClient(&strWindowList.Item[nIndex].Location);
strWindowList.Count++;
}
return TRUE;
}
#endif
//調整字體
#define DEFAULT_DPI 96.0
BOOL ChildWindowFontRestore(HWND hChildWnd )
{
LOGFONT LgFont;
int DpiY;
HDC hDC = ::GetDC(NULL);
if(hDC != NULL)
{
DpiY= GetDeviceCaps(hDC, LOGPIXELSY);
::ReleaseDC(NULL, hDC);
}
double dbScale = (double)DEFAULT_DPI / DpiY;
//獲取當前字體
HFONT hFont = (HFONT)::SendMessage(hChildWnd, WM_GETFONT, NULL, NULL);
if(hFont != NULL)
{
//獲取字體信息
::GetObject(hFont, sizeof(LOGFONT), &LgFont);
//按比例修改大小
LgFont.lfHeight = LONG( (double)LgFont.lfHeight * dbScale + 0.5f);
//生成物新的字體
hFont = ::CreateFontIndirect(&LgFont);
if(hFont != NULL)
{
//重設字體
SendMessage(hChildWnd, WM_SETFONT, (LPARAM)hFont, TRUE);
}
}
return TRUE;
}
HFONT的保存及釋放過程我沒有寫出來,實際應用中建立的字體句柄要保存,在程序結束時釋放。
還有就是,若程序是在WIN8以上系統下運行,這樣調的結果可能反倒導致界面變亂,貌似WIN8以上系統已解決了這個問題,所以在軟件裏應加上系統檢測,在WIN8以上就交由系統去處理。
高DPI下控件新葡京平臺搭建位置錯亂問題簡單粗暴的解決方法