在ui资源文件中,如果包含了combo控件,如下:
<Combo name="cmb_http_proxy" width="183" height="34" font="1" itemfont="1" textpadding="11,0,0,0" normalimage="file='image\btn_set_normal.png' dest='153,2,183,32'" hotimage="file='image\btn_set_hover.png' dest='153,2,183,32'" pushedimage="file='image\btn_set_press.png' dest='153,2,183,32'" itemtextcolor="#FF333333" textcolor="#FF333333" itemselectedtextcolor="#FF333333" itemhottextcolor="#FF333333" bordersize="1" bordercolor="#FFD9D9D9" itemtextpadding="11,7,0,0" itemlinecolor="#FF333333">
<ListLabelElement name="cmb_item_1" text="不使用代理"/>
<ListLabelElement name="cmb_item_2" text="HTTP代理"/>
</Combo>
在窗体创建时,两个下拉选项会在create函数中创建出来,两个ListLabelElement的printmanager从combo控件赋值,combo的printmanager从窗体赋值,则ListLabelElement包含所有窗体所包含的属性,如字体等。
并且在combo属性中指定了itemfont属性,ListLabelElement 的高度就会根据该属性确定的字体大小自动计算,源码中的方法见下:
SIZE CListLabelElementUI::EstimateSize(SIZE szAvailable)
{
if( m_pOwner == NULL ) return CDuiSize(0, 0);
CDuiString sText = GetText();
TListInfoUI* pInfo = m_pOwner->GetListInfo();
SIZE cXY = m_cxyFixed;
if( cXY.cy == 0 && m_pManager != NULL ) {
cXY.cy = m_pManager->GetFontInfo(pInfo->nFont)->tm.tmHeight + 8;
cXY.cy += pInfo->rcTextPadding.top + pInfo->rcTextPadding.bottom;
}
if( cXY.cx == 0 && m_pManager != NULL ) {
RECT rcText = { 0, 0, 9999, cXY.cy };
if( pInfo->bShowHtml ) {
int nLinks = 0;
CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, sText, 0, NULL, NULL, nLinks, DT_SINGLELINE | DT_CALCRECT | pInfo->uTextStyle & ~DT_RIGHT & ~DT_CENTER);
}
else {
CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, sText, 0, pInfo->nFont, DT_SINGLELINE | DT_CALCRECT | pInfo->uTextStyle & ~DT_RIGHT & ~DT_CENTER);
}
cXY.cx = rcText.right - rcText.left + pInfo->rcTextPadding.left + pInfo->rcTextPadding.right;
}
return cXY;
}
以下两句代码就确定了ListLabelElement 的高度
cXY.cy = m_pManager->GetFontInfo(pInfo->nFont)->tm.tmHeight + 8;
cXY.cy += pInfo->rcTextPadding.top + pInfo->rcTextPadding.bottom;
其中GetFontInfo代码见下:
TFontInfo* CPaintManagerUI::GetFontInfo(int id)
{
if (id < 0) return GetDefaultFontInfo();
TCHAR idBuffer[16];
::ZeroMemory(idBuffer, sizeof(idBuffer));
_itot(id, idBuffer, 10);
TFontInfo* pFontInfo = static_cast<TFontInfo*>(m_ResInfo.m_CustomFonts.Find(idBuffer));
if (!pFontInfo) pFontInfo = static_cast<TFontInfo*>(m_SharedResInfo.m_CustomFonts.Find(idBuffer));
if (!pFontInfo) pFontInfo = GetDefaultFontInfo();
if (pFontInfo->tm.tmHeight == 0)
{
HFONT hOldFont = (HFONT) ::SelectObject(m_hDcPaint, pFontInfo->hFont);
::GetTextMetrics(m_hDcPaint, &pFontInfo->tm);
::SelectObject(m_hDcPaint, hOldFont);
}
return pFontInfo;
}
确定控件高度是在创建下拉窗体时调用的,创建下拉窗体的方法见下:
void CComboWnd::Init(CComboUI* pOwner)
{
m_bHitItem = false;
m_pOwner = pOwner;
m_pLayout = NULL;
m_iOldSel = m_pOwner->GetCurSel();
// Position the popup window in absolute space
SIZE szDrop = m_pOwner->GetDropBoxSize();
RECT rcOwner = pOwner->GetPos();
RECT rc = rcOwner;
rc.top = rc.bottom; // 父窗口left、bottom位置作为弹出窗口起点
rc.bottom = rc.top + szDrop.cy; // 计算弹出窗口高度
if( szDrop.cx > 0 ) rc.right = rc.left + szDrop.cx; // 计算弹出窗口宽度
SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };
int cyFixed = 0;
for( int it = 0; it < pOwner->GetCount(); it++ ) {
CControlUI* pControl = static_cast<CControlUI*>(pOwner->GetItemAt(it));
if( !pControl->IsVisible() ) continue;
SIZE sz = pControl->EstimateSize(szAvailable);
cyFixed += sz.cy;
}
cyFixed += 4;
rc.bottom = rc.top + MIN(cyFixed, szDrop.cy);
::MapWindowRect(pOwner->GetManager()->GetPaintWindow(), HWND_DESKTOP, &rc);
MONITORINFO oMonitor = {};
oMonitor.cbSize = sizeof(oMonitor);
::GetMonitorInfo(::MonitorFromWindow(*this, MONITOR_DEFAULTTOPRIMARY), &oMonitor);
CDuiRect rcWork = oMonitor.rcWork;
if( rc.bottom > rcWork.bottom ) {
rc.left = rcOwner.left;
rc.right = rcOwner.right;
if( szDrop.cx > 0 ) rc.right = rc.left + szDrop.cx;
rc.top = rcOwner.top - MIN(cyFixed, szDrop.cy);
rc.bottom = rcOwner.top;
::MapWindowRect(pOwner->GetManager()->GetPaintWindow(), HWND_DESKTOP, &rc);
}
Create(pOwner->GetManager()->GetPaintWindow(), NULL, WS_POPUP, WS_EX_TOOLWINDOW, rc);
// HACK: Don't deselect the parent's caption
HWND hWndParent = m_hWnd;
while( ::GetParent(hWndParent) != NULL ) hWndParent = ::GetParent(hWndParent);
::ShowWindow(m_hWnd, SW_SHOW);
::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L);
}
在创建窗体时,ListLabelElement 中的printmanager与combo中的相同,所有属性也与combo中的属性一致,但是在创建下拉窗体后(代码中Create(pOwner->GetManager()->GetPaintWindow(), NULL, WS_POPUP, WS_EX_TOOLWINDOW, rc);),会调到下拉窗体的初始化方法,在CComboWnd::HandleMessage中,见下:
if( uMsg == WM_CREATE ) {
m_pm.SetForceUseSharedRes(true);
m_pm.Init(m_hWnd);
// The trick is to add the items to the new container. Their owner gets
// reassigned by this operation - which is why it is important to reassign
// the items back to the righfull owner/manager when the window closes.
m_pLayout = new CVerticalLayoutUI;
m_pLayout->SetManager(&m_pm, NULL, true);
LPCTSTR pDefaultAttributes = m_pOwner->GetManager()->GetDefaultAttributeList(_T("VerticalLayout"));
if( pDefaultAttributes ) {
m_pLayout->ApplyAttributeList(pDefaultAttributes);
}
m_pLayout->SetInset(CDuiRect(1, 1, 1, 1));
m_pLayout->SetBkColor(0xFFFFFFFF);
m_pLayout->SetBorderColor(0xFFC6C7D2);
m_pLayout->SetBorderSize(1);
m_pLayout->SetAutoDestroy(false);
m_pLayout->EnableScrollBar();
m_pLayout->ApplyAttributeList(m_pOwner->GetDropBoxAttributeList());
for( int i = 0; i < m_pOwner->GetCount(); i++ ) {
m_pLayout->Add(static_cast<CControlUI*>(m_pOwner->GetItemAt(i)));
}
CShadowUI *pShadow = m_pOwner->GetManager()->GetShadow();
pShadow->CopyShadow(m_pm.GetShadow());
m_pm.GetShadow()->ShowShadow(m_pOwner->IsShowShadow());
m_pm.AttachDialog(m_pLayout);
m_pm.AddNotifier(this);
return 0;
}
其中,在
m_pLayout->Add(static_cast<CControlUI*>(m_pOwner->GetItemAt(i)));
中会将传进来的ListLabelElement 控件的printmanager变量覆盖,此时会丢失掉combo的所有属性,包括字体属性等,在之后的窗体绘制中再次获取ListLabelElement 的高度属性时(前面的CListLabelElementUI::EstimateSize方法),此时的字体属性为空,字体高度返回默认值12,最终ListLabelElement 被绘制的高度与实际期望有所偏差,导致显示结果可能会有如下情形:
解决办法是在设置全局字体属性时,设置字体为共享字体,代码如下:
<Font id="1" name="微软雅黑" size="14" shared="true"/>
shared属性确定该字体为共享字体,此时,在CListLabelElementUI::EstimateSize方法获取字体属性时,在无法获取本地字体属性时,会获取共享字体,然后正确绘制ListLabelElement 的高度,效果如下: