ON_COMMAND(ID_VIEW_HIDETHISTECH, OnViewHidethistech) //日K线图隐藏技术指标的事件
ON_UPDATE_COMMAND_UI(ID_VIEW_HIDETHISTECH, OnUpdateViewHidethistech)//日K线图隐藏技术指标的事件
ON_COMMAND(ID_VIEW_THISTECHPARAM, OnViewThistechparam)//日K线图显示某项技术指标的事件
ON_UPDATE_COMMAND_UI(ID_VIEW_THISTECHPARAM, OnUpdateViewThistechparam)//日K线图显示某项技术指标的事件
//}}AFX_MSG_MAP
第一部分 弹出菜单框
在主程序中,会根据当前视图的类型确定弹出框所包含的菜单项
1.弹出菜单消息
G:\stock\TskingVS2019\src\Client\StkUI\MainFrm.cpp
BEGIN_MESSAGE_MAP(CMainFrame, CTskMainFrame)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_WM_SIZE()
ON_WM_CLOSE()
ON_WM_INITMENUPOPUP() //鼠标右键弹出框菜单
WM_INITMENU 对应File Edit View Help四个
WM_INITMENUPOPUP消息对应Open Save Print, Exit子菜单项
WM_INITMENU是窗口主菜单被鼠标点击或用Alt键激活时产生的,WPARAM是窗口主菜单的HMENU。
WM_INITMENUPOPUP是要弹出某个子菜单的时候产生的,WPARAM是窗口要弹出的子菜单的HMENU,LPARAM的低WORD表示要弹出的子菜单在其父菜单中的索引值,从0开始,LPARAM的高WORD如果是1,表示当前操作的是窗口的SYSMENU,如果是0则不是SYSMENU。
这两个消息与子菜单中的菜单项无关。
G:\stock\TskingVS2019\src\Client\StkUI\MainFrm.h
afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);
参数
hmenuPopup
wParam值.下拉菜单或子菜单的句柄
uPos
lParam低次序字的值.指定一个打开的下拉菜单或子菜单在菜单项中基于0相关联的位置
fSystemMenu
lParam高次序字的值.指定是否下拉菜单是窗体菜单(同样大家知道的系统菜单或控制菜单),如果菜单是窗体菜单,
这个参数是TRUE,否则它是FALSE;
InitMenuPopup(pPopupMenu);
}
CTskMainFrame::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}
第二部分 弹出框菜单的消息响应
弹出框菜单的响应分为两种,对没有子菜单项的,用ON_COMMAND()消息来响应;对有子菜单项的,用ON_COMMAND_RANGE()消息来响应。
响应鼠标右键弹出菜单点击子菜单项消息响应事件
G:\stock\TskingVS2019\src\Client\StkUI\View\GraphView.cpp
菜单ID值是连续的,用ON_COMMAND_RANGE来映射消息处理函数,可以在一个函数中处理一个范围内的所有消息。
BEGIN_MESSAGE_MAP(CGraphView, CView)
//{{AFX_MSG_MAP(CGraphView)
ON_WM_CREATE()
ON_COMMAND(ID_VIEW_HIDETHISTECH, OnViewHidethistech) //日K线图弹出框“隐藏该指标(&H)”的事件
ON_UPDATE_COMMAND_UI(ID_VIEW_HIDETHISTECH, OnUpdateViewHidethistech)//日K线图弹出框“隐藏该指标(&H)”的事件
ON_COMMAND(ID_VIEW_THISTECHPARAM, OnViewThistechparam)//日K线图弹出框“调整该指标参数(&D)...”的事件
ON_UPDATE_COMMAND_UI(ID_VIEW_THISTECHPARAM, OnUpdateViewThistechparam)//日日K线图弹出框“调整该指标参数(&D)...”的事件
ON_COMMAND_RANGE(ID_VIEW_TECH_START, ID_VIEW_TECH_END, OnViewTechRange) //技术指标发生变化
ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_TECH_START, ID_VIEW_TECH_END, OnUpdateViewTechRange)//技术指标发生变化
实现函数
//功能:调用AfxGetProfile().ToggleGraphViewTech(nID)更新m_anGraphTechsShow;数组,实际上是添加或删除要显示或隐藏的指标id。然后重新画图。
G:\stock\TskingVS2019\src\Client\StkUI\View\GraphView.cpp
void CGraphView::OnViewTechRange(UINT nCmdID)
{
if (nCmdID < ID_VIEW_TECH_START || nCmdID > ID_VIEW_TECH_END)
{
ASSERT(FALSE);
return;
}
UINT nID = nCmdID - ID_VIEW_TECH_START;
if (nID > STT_MAX)
nID = nID-STT_MAX-1+STT_USER_MIN;
AfxGetProfile().ToggleGraphViewTech(nID);
CRect rectClient;
GetClientRect(&rectClient);
m_graph.ResetClient(rectClient);
Invalidate();
}
G:\stock\TskingVS2019\src\Client\StkLib\Src\Profile.cpp
//功能:根据子菜单项ID更新m_anGraphTechsShow;数组,实际上是添加或删除要显示或隐藏的指标id
void CStProfile::ToggleGraphViewTech( UINT nTech )
{
SP_ASSERT( (nTech >= STT_MIN && nTech <= STT_MAX) || nTech >= STT_USER_MIN );
if( nTech >= STT_KLINE_MIN && nTech <= STT_KLINE_MAX )
{
BOOL bHas = FALSE;
CSPDWordArray & anKLine = GetGraphTechsKLine( );
for( int k=anKLine.GetSize()-1; k>=0; k-- )
{
if( anKLine[k] == nTech )
{
anKLine.RemoveAt(k);
bHas = TRUE;
}
}
if( !bHas )
anKLine.InsertAt( 0, nTech );
}
else
{
BOOL bHas = FALSE;
CSPDWordArray & anShow = GetGraphTechsShow( );
for( int k=anShow.GetSize()-1; k>=0; k-- )
{
if( anShow[k] == nTech )
{
anShow.RemoveAt(k);
bHas = TRUE;
}
}
if( !bHas )
anShow.Add( nTech );
}
}
叠加图指标类
StkLib\Src\TechKLine.cpp
/////////////////////////////////////////////////////////////////////
// CDJ
// K线叠加图 指标实现类 by freeman
CStock CDJ::m_stockSha;
CStock CDJ::m_stockSzn;
CSPString CDJ::m_strCodeOrg;
CDJ::CDJ( )
{
SetDefaultParameters( );
}
CDJ::CDJ( CKData * pKData )
: CTechnique( pKData )
{
SetDefaultParameters( );
}
CDJ::~CDJ()
{
Clear( );
}
void CDJ::SetDefaultParameters( )
{
m_strCodeSha = STKLIB_CODE_MAIN;
m_strCodeSzn = STKLIB_CODE_MAINSZN;
}
void CDJ::AttachParameters( CDJ & src )
{
m_strCodeSha = src.m_strCodeSha;
m_strCodeSzn = src.m_strCodeSzn;
}
BOOL CDJ::IsValidParameters( )
{
return ( m_strCodeSha.GetLength() > 0 && m_strCodeSzn.GetLength() > 0 );
}
void CDJ::Clear( )
{
CTechnique::Clear( );
}
/***
K线叠加图,准备叠加K线的数据 叠加图 数据准备函数 by freeman
*/
BOOL CDJ::PrepareStockData(CStDatabase * pDatabase, const char * szCodeOrg,
int nCurKType, int nCurKFormat, int nCurMaindataType,
DWORD dwAutoResumeDRBegin, int nAutoResumeDRLimit )
{
SP_ASSERT( pDatabase );
// bReload and kdayMain
BOOL bReload = (NULL!=szCodeOrg && 0!=strncmp(szCodeOrg,m_strCodeOrg,m_strCodeOrg.GetLength()) );
m_strCodeOrg = szCodeOrg;
// m_stockSha
m_stockSha.SetStockCode( CStock::marketSHSE, m_strCodeSha );
AfxPrepareStockData( pDatabase, m_stockSha, nCurKType, nCurKFormat, nCurMaindataType, FALSE, bReload );
// m_stockSzn
m_stockSzn.SetStockCode( CStock::marketSZSE, m_strCodeSzn );
AfxPrepareStockData( pDatabase, m_stockSzn, nCurKType, nCurKFormat, nCurMaindataType, FALSE, bReload );
return TRUE;
}
第三部分 画叠加图
技术指标窗口的绘图方法是和K线窗口的绘图方法是相同的.
1.视图 CGraphView 中的OnDraw函数调用m_graph.Redraw函数来绘制相关的图表
OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw。
OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,没有响应消息的功能.当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创建CPaintDC类的DC对象来响应该消息并调用视图的OnDraw成员函数.OnPaint最后也要调用OnDraw,因此一般在OnDraw函数中进行绘制。
G:\stock\TskingVS2019\src\Client\StkUI\View\GraphView.cpp
/////////////////////////////////////////////////////////////////////////////
// CGraphView drawing
void CGraphView::OnDraw(CDC* pDC)
{
CStaticDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rectClient;
GetClientRect(&rectClient);
CMemDC memDC(pDC);
m_graph.Redraw(&memDC, rectClient); //画图
G:\stock\TskingVS2019\src\Client\StkUI\View\StockGraph.cpp
void CStockGraph::Redraw(CDC* pDC, CRect rectAll)
{
ASSERT(m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()));
if (m_rectAll != rectAll)
ResetClient(rectAll);
DrawStock(pDC);
}
2.Redraw函数调用DrawStock来绘制图表
在DrawStock()函数中,
1.DrawKLine(pDC); 负责画日K线和叠加类的指标。
1.DrawTechLine(pDC); 负责画窗口底部的技术指标线。
//画股票K线 by freeman
void CStockGraph::DrawStock(CDC* pDC)
{
ASSERT(m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()));
if (!m_pParent || !::IsWindow(m_pParent->GetSafeHwnd()))
return;
CClientDC dc(m_pParent);
if (NULL == pDC)
pDC = &dc;
if (!m_CurStock.GetStockInfo().IsValidStock())
return;
CKData& kdata = m_CurStock.GetKData(m_nCurKType);
if (kdata.GetSize() == 0)
{
DrawNoDataInfo(pDC); //股票K线没有数据时,显示获取更多数据请点击菜单“数据->历史数据下载...”。"
}
else if (PrepareDrawKLine()) // Reset m_nIndexStart, m_nIndexEnd, m_nIndexCurrent
{
DrawDateAxis(pDC); //画K线底部的日期提示框里的文字
DrawTechLine(pDC); //画窗口底部的技术指标线
DrawKLine(pDC); //画K线图
DrawKData(); //画周期K线的左上角信息
}
DrawReportRegion(pDC); //画日K线右下角窗口:参数、成本、火焰山、活跃
}
2.1在函数DrawStock中调用DrawTechLine来进行实际技术指标的绘制工作
//画窗口底部的技术指标线 by freeman
void CStockGraph::DrawTechLine(CDC* pDC)
{
CSPDWordArray& anShow = AfxGetProfile().GetGraphTechsShow();
for (int k = 0; k < m_arcTech.GetSize(); k++)
{
CRect rect = m_arcTech.ElementAt(k);
if (k < anShow.GetSize())
{
DrawTechLine(pDC, anShow[k], rect, FALSE);
DrawTechLine(pDC, anShow[k], rect, TRUE);
}
}
}
/画窗口底部的技术指标线 by freeman
void CStockGraph::DrawTechLine(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle)
{
DECLARE_COLOR_DEFINATION
if (!bDrawTitle)
{
pDC->FillSolidRect(&rect, clrBK);
pDC->FillSolidRect(CRect(m_rectAll.left, rect.top, rect.left - 1, rect.bottom), clrBK);
pDC->Draw3dRect(&rect, clrBorder, clrBorder);
}
switch (nTech)
{
case STT_MACD: DrawTechMACD(pDC, rect, bDrawTitle); break;
case STT_MIKE: DrawTechMIKE(pDC, rect, bDrawTitle); break;
case STT_PSY: DrawTechPSY(pDC, rect, bDrawTitle); break;
.
.
.
default:
{
UINT nTechUserCount = CTechUser::GetTechUserCount();
if (nTech >= STT_USER_MIN && nTech <= STT_USER_MIN + nTechUserCount - 1)
DrawTechUser(nTech, pDC, rect, bDrawTitle);
else
ASSERT(FALSE);
}
}
2.2 画日K线和画叠加类的指标。
/画周期K线图 by freeman
void CStockGraph::DrawKLine(CDC* pDC)
{
// KLine Attribute Rect
CRect rectKLineAttrib = m_rectKLineCenter;
rectKLineAttrib.top -= m_nHeightSubtitle;
// Draw STT_DJ and STT_CW
CSPDWordArray& anTech = AfxGetProfile().GetGraphTechsKLine();
for (int k = 0; k < anTech.GetSize(); k++)
if (STT_DJ == anTech[k] || STT_CW == anTech[k]) //画K线时 同时画叠加图
DrawKLineAttribute(pDC, anTech[k], rectKLineAttrib, FALSE, dMin, dMax);
DrawTechDJ2(pDC, rectKLineAttrib, FALSE, dMin, dMax);
// Draw K Line 循环画K线
for (int k = m_nIndexStart; k <= m_nIndexEnd; k++)
{
DrawOneKLine(pDC, k, k, &kdata, dMin, dMax, FALSE);
}
// Draw Tech KLine except STT_DJ and STT_CW
for (int k = 0; k < anTech.GetSize(); k++) //画除叠加图之外的其他叠加技术指标
if (STT_DJ != anTech[k] && STT_CW != anTech[k])
DrawKLineAttribute(pDC, anTech[k], rectKLineAttrib, FALSE, dMin, dMax);
5. 然后根据实际的技术指标类型,调用不同的绘图函数来完成不同的技术指标的绘制
G:\stock\TskingVS2019\src\Client\StkUI\View\StockGraph.cpp
//画周期K线叠加技术指标 如boll线、叠加图等 by freeman
void CStockGraph::DrawKLineAttribute(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle, double dMin, double dMax)
{
switch (nTech)
{
case STT_MA: DrawTechMA(pDC, rect, bDrawTitle, dMin, dMax); break;
case STT_BBI: DrawTechBBI(pDC, rect, bDrawTitle, dMin, dMax); break;
case STT_BOLL: DrawTechBOLL(pDC, rect, bDrawTitle, dMin, dMax); break;
case STT_PV: DrawTechPV(pDC, rect, bDrawTitle, dMin, dMax); break;
case STT_SAR: DrawTechSAR(pDC, rect, bDrawTitle, dMin, dMax); break;
case STT_DJ: DrawTechDJ(pDC, rect, bDrawTitle, dMin, dMax); break; //画叠加图
case STT_CW: DrawTechCW(pDC, rect, bDrawTitle, dMin, dMax); break;
default:;
ASSERT(FALSE);
}
}
3.1 光标移动时间处理
void CStockGraph::OnIndexCurrentChanged()
{
if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd()))
{
CClientDC dc(m_pParent);
CRect rectKLineAttrib = m_rectKLineCenter;
rectKLineAttrib.top -= m_nHeightSubtitle;
CSPDWordArray& anTech = AfxGetProfile().GetGraphTechsKLine();
if (anTech.GetSize() > 0)
DrawKLineAttribute (&dc, anTech[0], rectKLineAttrib, TRUE, 0, 0);
CSPDWordArray& anShow = AfxGetProfile().GetGraphTechsShow();
for (int k = 0; k < m_arcTech.GetSize(); k++)
{
CRect rect = m_arcTech.ElementAt(k);
if (k < anShow.GetSize() && anShow[k] == STT_MIKE)
{
DrawTechLine(&dc, anShow[k], rect, FALSE);
DrawTechLine(&dc, anShow[k], rect, TRUE);
}
else if (k < anShow.GetSize())
{
DrawTechLine(&dc, anShow[k], rect, TRUE);
}
}
DrawReportRegion(&dc);
DrawKData();
}
}
3.在函数DrawStock中调用DrawTechLine来进行实际技术指标的绘制工作
4. 函数
G:\stock\TskingVS2019\src\Client\StkUI\View\StockGraph.cpp
DrawKLineAttribute(CDC* pDC, UINT nTech, CRect rect, BOOL bDrawTitle, double dMin, double dMax)
画周期K线叠加技术指标 如boll线、叠加图等
DrawTechLine(CDC * pDC, UINT nTech, CRect rect, BOOL bDrawTitle )
来完成实际的绘制工作,也就是给定绘图设备pDC,技术指标类型nTech,特定的窗口位置rect,以及标题bDrawTitle等参数.
6. 函数DrawTechMACD( CDC * pDC, CRect rect, BOOL bDrawTitle, int nTech, CMACD * pmacd )
完成实际的技术指标的绘制工作
G:\stock\TskingVS2019\src\Client\StkUI\View\DrawTech.cpp
//功能:画叠加图函数 CStockGraph::DrawTechDJ()
void CStockGraph::DrawTechDJ( CDC * pDC, CRect rect, BOOL bDrawTitle, double dMin, double dMax )
{
CHECK_NODATATODRAW
DECLARE_COLOR_DEFINATION
CKData & kdata = m_CurStock.GetKData(m_nCurKType);
m_techparam.dj.AttachParameters( AfxGetProfile().GetTechParameters().dj );
// Draw Sub Title
if( bDrawTitle )
{
CString strTitle = AfxGetSTTShortName(STT_DJ);
strTitle += " ";
if( m_CurStock.GetStockInfo().IsShangHai() )
strTitle += m_techparam.dj.m_strCodeSha;
else if( m_CurStock.GetStockInfo().IsShenZhen() )
strTitle += m_techparam.dj.m_strCodeSzn;
DrawTechTitle( pDC, rect.left+5, rect.top+1, strTitle, TA_LEFT | TA_TOP, 14, clrBK, clrTitle );
return;
}
// Prepare Data
if( !m_techparam.dj.IsValidParameters() )
m_techparam.dj.SetDefaultParameters( );
if( !m_techparam.dj.PrepareStockData( &AfxGetDB(), m_CurStock.GetStockCode(),
m_nCurKType, m_nCurKFormat, m_nCurMaindataType,
AfxGetProfile().GetAutoResumeDRBegin(), AfxGetProfile().GetAutoResumeDRLimit() ) )
return;
CKData * pKData = NULL;
if( m_CurStock.GetStockInfo().IsShangHai() )
pKData = & ( CDJ::m_stockSha.GetKData(m_nCurKType) );
else if( m_CurStock.GetStockInfo().IsShenZhen() )
pKData = & ( CDJ::m_stockSzn.GetKData(m_nCurKType) );
else
return;
// GetMinMaxInfo
double dMinDJ = 0, dMaxDJ = 0;
CKLine kline( pKData );
int nStartDJ = pKData->GetIndexByDate( kdata.ElementAt(m_nIndexStart).m_date );
int nEndDJ = pKData->GetIndexByDate( kdata.ElementAt(m_nIndexEnd).m_date );
if( -1 == nStartDJ ) nStartDJ = 0;
if( -1 == nEndDJ ) nEndDJ = pKData->GetSize()-1;
if( nStartDJ < 0 || nEndDJ < 0 || !kline.GetMinMaxInfo( nStartDJ, nEndDJ, &dMinDJ, &dMaxDJ ) )
return;
// Draw
for( int nIndex=m_nIndexStart; nIndex<=m_nIndexEnd; nIndex++ )
{
DWORD date = kdata.ElementAt(nIndex).m_date;
int nIndexDJ = pKData->GetIndexByDate( date );
if( nIndexDJ >= 0 && nIndexDJ < pKData->GetSize() )
{
DrawOneKLine( pDC, nIndex, nIndexDJ, pKData, dMinDJ, dMaxDJ, TRUE );
}
}
}