www.pudn.com > CHA06.rar > WYMETERCTL.CPP


#include "stdafx.h"
#include "Meter.h"
#include "wyMeterCtl.h"
#include "wyMeterPpg.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CwyMeterCtrl, COleControl)

// 消息映射
BEGIN_MESSAGE_MAP(CwyMeterCtrl, COleControl)
//{{AFX_MSG_MAP(CwyMeterCtrl)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_TIMER()
//}}AFX_MSG_MAP
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
ON_OLEVERB(AFX_IDS_VERB_EDIT, OnEdit)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()

// 调度映射
BEGIN_DISPATCH_MAP(CwyMeterCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CwyMeterCtrl)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "dMaxValue", m_dMaxValue, OnDMaxValueChanged, VT_R8)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "dMinValue", m_dMinValue, OnDMinValueChanged, VT_R8)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "dCurrentValue", m_dCurrentValue, OnDCurrentValueChanged, VT_R8)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "nScaleDecimals", m_nScaleDecimals, OnNScaleDecimalsChanged, VT_I2)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "nValueDecimals", m_nValueDecimals, OnNValueDecimalsChanged, VT_I2)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "colorNeedle", m_colorNeedle, OnColorNeedleChanged, VT_COLOR)
DISP_PROPERTY_NOTIFY(CwyMeterCtrl, "strUnits", m_strUnits, OnStrUnitsChanged, VT_BSTR)
DISP_FUNCTION(CwyMeterCtrl, "SetCurrentValue", SetCurrentValue, VT_EMPTY, VTS_R8)
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CwyMeterCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

// 事件映射
BEGIN_EVENT_MAP(CwyMeterCtrl, COleControl)
//{{AFX_EVENT_MAP(CwyMeterCtrl)
//}}AFX_EVENT_MAP
END_EVENT_MAP()


// 属性页
BEGIN_PROPPAGEIDS(CwyMeterCtrl, 1)
PROPPAGEID(CwyMeterPropPage::guid)
END_PROPPAGEIDS(CwyMeterCtrl)

// 初始化类工厂和 GUID
IMPLEMENT_OLECREATE_EX(CwyMeterCtrl, "METER.wyMeterCtrl.1",
0x15491a65, 0x5b63, 0x11d5, 0xad, 0xd3, 0, 0x10, 0x88, 0xab, 0x5d, 0x33)

// 类型库 ID 和 版本
IMPLEMENT_OLETYPELIB(CwyMeterCtrl, _tlid, _wVerMajor, _wVerMinor)

// 接口 IDs
const IID BASED_CODE IID_DwyMeter =
{ 0x15491a63, 0x5b63, 0x11d5, { 0xad, 0xd3, 0, 0x10, 0x88, 0xab, 0x5d, 0x33 } };
const IID BASED_CODE IID_DwyMeterEvents =
{ 0x15491a64, 0x5b63, 0x11d5, { 0xad, 0xd3, 0, 0x10, 0x88, 0xab, 0x5d, 0x33 } };

// 控件类型信息
static const DWORD BASED_CODE _dwwyMeterOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CwyMeterCtrl, IDS_WYMETER, _dwwyMeterOleMisc)

// 增加和删除系统注册记录
BOOL CwyMeterCtrl::CwyMeterCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// 注册控件

if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_WYMETER,
IDB_WYMETER,
afxRegInsertable | afxRegApartmentThreading,
_dwwyMeterOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}

//控件构造函数
CwyMeterCtrl::CwyMeterCtrl()
{
InitializeIIDs(&amt;IID_DwyMeter, &amt;IID_DwyMeterEvents);
//设定控件的初始大小
SetInitialSize( 90, 90 );
}

//控件析构函数
CwyMeterCtrl::~CwyMeterCtrl()
{
}

//控件绘制函数
void CwyMeterCtrl::OnDraw(
CDC* pdc, const CRect&amt; rcBounds, const CRect&amt; rcInvalid)
{
// DoSuperclassPaint(pdc, rcBounds);

CBrush brush(TranslateColor(GetBackColor()));
pdc->FillRect(rcBounds,&amt;brush);

DrawMeterBackground(pdc, rcBounds) ;
DrawNeedle(pdc) ;
DrawValue(pdc) ;
}

// 持久性支持
void CwyMeterCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);

