url:http://dev.firnow.com/course/3_program/c++/cppsl/2008227/101906.html
儀錶控制項,溫度計控制項,LED控制項等是工業組態軟體中最常用的人際互動控制項,能夠提供一種更友好的介面展示方法。而溫度計控制項其實綜合起來就是一個水銀柱繪製和一個刻度的繪製的過程。由於考慮到重新整理可能對介面的產生的影響,我們採用雙緩衝技術實現。
通過VC的ClassWizard建立一個溫度計顯示控制項類,繼承CStatic。我們映射WM_PAINT訊息,在這裡完成溫度計的各種繪製工作就可以了。這個和前面的儀錶控制項繪製原理是一致的。唯一的區別就是在介面展示不同的繪製效果而已。
void CThermoMeter::OnPaint()
{
CPaintDC dc(this);
// 獲得控制項地區
GetClientRect (&m_rectCtrl);
CMemDC memDC(&dc, &m_rectCtrl);
//繪製儀錶盤
if (m_dcMeterPlate.GetSafeHdc() == NULL || (m_bitmapMeterPlate.m_hObject == NULL))
{
m_dcMeterPlate.CreateCompatibleDC(&dc);
m_bitmapMeterPlate.CreateCompatibleBitmap(&dc, m_rectCtrl.Width(), m_rectCtrl.Height()) ;
m_pbitmapOldMeterPlate = m_dcMeterPlate.SelectObject(&m_bitmapMeterPlate) ;
DrawMeterBackground(&m_dcMeterPlate, m_rectCtrl);
}
memDC.BitBlt(0, 0, m_rectCtrl.Width(), m_rectCtrl.Height(),
&m_dcMeterPlate, 0, 0, SRCCOPY);
DrawScale(&memDC);
m_ctrlUnit.Draw(&memDC);
}
由於在雙緩衝繪製過程中,我們的背景可以認為是不變的,所以在程式啟動並執行資料重新整理過程中,我們可以在初始化的時候在記憶體中間完成這部分的繪製工作,然後調用的時候將他Bitbtn到介面就可以了。重新整理資料的時候每次調用以前繪製的成果BitBtn(貼)到介面上,這樣就可以省掉了重新整理的時候給人的閃爍的感覺了。然後完成水銀柱的繪製以及即時值的繪製。介面繪製為這個控制項的核心部分了。
//繪製儀錶背景
void CThermoMeter::DrawMeterBackground(CDC *pDC, CRect &rect)
{
CString strScale;
CFont fScaleFont, *pOldFont;
CPen penThick, penThin, penShadow, penScale, *pOldPen;
CBrush m_brushBack, pBackBrush, *pOldBrush;
pDC->SetBkColor(m_BackColor);
m_brushBack.CreateSolidBrush(m_BackColor);
pOldBrush = (CBrush *)pDC->SelectObject(&m_brushBack);
pDC->FillRect(rect, &m_brushBack); //繪製背景
pDC->SelectObject(pOldBrush);
m_brushBack.DeleteObject();
//繪製邊框的立體效果
penThick.CreatePen(PS_SOLID, 1, RGB(172, 168, 153));
penThin.CreatePen(PS_SOLID, 1, RGB(113, 111, 110));
penShadow.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
pOldPen = (CPen *)pDC->SelectObject(&penShadow);
pDC->MoveTo(rect.left, rect.top);
pDC->LineTo(rect.right - 1, rect.top);
pDC->MoveTo(rect.left + 1, rect.top + 1);
pDC->LineTo(rect.right - 2, rect.top + 1);
pDC->MoveTo(rect.left, rect.top);
pDC->LineTo(rect.left, rect.bottom - 1);
pDC->MoveTo(rect.left + 1, rect.top + 1);
pDC->LineTo(rect.left + 1, rect.bottom - 2);
pDC->SelectObject(&penThick);
pDC->MoveTo(rect.left + 1, rect.bottom - 1);
pDC->LineTo(rect.right - 1, rect.bottom - 1);
pDC->LineTo(rect.right - 1, rect.top + 1);
pDC->SelectObject(&penThin);
pDC->MoveTo(rect.left, rect.bottom);
pDC->LineTo(rect.right, rect.bottom);
pDC->LineTo(rect.right, rect.top);
pDC->SelectObject(pOldPen);
//繪製指標顯示地區
CRect rectScale;
rectScale.SetRect(rect.left + rect.Width() / 4,
rect.top + rect.Height() / 12,
rect.left + rect.Width() / 4 + rect.Width() / 10,
rect.bottom - rect.Height() / 12);
pBackBrush.CreateSolidBrush(RGB(128, 0, 0));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
pDC->FillRect(&rectScale, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
//繪製指標地區立體效果
pDC->SelectObject(&penThick);
pDC->MoveTo(rectScale.left, rectScale.bottom);
pDC->LineTo(rectScale.left, rectScale.top);
pDC->LineTo(rectScale.right, rectScale.top);
pDC->SelectObject(&penThin);
pDC->MoveTo(rectScale.left + 1, rectScale.bottom);
pDC->LineTo(rectScale.left + 1, rectScale.top + 1);
pDC->LineTo(rectScale.right, rectScale.top + 1);
pDC->SelectObject(&penShadow);
pDC->MoveTo(rectScale.right - 1, rectScale.top + 1);
pDC->LineTo(rectScale.right - 1, rectScale.bottom - 1);
pDC->LineTo(rectScale.left + 1, rectScale.bottom - 1);
pDC->MoveTo(rectScale.right, rectScale.top);
pDC->LineTo(rectScale.right, rectScale.bottom);
pDC->LineTo(rectScale.left, rectScale.bottom);
m_rectScale = rectScale;
m_rectScale.DeflateRect(2, 2, 2, 2);
//繪製刻度
fScaleFont.CreateFont(rect.Height() / 8, 0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"System");
pOldFont = (CFont *)pDC->SelectObject(&fScaleFont);
penScale.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
for (int i=0; i<m_nTicks+1; i++)
{
CPoint ptStartTick, ptEndTick;
int nTickDisc = rect.Height() * 5 * i / (6 * m_nTicks);
ptStartTick.x = rect.left + rect.Width() * 3 / 7;
ptStartTick.y = rect.bottom - rect.Height() / 12 - nTickDisc;
ptEndTick.x = rect.left + rect.Width() * 3 / 5;
ptEndTick.y = rect.bottom - rect.Height() / 12 - nTickDisc;
pDC->SelectObject(penScale);
pDC->MoveTo(ptStartTick);
pDC->LineTo(ptEndTick);
//繪製立體感覺
CPoint ptShadowStartTick, ptShadowEndTick;
ptShadowStartTick = ptStartTick;
ptShadowEndTick = ptEndTick;
ptShadowStartTick.y--;
ptShadowEndTick.y--;
pDC->SelectObject(penShadow);
pDC->MoveTo(ptShadowStartTick);
pDC->LineTo(ptShadowEndTick);
//繪製子刻度
for (int j=0; j<m_nSubTicks+1; j++)
{
if (i < m_nTicks)
{
CPoint ptSubStartTick, ptSubEndTick;
int nSubTickDisc = (rect.Height() * 5 / (6 * m_nTicks)) * j / m_nSubTicks;
ptSubStartTick.x = ptStartTick.x;
ptSubStartTick.y = ptStartTick.y - nSubTickDisc;
ptSubEndTick.x = ptSubStartTick.x + (ptEndTick.x - ptSubStartTick.x) / 2;
ptSubEndTick.y = ptSubStartTick.y;
pDC->SelectObject(penScale);
pDC->MoveTo(ptSubStartTick);
pDC->LineTo(ptSubEndTick);
CPoint ptShadowSubStartTick, ptShadowSubEndTick;
ptShadowSubStartTick = ptSubStartTick;
ptShadowSubEndTick = ptSubEndTick;
ptShadowSubStartTick.y--;
ptShadowSubEndTick.y--;
pDC->SelectObject(penShadow);
pDC->MoveTo(ptShadowSubStartTick);
pDC->LineTo(ptShadowSubEndTick);
}
}
//繪製刻度
CRect ptScale;
pDC->SetBkMode(TRANSPARENT);
strScale.Format("%.0f", (m_dMaxValue - m_dMinValue) * i / m_nTicks);
CSize size = pDC->GetTextExtent(strScale);
pDC->SetTextColor(RGB(255, 255, 255));
ptScale.SetRect(rect.left + rect.Width() * 2 / 3,
ptStartTick.y - size.cy / 2,
rect.right,
ptStartTick.y + size.cy / 2);
pDC->DrawText(strScale, ptScale, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
pDC->SetTextColor(RGB(0, 0, 0));
ptScale.DeflateRect(-1, -1, 1, 1);
pDC->DrawText(strScale, ptScale, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldFont);
penThick.DeleteObject();
penThin.DeleteObject();
penShadow.DeleteObject();
m_brushBack.DeleteObject();
pBackBrush.DeleteObject();
fScaleFont.DeleteObject();
penScale.DeleteObject();
}
下面是溫度計水銀柱的繪製。
void CThermoMeter::DrawScale(CDC *pDC)
{
if (m_dCurrentValue > m_dMaxValue)
{
m_dCurrentValue = m_dMaxValue;
}
else if (m_dCurrentValue < m_dMinValue)
{
m_dCurrentValue = m_dMinValue;
}
CRect rectScale;
double yScale = (m_dCurrentValue - m_dMinValue) / (m_dMaxValue - m_dMinValue);
rectScale.SetRect(m_rectScale.left,
m_rectScale.bottom - int(yScale * (m_rectScale.Height())),
m_rectScale.right,
m_rectScale.bottom);
CBrush pBackBrush, *pOldBrush;
pBackBrush.CreateSolidBrush(RGB(255, 0, 0));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
pDC->FillRect(&rectScale, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
m_ctrlUnit.SetRect(GetUnitRect());
}