高DPI下控件新葡京平台搭建位置错乱问题简单粗暴的解决方法

近日写新葡京平台搭建论坛: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以上就交由系统去处理。

猜你喜欢

转载自blog.51cto.com/13857162/2137844
今日推荐