PX_Color(pPX,"colorNeedle",m_colorNeedle,RGB(255,0,0));
PX_String(pPX,"strUnits" ,m_strUnits,"v");
PX_Short(pPX,"nScaleDecimals" ,m_nScaleDecimals,0);
PX_Short(pPX,"nValueDecimals" ,m_nValueDecimals,1);

PX_Double(pPX,"dMaxValue" ,m_dMaxValue,10.0);
PX_Double(pPX,"dMinValue" ,m_dMinValue,0.0);
PX_Double(pPX,"dCurrentValue" ,m_dCurrentValue,0.0);

}

// 恢复初始状态
void CwyMeterCtrl::OnResetState()
{
COleControl::OnResetState(); // Resets defaults found in DoPropExchange
}

//显示About对话框
void CwyMeterCtrl::AboutBox()
{
CDialog dlgAbout(IDD_ABOUTBOX_WYMETER);
dlgAbout.DoModal();
}

// 设置窗口初始参数
BOOL CwyMeterCtrl::PreCreateWindow(CREATESTRUCT&amt; cs)
{
cs.lpszClass = _T("STATIC");
return COleControl::PreCreateWindow(cs);
}


// 说明是否使用基类控件
BOOL CwyMeterCtrl::IsSubclassedControl()
{
return TRUE;
}


//处理命令消息
LRESULT CwyMeterCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32
WORD wNotifyCode = HIWORD(wParam);
#else
WORD wNotifyCode = HIWORD(lParam);
#endif
return 0;
}

//绘制指针
void CwyMeterCtrl::DrawNeedle(CDC *pDC)
{
int nResult ;

double dAngleRad ;
double dTemp ;
CBrush brushFill, *pBrushOld ;
CPen penDraw, *pPenOld ;
CPoint pointNeedle[3] ;

//这个函数绘制三角指针.基本指针是一个水平线,它通过弧线的中点
//绘制在仪表上,指尖所在的角度通过当前值和标尺计算得到.
//指针由三点构成的多边形组成,三角形绘制在仪表的区域内.

// 计算指针的第一个和最后一个点
pointNeedle[0].x = m_nBottomCX + m_nBottomRadius/20 ;
pointNeedle[0].y = m_nBottomCY ;
pointNeedle[2].x = m_nBottomCX - m_nBottomRadius/20 ;
pointNeedle[2].y = m_nBottomCY ;

// 计算指尖的角度
dAngleRad = (m_dCurrentValue-m_dMinValue)*(m_dRightAngleRad-m_dLeftAngleRad)/
(m_dMaxValue-m_dMinValue) + m_dLeftAngleRad ;

// 如果角度超出仪表,使用最大或最小角度
dAngleRad = max(dAngleRad, m_dRightAngleRad) ;
dAngleRad = min(dAngleRad, m_dLeftAngleRad) ;

// 计算指尖的 X 坐标
dTemp = m_nBottomCX + m_nTopRadius*cos(dAngleRad) ;
pointNeedle[1].x = ROUND(dTemp) ;

// 计算指尖的 Y 坐标
dTemp = m_nBottomCY - m_nTopRadius*sin(dAngleRad) ;
pointNeedle[1].y = ROUND(dTemp) ;

// 基于仪表选择剪贴区域
pDC->SelectClipRgn(&amt;m_rgnBoundary) ;

// 按照指针颜色创建画笔和刷子
brushFill.CreateSolidBrush(m_colorNeedle) ;
penDraw.CreatePen(PS_SOLID, 1, m_colorNeedle) ;

// 选择画笔和刷子
pPenOld = pDC->SelectObject(&amt;penDraw) ;
pBrushOld = pDC->SelectObject(&amt;brushFill) ;

// 绘制指针
pDC->Polygon(pointNeedle, 3) ;

// 恢复剪贴区域
nResult = pDC->SelectClipRgn(NULL) ;

// 恢复画笔和刷子
pDC->SelectObject(pPenOld) ;
pDC->SelectObject(pBrushOld) ;
}

//输出数值
void CwyMeterCtrl::DrawValue(CDC *pDC)
{
CFont *pFontOld ;
CString strTemp ;

// 选取字体,字体根据背景大小设定
pFontOld = pDC->SelectObject(&amt;m_fontValue) ;

// 设定字体背景色和文本色
pDC->SetTextColor(m_colorText) ;
pDC->SetBkColor(m_colorButton) ;

// 输出文本,文本与中点和基线对齐
pDC->SetTextAlign(TA_CENTER|TA_BASELINE) ;
strTemp.Format(">.*f", m_nValueDecimals, m_dCurrentValue) ;
pDC->TextOut(m_nValueCenter, m_nValueBaseline, strTemp) ;

// 恢复原背景色和字体
pDC->SetBkColor(m_colorWindow) ;
pDC->SelectObject(pFontOld) ;

}

