You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows DPI变更后CComboBox自定义控件下拉项不显示问题求助

自定义可变高度自绘组合框高DPI下显示异常问题

我基于CComboBox实现了带有CBS_OWNERDRAWVARIABLE样式的自定义组合框:

  • DPI为96(系统「缩放与布局」设为100%)时,下拉项显示正常
  • DPI大于96(缩放比例超过100%,如125%)时,下拉项无法显示
  • 将样式改为CBS_OWNERDRAWFIXED后,所有场景下均显示正常

显示效果:

  • 缩放100%正常效果:scale percent = 100
  • 缩放125%异常效果:scale percent = 125

以下是DrawItem和MeasureItem的代码片段:

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
   if (lpMeasureItemStruct->itemID != -1)
   {
     lpMeasureItemStruct->itemHeight = 16; 
   }
}

void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
   ASSERT(lpDrawItemStruct->CtlType == ODT_COMBOBOX);
   LPCTSTR lpszText = (LPCTSTR)lpDrawItemStruct->itemData;
   ASSERT(lpszText != NULL);
   CDC dc;

   dc.Attach(lpDrawItemStruct->hDC);

   // Save these value to restore them when done drawing.
   COLORREF crOldTextColor = dc.GetTextColor();
   COLORREF crOldBkColor = dc.GetBkColor();

   // If this item is selected, set the background color
   // and the text color to appropriate values. Erase
   // the rect by filling it with the background color.
   if ((lpDrawItemStruct->itemAction & ODA_SELECT) &&
       (lpDrawItemStruct->itemState & ODS_SELECTED))
   {
      dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
      dc.SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
      dc.FillSolidRect(&lpDrawItemStruct->rcItem, ::GetSysColor(COLOR_HIGHLIGHT));
   }
   else
   {
      dc.FillSolidRect(&lpDrawItemStruct->rcItem, crOldBkColor);
   }

   // Draw the text.
   dc.DrawText(
       lpszText,
       (int)_tcslen(lpszText),
       &lpDrawItemStruct->rcItem,
       DT_CENTER | DT_SINGLELINE | DT_VCENTER);

   // Reset the background color and the text color back to their
   // original values.
   dc.SetTextColor(crOldTextColor);
   dc.SetBkColor(crOldBkColor);

   dc.Detach();
}

问题原因

核心问题是MeasureItem中硬编码的itemHeight未做DPI缩放适配。在高DPI环境下,系统会对控件的布局坐标进行缩放,但固定的16像素高度没有按当前DPI比例放大,导致下拉项实际显示高度被压缩到几乎不可见(比如125%缩放时,16px逻辑像素对应12.8px物理像素,可能被系统布局裁剪)。而CBS_OWNERDRAWFIXED样式下,系统会自动处理高度的DPI缩放,因此显示正常。

解决方案

方案一:对itemHeight做DPI缩放适配

在MeasureItem中根据当前窗口的DPI值,计算缩放后的项高度:

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    if (lpMeasureItemStruct->itemID != -1)
    {
        // 获取当前窗口的DPI(适用于Per-Monitor DPI感知程序)
        UINT dpi = GetDpiForWindow(m_hWnd);
        // 以96为基准DPI,计算缩放后的高度
        int scaledHeight = MulDiv(16, dpi, 96);
        lpMeasureItemStruct->itemHeight = scaledHeight;
    }
}

如果程序是系统DPI感知(而非Per-Monitor),可通过屏幕DC获取DPI:

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    if (lpMeasureItemStruct->itemID != -1)
    {
        CDC dc;
        dc.Attach(::GetDC(NULL));
        UINT dpiY = dc.GetDeviceCaps(LOGPIXELSY);
        dc.Detach();
        
        int scaledHeight = MulDiv(16, dpiY, 96);
        lpMeasureItemStruct->itemHeight = scaledHeight;
    }
}

方案二:开启Per-Monitor DPI感知

在应用程序清单文件中添加Per-Monitor V2 DPI感知配置,确保系统能正确向控件传递当前显示器的DPI信息:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
    </windowsSettings>
</application>

内容的提问来源于stack exchange,提问作者latosvarun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 08:11:03