1. 程式人生 > >高DPI下控件新葡京平臺搭建位置錯亂問題簡單粗暴的解決方法

高DPI下控件新葡京平臺搭建位置錯亂問題簡單粗暴的解決方法

height cst 有效 class win8 搭建論壇 ott back 表格

近日寫新葡京平臺搭建論壇:haozbbs.com Q1446595067 一軟件,遇到了高DPI下界面錯亂的問題,在網上搜索了好幾天,都沒有滿意的解決方法。也下載了一些壇友的解決方案示例,其基本思路是按比例將高DPI下控件的位置及大小恢復為默認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下控件新葡京平臺搭建位置錯亂問題簡單粗暴的解決方法