//绘制背景
void CwyMeterCtrl::DrawMeterBackground(CDC *pDC, const CRect &amt;rect)
{
int i, nAngleDeg, nRef ;
int nHeight ;
int nHalfPoints ;
int nStartAngleDeg, nEndAngleDeg ;
int nTickDeg ;
int nAngleIncrementDeg ;
double dTemp, dAngleRad, dX, dY ;
double dRadPerDeg ;
CString strTemp ;
CPoint pointRecess[BOUNDARY_POINTS] ;
CBrush brushFill, *pBrushOld ;
CFont *pFontOld ;
CPen penDraw, *pPenOld ;
TEXTMETRIC tm ;

// 计算仪表半径的中点
m_nBottomCX = rect.right-5;
m_nBottomCY = rect.bottom -5;

// 计算仪表半径
m_nTopRadius = rect.Height()*6/8 ;
m_nBottomRadius = m_nTopRadius/2 ;

// 每度表示的弧度数
dRadPerDeg = 4.0*atan(1.0)/180.0 ;

// 设置仪表表面区域范围
nStartAngleDeg =90; // 60 ;
nEndAngleDeg =180; // 120 ;
nTickDeg = 15 ;

// 弧上绘制点个数
nAngleIncrementDeg = 5 ;

// 角度转换为弧度
m_dLeftAngleRad = nEndAngleDeg*dRadPerDeg ;
m_dRightAngleRad = nStartAngleDeg*dRadPerDeg ;


// 构造仪表表面区域,它是一个多边形区域
nRef = 0 ;
for (nAngleDeg=nStartAngleDeg; nAngleDeg<=nEndAngleDeg; nAngleDeg+=nAngleIncrementDeg)
{
// 计算当前角度的弧度数
dAngleRad = nAngleDeg*dRadPerDeg ;

// 计算 X 坐标
dTemp = m_nBottomCX + m_nTopRadius*cos(dAngleRad) ;
m_pointBoundary[nRef].x = ROUND(dTemp) ;

// 计算 Y 坐标
dTemp = m_nBottomCY - m_nTopRadius*sin(dAngleRad) ;
m_pointBoundary[nRef].y = ROUND(dTemp) ;
nRef++ ;
}
nHalfPoints = nRef ;
// 增加低边弧上的点

for (nAngleDeg=nEndAngleDeg; nAngleDeg>=nStartAngleDeg; nAngleDeg-=nAngleIncrementDeg)
{
dAngleRad = nAngleDeg*dRadPerDeg ;
dTemp = m_nBottomCX + m_nBottomRadius*cos(dAngleRad) ;
m_pointBoundary[nRef].x = ROUND(dTemp) ;
dTemp = m_nBottomCY - m_nBottomRadius*sin(dAngleRad) ;
m_pointBoundary[nRef].y = ROUND(dTemp) ;
nRef++ ;
}

// 构造仪表表面多边形
for (i=0; i<nRef; i++)
{
pointRecess[i].x = m_pointBoundary[i].x ;
pointRecess[i].y = m_pointBoundary[i].y-1 ;
}
pointRecess[0].x = pointRecess[0].x + 1 ;
pointRecess[nRef-1].x = pointRecess[nRef-1].x - 1 ;
for (i=nHalfPoints; i<nRef; i++)
{
pointRecess[i].x = m_pointBoundary[i].x ;
pointRecess[i].y = m_pointBoundary[i].y+1 ;
}
pointRecess[nHalfPoints].x = pointRecess[nHalfPoints].x - 1 ;
pointRecess[nRef-1].x = pointRecess[nRef-1].x + 1 ;

// 建立仪表表面区域
m_rgnBoundary.DeleteObject() ;
m_rgnBoundary.CreatePolygonRgn(m_pointBoundary, nRef, ALTERNATE) ;

// 定位数值输出矩形
m_rectValue.left = rect.left + 5 ;
m_rectValue.right = rect.right - 3*rect.Width()/5 ;
m_rectValue.bottom = rect.bottom - 8*rect.Height()/11 ;
m_rectValue.top = rect.top +5;

// 获取有关系统颜色
m_colorWindow = GetSysColor(COLOR_WINDOW) ;
m_colorButton = GetSysColor(COLOR_BTNFACE) ;
m_colorShadow = GetSysColor(COLOR_BTNSHADOW) ;
m_colorHighlight = GetSysColor(COLOR_BTNHIGHLIGHT) ;
m_colorText = GetSysColor(COLOR_BTNTEXT) ;

// 用按钮色填充背景
brushFill.DeleteObject() ;
brushFill.CreateSolidBrush(m_colorButton) ;
pBrushOld = pDC->SelectObject(&amt;brushFill) ;
pDC->Rectangle(rect) ;
pDC->SelectObject(pBrushOld) ;

// 绘制仪表凹面.
penDraw.DeleteObject() ;
penDraw.CreatePen(PS_SOLID, 1, m_colorShadow) ;
pPenOld = pDC->SelectObject(&amt;penDraw) ;
pDC->MoveTo(pointRecess[0]) ;
pDC->PolylineTo(pointRecess, nHalfPoints+1) ;
pDC->SelectObject(pPenOld) ;

// 绘制仪表凹面阴影.
penDraw.DeleteObject() ;
penDraw.CreatePen(PS_SOLID, 1, m_colorHighlight) ;
pPenOld = pDC->SelectObject(&amt;penDraw) ;

// 绘制低边弧
pDC->PolylineTo(&amt;pointRecess[nHalfPoints], nHalfPoints) ;
pDC->LineTo(pointRecess[0]) ; // connect it to the top
pDC->SelectObject(pPenOld) ;

// 绘制仪表表面

// 使用文本颜色绘制边框
penDraw.DeleteObject() ;
penDraw.CreatePen(PS_SOLID, 1, m_colorText) ;
pPenOld = pDC->SelectObject(&amt;penDraw) ;

// 使用窗口颜色填充
brushFill.DeleteObject() ;
brushFill.CreateSolidBrush(m_colorWindow) ;
pBrushOld = pDC->SelectObject(&amt;brushFill) ;

// 绘制表面
pDC->Polygon(m_pointBoundary, nRef) ;

// 恢复刷子
pDC->SelectObject(pBrushOld) ;


// 绘制刻度记号.
// 刻度长度比高边下降15>
dTemp = m_nTopRadius - 0.15*(m_nTopRadius-m_nBottomRadius) ;

for (nAngleDeg=90; nAngleDeg>nStartAngleDeg; nAngleDeg-=nTickDeg)
{
dAngleRad = nAngleDeg*dRadPerDeg ;

dX = m_nBottomCX + m_nTopRadius*cos(dAngleRad) ;
dY = m_nBottomCY - m_nTopRadius*sin(dAngleRad) ;
pDC->MoveTo(ROUND(dX), ROUND(dY)) ;

dX = m_nBottomCX + dTemp*cos(dAngleRad) ;
dY = m_nBottomCY - dTemp*sin(dAngleRad) ;
pDC->LineTo(ROUND(dX), ROUND(dY)) ;
}

for (nAngleDeg=90+nTickDeg; nAngleDeg<nEndAngleDeg; nAngleDeg+=nTickDeg)
{
dAngleRad = nAngleDeg*dRadPerDeg ;

dX = m_nBottomCX + m_nTopRadius*cos(dAngleRad) ;
dY = m_nBottomCY - m_nTopRadius*sin(dAngleRad) ;
pDC->MoveTo(ROUND(dX), ROUND(dY)) ;

dX = m_nBottomCX + dTemp*cos(dAngleRad) ;
dY = m_nBottomCY - dTemp*sin(dAngleRad) ;
pDC->LineTo(ROUND(dX), ROUND(dY)) ;
}

// 恢复画笔
pDC->SelectObject(pPenOld) ;

// 绘制数值显示矩形
penDraw.DeleteObject() ;
penDraw.CreatePen(PS_SOLID, 1, m_colorShadow) ;
pPenOld = pDC->SelectObject(&amt;penDraw) ;
pDC->MoveTo(m_rectValue.left, m_rectValue.bottom) ;
pDC->LineTo(m_rectValue.left, m_rectValue.top) ;
pDC->LineTo(m_rectValue.right, m_rectValue.top) ;
pDC->SelectObject(pPenOld) ;

penDraw.DeleteObject() ;
penDraw.CreatePen(PS_SOLID, 1, m_colorHighlight) ;
pPenOld = pDC->SelectObject(&amt;penDraw) ;
pDC->LineTo(m_rectValue.right, m_rectValue.bottom) ;
pDC->LineTo(m_rectValue.left, m_rectValue.bottom) ;
pDC->SelectObject(pPenOld) ;

// 计算字体大小
nHeight = m_rectValue.Height()*85/100 ;
m_fontValue.DeleteObject() ;
m_fontValue.CreateFont (nHeight, 0, 0, 0, 400,
FALSE, FALSE, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH|FF_SWISS, "Arial") ;

pFontOld = pDC->SelectObject(&amt;m_fontValue) ;
pDC->GetTextMetrics(&amt;tm) ;
m_nValueFontHeight = tm.tmHeight ;
m_nValueFontWidth = tm.tmAveCharWidth ;


m_nValueBaseline = m_rectValue.bottom - m_nValueFontHeight/4 ;
m_nValueCenter = m_rectValue.left + m_rectValue.Width()/2 ;

// 输出数值
pDC->SetTextColor(m_colorText) ;
pDC->SetBkColor(m_colorButton) ;

// 输出单位
pDC->SetTextAlign(TA_CENTER|TA_BASELINE) ;
pDC->TextOut(rect.right - 2*m_nValueFontWidth,
rect.bottom - m_nValueFontHeight/2,
m_strUnits) ;

// 输出最小值
pDC->SetTextAlign(TA_LEFT|TA_BASELINE) ;
strTemp.Format(">.*lf", m_nScaleDecimals, m_dMinValue) ;
pDC->TextOut(rect.left+1,rect.bottom-5,strTemp) ;

// 输出最大值
pDC->SetTextAlign(TA_RIGHT|TA_BASELINE) ;
strTemp.Format(">.*lf", m_nScaleDecimals, m_dMaxValue) ;
pDC->TextOut((rect.right+m_pointBoundary[0].x)/2,
rect.top+m_nValueFontHeight-3,
strTemp) ;

// 恢复字体和文字背景色
pDC->SelectObject(pFontOld) ;
pDC->SetBkColor(m_colorWindow) ;

}

