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());
}