高DPI下控件位置错乱问题简单粗暴的解决方法

   近日写一软件,遇到了高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以上就交由系统去处理。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值