//重绘指针
void CwyMeterCtrl::UpdateNeedle(double dValue)
{
m_dCurrentValue = dValue ;
InvalidateControl() ;
}

// 消息处理
void CwyMeterCtrl::OnDMaxValueChanged() //最大值改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnDMinValueChanged() //最小值改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnDCurrentValueChanged() //当前值改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnNScaleDecimalsChanged() //标尺精度改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnNValueDecimalsChanged() //数值精度改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnColorNeedleChanged() //指针颜色改变
{
InvalidateControl();
SetModifiedFlag();
}

void CwyMeterCtrl::OnStrUnitsChanged() //数值单位改变
{
InvalidateControl();
SetModifiedFlag();
}

//串行化
void CwyMeterCtrl::Serialize(CArchive&amt; ar)
{
if (ar.IsStoring())
{ // 保存
ar << m_strUnits;
ar << m_dMaxValue;
ar << m_dMinValue;
ar << m_nScaleDecimals;
ar << m_nValueDecimals;
ar << m_colorNeedle;
}
else
{ // 读取
ar >> m_strUnits;
ar >> m_dMaxValue;
ar >> m_dMinValue;
ar >> m_nScaleDecimals;
ar >> m_nValueDecimals;
ar >> m_colorNeedle;
m_dCurrentValue=0.0;
}
}

//建立窗口时设置定时器
int CwyMeterCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;

SetTimer (1, 1000, NULL) ;

return 0;
}

//关闭窗口时删除定时器
void CwyMeterCtrl::OnDestroy()
{
COleControl::OnDestroy();

KillTimer (1) ;

}

//定时消息处理,用于测试
void CwyMeterCtrl::OnTimer(UINT nIDEvent)
{
//return;
static double dStep = 1.5 ;
static double dValue = 0.0 ;

dValue += dStep ;
if (dValue > m_dMaxValue)
{
dStep = -fabs(dStep) ;
dValue = m_dMaxValue-dStep ;
}
else if (dValue < m_dMinValue)
{
dStep = fabs(dStep) ;
dValue = m_dMinValue+dStep ;
}

UpdateNeedle(dValue) ;

COleControl::OnTimer(nIDEvent);
}

//设定当前值
void CwyMeterCtrl::SetCurrentValue(double val)
{
UpdateNeedle(val) ;//重绘指针
}