[C++] 函数绘图程序 →→→→→进入此内容的聊天室

来自 , 2019-05-18, 写在 C++, 查看 128 次.
URL http://www.code666.cn/view/deb54ffb
  1. /*
  2.  *
  3.  *      ChartCtrl.cpp
  4.  *
  5.  *      Written by C閐ric Moonen (cedric_moonen@hotmail.com)
  6.  *
  7.  *
  8.  *
  9.  *      This code may be used for any non-commercial and commercial purposes in a compiled form.
  10.  *      The code may be redistributed as long as it remains unmodified and providing that the
  11.  *      author name and this disclaimer remain intact. The sources can be modified WITH the author
  12.  *      consent only.
  13.  *     
  14.  *      This code is provided without any garanties. I cannot be held responsible for the damage or
  15.  *      the loss of time it causes. Use it at your own risks
  16.  *
  17.  *      An e-mail to notify me that you are using this code is appreciated also.
  18.  *
  19.  *
  20.  *      History:
  21.  *              - 18/05/2006: Added support for panning
  22.  *              - 28/05/2006: Bug corrected in RemoveAllSeries
  23.  *              - 28/05/2006: Added support for resizing
  24.  *              - 12/06/2006: Added support for manual zoom
  25.  *              - 10/08/2006: Added SetZoomMinMax and UndoZoom
  26.  *              - 24/03/2007: GDI leak corrected
  27.  *              - 24/03/2007: Invisible series are not taken in account for auto axis
  28.  *                                        and legend (thanks to jerminator-jp).
  29.  *              - 24/03/2007: possibility to specify a margin for the axis. Needs to be improved
  30.  *              - 05/04/2007: ability to change the text color of the axis.
  31.  *              - 05/04/2007: ability to change the color of the border of the drawing area.
  32.  *              - 05/04/2007: Surface series added.
  33.  *              - 26/08/2007: The clipping area of the series is a bit larger (they will be
  34.  *                                        drawn over the bottom and right axes).
  35.  *              - 12/01/2007: Ability to change the color of the zoom rectangle.
  36.  *              - 08/02/2008: Added convenience functions to convert from date to value and
  37.  *                                        opposite.
  38.  *              - 21/02/2008: The zoom doesn't do anything if the user only clicks on the control
  39.  *                                        (thanks to Eugene Pustovoyt).
  40.  *              - 29/02/2008: The auto axis are now refreshed when a series is removed (thanks to
  41.  *                                        Bruno Lavier).
  42.  *              - 08/03/2008: EnableRefresh function added (thanks to Bruno Lavier).
  43.  *              - 21/03/2008: Added support for scrolling.
  44.  *              - 25/03/2008: UndoZoom function added.
  45.  *              - 25/03/2008: A series can now be removed using its pointer.
  46.  *              - 12/08/2008: Performance patch (thanks to Nick Holgate).
  47.  *              - 18/08/2008: Added support for printing.
  48.  *              - 31/10/2008: Fixed a bug for unicode.
  49.  *
  50.  */
  51.  
  52. #include "stdafx.h"
  53. #include "ChartCtrl.h"
  54.  
  55. #include "ChartSerie.h"
  56. #include "ChartGradient.h"
  57. #include "ChartStandardAxis.h"
  58. #include "ChartDateTimeAxis.h"
  59. #include "ChartLogarithmicAxis.h"
  60. #include "ChartCrossHairCursor.h"
  61. #include "ChartDragLineCursor.h"
  62.  
  63. #include "ChartPointsSerie.h"
  64. #include "ChartLineSerie.h"
  65. #include "ChartSurfaceSerie.h"
  66. #include "ChartBarSerie.h"
  67. #include "ChartCandlestickSerie.h"
  68. #include "ChartGanttSerie.h"
  69.  
  70. #if _MFC_VER > 0x0600
  71. #include "atlImage.h"
  72. #endif
  73.  
  74. using namespace std;
  75.  
  76. #ifdef _DEBUG
  77. #define new DEBUG_NEW
  78. #undef THIS_FILE
  79. static char THIS_FILE[] = __FILE__;
  80. #endif
  81.  
  82. #define CHARTCTRL_CLASSNAME    _T("ChartCtrl")  // Window class name
  83.  
  84.  
  85. COLORREF pSeriesColorTable[] = { RGB(255,0,0), RGB(0,150,0), RGB(0,0,255), RGB(255,255,0), RGB(0,255,255),
  86.                                                                  RGB(255,128,0), RGB(128,0,128), RGB(128,128,0), RGB(255,0,255), RGB(64,128,128)};
  87.  
  88. /////////////////////////////////////////////////////////////////////////////
  89. // CChartCtrl
  90.  
  91. CChartCtrl::CChartCtrl()
  92. {
  93.         RegisterWindowClass();
  94.  
  95.         m_iEnableRefresh = 1;
  96.         m_bPendingRefresh = false;
  97.         m_BorderColor = RGB(0,0,0);
  98.         m_BackColor = GetSysColor(COLOR_BTNFACE);
  99.         EdgeType = EDGE_RAISED;
  100.         m_BackGradientType = gtVertical;
  101.         m_bBackGradient = false;
  102.         m_BackGradientCol1 = m_BackGradientCol2 = m_BackColor;
  103.  
  104.         for (int i=0;i<4;i++)
  105.                 m_pAxes[i] = NULL;
  106.  
  107.         m_pLegend = new CChartLegend(this);
  108.         m_pTitles = new CChartTitle(this);
  109.        
  110.         m_bMemDCCreated = false;
  111.         m_bPanEnabled = true;
  112.         m_bRMouseDown = false;
  113.  
  114.         m_bZoomEnabled = true;
  115.         m_bLMouseDown = false;
  116.         m_ZoomRectColor = RGB(255,255,255);
  117.  
  118.         m_bToolBarCreated = false;
  119.         m_pMouseListener = NULL;
  120.         m_bMouseVisible = true;
  121. }
  122.  
  123. CChartCtrl::~CChartCtrl()
  124. {
  125.         TSeriesMap::iterator seriesIter = m_mapSeries.begin();
  126.         for (seriesIter; seriesIter!=m_mapSeries.end(); seriesIter++)
  127.         {
  128.                 delete (seriesIter->second);
  129.         }
  130.         TCursorMap::iterator cursorIter = m_mapCursors.begin();
  131.         for (cursorIter; cursorIter!=m_mapCursors.end(); cursorIter++)
  132.         {
  133.                 delete (cursorIter->second);
  134.         }
  135.  
  136.         for (int i=0; i<4 ;i++)
  137.         {
  138.                 if (m_pAxes[i])
  139.                         delete m_pAxes[i];
  140.         }
  141.  
  142.         if (m_pLegend)
  143.         {
  144.                 delete m_pLegend;
  145.                 m_pLegend = NULL;
  146.         }
  147.         if (m_pTitles)
  148.         {
  149.                 delete m_pTitles;
  150.                 m_pTitles = NULL;
  151.         }
  152. }
  153.  
  154.  
  155. BEGIN_MESSAGE_MAP(CChartCtrl, CWnd)
  156.         //{{AFX_MSG_MAP(CChartCtrl)
  157.         ON_WM_PAINT()
  158.         ON_WM_ERASEBKGND()
  159.         ON_WM_MOUSEMOVE()
  160.         ON_WM_LBUTTONDOWN()
  161.         ON_WM_LBUTTONUP()
  162.         ON_WM_LBUTTONDBLCLK()
  163.         ON_WM_RBUTTONDOWN()
  164.         ON_WM_RBUTTONUP()
  165.         ON_WM_RBUTTONDBLCLK()
  166.         ON_WM_SIZE()
  167.         ON_WM_HSCROLL()
  168.         ON_WM_VSCROLL()
  169.         //}}AFX_MSG_MAP
  170. END_MESSAGE_MAP()
  171.  
  172.  
  173. /////////////////////////////////////////////////////////////////////////////
  174. // CChartCtrl message handlers
  175.  
  176. void CChartCtrl::OnPaint()
  177. {
  178.         CPaintDC dc(this); // device context for painting
  179.  
  180.         if (!m_bMemDCCreated)
  181.         {
  182.                 RefreshCtrl();
  183.                 m_bMemDCCreated = true;
  184.         }
  185.  
  186.     // Get Size of Display area
  187.     CRect rect;
  188.     GetClientRect(&rect);
  189.         dc.BitBlt(0, 0, rect.Width(), rect.Height(),
  190.                           &m_BackgroundDC, 0, 0, SRCCOPY) ;
  191.  
  192.         // Draw the zoom rectangle
  193.         if (m_bZoomEnabled && m_bLMouseDown)
  194.         {
  195.                 CPen NewPen(PS_SOLID,1,m_ZoomRectColor);
  196.                 CPen* pOldPen = dc.SelectObject(&NewPen);
  197.  
  198.                 dc.MoveTo(m_rectZoomArea.TopLeft());
  199.                 dc.LineTo(m_rectZoomArea.right,m_rectZoomArea.top);
  200.                 dc.LineTo(m_rectZoomArea.BottomRight());
  201.                 dc.LineTo(m_rectZoomArea.left,m_rectZoomArea.bottom);
  202.                 dc.LineTo(m_rectZoomArea.TopLeft());
  203.  
  204.                 dc.SelectObject(pOldPen);
  205.                 DeleteObject(NewPen);
  206.         }
  207.  
  208.         // Draw the cursors.
  209.         TCursorMap::iterator iter = m_mapCursors.begin();
  210.         for (iter; iter!=m_mapCursors.end(); iter++)
  211.                 iter->second->Draw(&dc);
  212. }
  213.  
  214. BOOL CChartCtrl::OnEraseBkgnd(CDC* )
  215. {
  216.         // To avoid flickering
  217. //      return CWnd::OnEraseBkgnd(pDC);
  218.         return FALSE;
  219. }
  220.  
  221. CChartCrossHairCursor* CChartCtrl::CreateCrossHairCursor(bool bSecondaryHorizAxis,
  222.                                                                                                                  bool bSecondaryVertAxis)
  223. {
  224.         CChartAxis* pHorizAxis = NULL;
  225.         CChartAxis* pVertAxis = NULL;
  226.         if (bSecondaryHorizAxis)
  227.                 pHorizAxis = m_pAxes[TopAxis];
  228.         else
  229.                 pHorizAxis = m_pAxes[BottomAxis];
  230.         if (bSecondaryVertAxis)
  231.                 pVertAxis = m_pAxes[RightAxis];
  232.         else
  233.                 pVertAxis = m_pAxes[LeftAxis];
  234.  
  235.         ASSERT(pHorizAxis != NULL);
  236.         ASSERT(pVertAxis != NULL);
  237.  
  238.         CChartCrossHairCursor* pNewCursor = new CChartCrossHairCursor(this, pHorizAxis, pVertAxis);
  239.         m_mapCursors[pNewCursor->GetCursorId()] = pNewCursor;
  240.         return pNewCursor;
  241. }
  242.  
  243. CChartDragLineCursor* CChartCtrl::CreateDragLineCursor(EAxisPos relatedAxis)
  244. {
  245.         ASSERT(m_pAxes[relatedAxis] != NULL);
  246.  
  247.         CChartDragLineCursor* pNewCursor = new CChartDragLineCursor(this, m_pAxes[relatedAxis]);
  248.         m_mapCursors[pNewCursor->GetCursorId()] = pNewCursor;
  249.         return pNewCursor;
  250. }
  251.  
  252. void CChartCtrl::AttachCustomCursor(CChartCursor* pCursor)
  253. {
  254.         m_mapCursors[pCursor->GetCursorId()] = pCursor;
  255. }
  256.  
  257. void CChartCtrl::RemoveCursor(unsigned cursorId)
  258. {
  259.         TCursorMap::iterator iter = m_mapCursors.find(cursorId);
  260.         if (iter != m_mapCursors.end())
  261.         {
  262.                 delete iter->second;
  263.                 m_mapCursors.erase(iter);
  264.         }
  265. }
  266.  
  267. void CChartCtrl::ShowMouseCursor(bool bShow)
  268. {
  269.         m_bMouseVisible = bShow;
  270.         if (!bShow)
  271.                 SetCursor(NULL);
  272. }
  273.  
  274. CChartStandardAxis* CChartCtrl::CreateStandardAxis(EAxisPos axisPos)
  275. {
  276.         CChartStandardAxis* pAxis = new CChartStandardAxis();
  277.         AttachCustomAxis(pAxis, axisPos);
  278.         return pAxis;
  279. }
  280.  
  281. CChartLogarithmicAxis* CChartCtrl::CreateLogarithmicAxis(EAxisPos axisPos)
  282. {
  283.         CChartLogarithmicAxis* pAxis = new CChartLogarithmicAxis();
  284.         AttachCustomAxis(pAxis, axisPos);
  285.         return pAxis;
  286. }
  287.  
  288. CChartDateTimeAxis* CChartCtrl::CreateDateTimeAxis(EAxisPos axisPos)
  289. {
  290.         CChartDateTimeAxis* pAxis = new CChartDateTimeAxis();
  291.         AttachCustomAxis(pAxis, axisPos);
  292.         return pAxis;
  293. }
  294.  
  295. void CChartCtrl::AttachCustomAxis(CChartAxis* pAxis, EAxisPos axisPos)
  296. {
  297.         // The axis should not be already attached to another control
  298.         ASSERT(pAxis->m_pParentCtrl == NULL);
  299.         pAxis->SetParent(this);
  300.  
  301.         if ( (axisPos==RightAxis) || (axisPos==TopAxis) )
  302.                 pAxis->SetSecondary(true);
  303.         if (  (axisPos==BottomAxis) || (axisPos==TopAxis) )
  304.                 pAxis->SetHorizontal(true);
  305.         else
  306.                 pAxis->SetHorizontal(false);
  307.         pAxis->CreateScrollBar();
  308.  
  309.         // Beofre storing the new axis, we should delete the previous one if any
  310.         if (m_pAxes[axisPos])
  311.                 delete m_pAxes[axisPos];
  312.         m_pAxes[axisPos] = pAxis;
  313. }
  314.  
  315. bool CChartCtrl::RegisterWindowClass()
  316. {
  317.         WNDCLASS wndcls;
  318.     HINSTANCE hInst = AfxGetInstanceHandle();
  319.  
  320.     if (!(::GetClassInfo(hInst, CHARTCTRL_CLASSNAME, &wndcls)))
  321.     {
  322.                 memset(&wndcls, 0, sizeof(WNDCLASS));  
  323.  
  324.                 wndcls.hInstance                = hInst;
  325.                 wndcls.lpfnWndProc              = ::DefWindowProc;
  326.                 wndcls.hCursor                  = NULL; //LoadCursor(NULL, IDC_ARROW);
  327.                 wndcls.hIcon                    = 0;
  328.                 wndcls.lpszMenuName             = NULL;
  329.                 wndcls.hbrBackground    = (HBRUSH) ::GetStockObject(WHITE_BRUSH);
  330.                 wndcls.style                    = CS_DBLCLKS;
  331.                 wndcls.cbClsExtra               = 0;
  332.                 wndcls.cbWndExtra               = 0;
  333.                 wndcls.lpszClassName    = CHARTCTRL_CLASSNAME;
  334.  
  335.         if (!RegisterClass(&wndcls))
  336.         {
  337.           //  AfxThrowResourceException();
  338.             return false;
  339.         }
  340.     }
  341.  
  342.     return true;
  343.  
  344. }
  345.  
  346. int CChartCtrl::Create(CWnd *pParentWnd, const RECT &rect, UINT nID, DWORD dwStyle)
  347. {
  348.         dwStyle |= WS_CLIPCHILDREN;
  349.         int Result = CWnd::Create(CHARTCTRL_CLASSNAME, _T(""), dwStyle, rect, pParentWnd, nID);
  350.  
  351.         if (Result)
  352.                 RefreshCtrl();
  353.  
  354.         return Result;
  355. }
  356.  
  357. void CChartCtrl::SetBackGradient(COLORREF Col1, COLORREF Col2, EGradientType GradientType)
  358. {
  359.         m_bBackGradient = true;
  360.         m_BackGradientCol1 = Col1;
  361.         m_BackGradientCol2 = Col2;
  362.         m_BackGradientType = GradientType;
  363.         RefreshCtrl();
  364. }
  365.  
  366. void CChartCtrl::EnableRefresh(bool bEnable)
  367. {
  368.         if (bEnable)
  369.                 m_iEnableRefresh++;
  370.         else
  371.                 m_iEnableRefresh--;
  372.         if (m_iEnableRefresh > 0 && m_bPendingRefresh)
  373.         {
  374.                 m_bPendingRefresh = false;
  375.                 RefreshCtrl();
  376.         }
  377. }
  378.  
  379. void CChartCtrl::UndoPanZoom()
  380. {
  381.         EnableRefresh(false);
  382.         if (m_pAxes[BottomAxis])
  383.                 m_pAxes[BottomAxis]->UndoZoom();
  384.         if (m_pAxes[LeftAxis])
  385.                 m_pAxes[LeftAxis]->UndoZoom();
  386.         if (m_pAxes[TopAxis])
  387.                 m_pAxes[TopAxis]->UndoZoom();
  388.         if (m_pAxes[RightAxis])
  389.                 m_pAxes[RightAxis]->UndoZoom();
  390.         EnableRefresh(true);
  391. }
  392.  
  393. void CChartCtrl::RefreshCtrl()
  394. {
  395.         // Window is not created yet, so skip the refresh.
  396.         if (!GetSafeHwnd())
  397.                 return;
  398.         if (m_iEnableRefresh < 1)
  399.         {
  400.                 m_bPendingRefresh = true;
  401.                 return;
  402.         }
  403.  
  404.         // Retrieve the client rect and initialize the
  405.         // plotting rect
  406.         CClientDC dc(this) ;  
  407.         CRect ClientRect;
  408.         GetClientRect(&ClientRect);
  409.         m_PlottingRect = ClientRect;           
  410.  
  411.         // If the backgroundDC was not created yet, create it (it
  412.         // is used to avoid flickering).
  413.         if (!m_BackgroundDC.GetSafeHdc() )
  414.         {
  415.                 CBitmap memBitmap;
  416.                 m_BackgroundDC.CreateCompatibleDC(&dc) ;
  417.                 memBitmap.CreateCompatibleBitmap(&dc, ClientRect.Width(),ClientRect.Height()) ;
  418.                 m_BackgroundDC.SelectObject(&memBitmap) ;
  419.         }
  420.  
  421.         // Draw the chart background, which is not part of
  422.         // the DrawChart function (to avoid a background when
  423.         // printing).
  424.         DrawBackground(&m_BackgroundDC, ClientRect);
  425.         ClientRect.DeflateRect(3,3);
  426.         DrawChart(&m_BackgroundDC,ClientRect);
  427.         for (int i=0; i<4 ;i++)
  428.         {
  429.                 if (m_pAxes[i])
  430.                         m_pAxes[i]->UpdateScrollBarPos();
  431.         }
  432.         Invalidate();
  433. }
  434.  
  435. void CChartCtrl::DrawChart(CDC* pDC, CRect ChartRect)
  436. {
  437.         m_PlottingRect = ChartRect;
  438.         CSize TitleSize = m_pTitles->GetSize(pDC);
  439.         CRect rcTitle;
  440.         rcTitle = ChartRect;
  441.         rcTitle.bottom = TitleSize.cy;
  442.  
  443.         ChartRect.top += TitleSize.cy;
  444.         m_pTitles->SetTitleRect(rcTitle);
  445.         m_pTitles->Draw(pDC);
  446.  
  447.         m_pLegend->ClipArea(ChartRect,pDC);
  448.  
  449.         //Clip all the margins (axis) of the client rect
  450.         int n=0;
  451.         for (n=0;n<4;n++)
  452.         {
  453.                 if (m_pAxes[n])
  454.                 {
  455.                         m_pAxes[n]->SetAxisSize(ChartRect,m_PlottingRect);
  456.                         m_pAxes[n]->Recalculate();
  457.                         m_pAxes[n]->ClipMargin(ChartRect,m_PlottingRect,pDC);
  458.                 }
  459.         }
  460.         for (n=0;n<4;n++)
  461.         {
  462.                 if (m_pAxes[n])
  463.                 {
  464.                         m_pAxes[n]->SetAxisSize(ChartRect,m_PlottingRect);
  465.                         m_pAxes[n]->Recalculate();
  466.                         m_pAxes[n]->Draw(pDC);
  467.                 }
  468.         }
  469.  
  470.         CPen SolidPen(PS_SOLID,0,m_BorderColor);
  471.         CPen* pOldPen = pDC->SelectObject(&SolidPen);
  472.  
  473.         pDC->MoveTo(m_PlottingRect.left,m_PlottingRect.top);
  474.         pDC->LineTo(m_PlottingRect.right,m_PlottingRect.top);
  475.         pDC->LineTo(m_PlottingRect.right,m_PlottingRect.bottom);
  476.         pDC->LineTo(m_PlottingRect.left,m_PlottingRect.bottom);
  477.         pDC->LineTo(m_PlottingRect.left,m_PlottingRect.top);
  478.  
  479.         pDC->SelectObject(pOldPen);
  480.         DeleteObject(SolidPen);
  481.  
  482.         TSeriesMap::iterator iter = m_mapSeries.begin();
  483.         for (iter; iter!=m_mapSeries.end(); iter++)
  484.         {
  485.                 CRect drawingRect = m_PlottingRect;
  486.                 drawingRect.bottom += 1;
  487.                 drawingRect.right += 1;
  488.                 iter->second->SetPlottingRect(drawingRect);
  489.                 iter->second->DrawAll(pDC);
  490.         }
  491.  
  492.         pDC->IntersectClipRect(m_PlottingRect);
  493.         // Draw the labels when all series have been drawn
  494.         for (iter=m_mapSeries.begin(); iter!=m_mapSeries.end(); iter++)
  495.         {
  496.                 iter->second->DrawLabels(pDC);
  497.         }
  498.         pDC->SelectClipRgn(NULL);
  499.  
  500.         // Draw the legend at the end (when floating it should come over the plotting area).
  501.         m_pLegend->Draw(pDC);
  502. }
  503.  
  504. void CChartCtrl::DrawBackground(CDC* pDC, CRect ChartRect)
  505. {
  506.         CBrush BrushBack;
  507.         BrushBack.CreateSolidBrush(m_BackColor) ;
  508.         if (!m_bBackGradient)
  509.         {
  510.                 pDC->SetBkColor(m_BackColor);
  511.                 pDC->FillRect(ChartRect,&BrushBack);
  512.         }
  513.         else
  514.         {
  515.                 CChartGradient::DrawGradient(pDC,ChartRect,m_BackGradientCol1,
  516.                                                                          m_BackGradientCol2,m_BackGradientType);
  517.         }
  518.  
  519.         // Draw the edge.
  520.         pDC->DrawEdge(ChartRect,EdgeType,BF_RECT);
  521. }
  522.  
  523. void CChartCtrl::RefreshScreenAutoAxes()
  524. {
  525.         for (int n=0;n<4;n++)
  526.         {
  527.                 if (m_pAxes[n])
  528.                         m_pAxes[n]->RefreshScreenAutoAxis();
  529.         }
  530. }
  531.  
  532. CChartPointsSerie* CChartCtrl::CreatePointsSerie(bool bSecondaryHorizAxis,
  533.                                                                                            bool bSecondaryVertAxis)
  534. {
  535.         CChartPointsSerie* pNewSerie = new CChartPointsSerie(this);
  536.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  537.         return pNewSerie;
  538. }
  539.  
  540. CChartLineSerie* CChartCtrl::CreateLineSerie(bool bSecondaryHorizAxis,
  541.                                                                                          bool bSecondaryVertAxis)
  542. {
  543.         CChartLineSerie* pNewSerie = new CChartLineSerie(this);
  544.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  545.         return pNewSerie;
  546. }
  547.  
  548. CChartSurfaceSerie* CChartCtrl::CreateSurfaceSerie(bool bSecondaryHorizAxis,
  549.                                                                                                    bool bSecondaryVertAxis)
  550. {
  551.         CChartSurfaceSerie* pNewSerie = new CChartSurfaceSerie(this);
  552.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  553.         return pNewSerie;
  554. }
  555.  
  556. CChartBarSerie* CChartCtrl::CreateBarSerie(bool bSecondaryHorizAxis,
  557.                                                                                    bool bSecondaryVertAxis)
  558. {
  559.         CChartBarSerie* pNewSerie = new CChartBarSerie(this);
  560.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  561.         return pNewSerie;
  562. }
  563.  
  564. CChartCandlestickSerie* CChartCtrl::CreateCandlestickSerie(bool bSecondaryHorizAxis,
  565.                                                                                                                    bool bSecondaryVertAxis)
  566. {
  567.         CChartCandlestickSerie* pNewSerie = new CChartCandlestickSerie(this);
  568.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  569.         return pNewSerie;
  570. }
  571.  
  572. CChartGanttSerie* CChartCtrl::CreateGanttSerie(bool bSecondaryHorizAxis,
  573.                                                                                                          bool bSecondaryVertAxis)
  574. {
  575.         CChartGanttSerie* pNewSerie = new CChartGanttSerie(this);
  576.         AttachCustomSerie(pNewSerie, bSecondaryHorizAxis, bSecondaryVertAxis);
  577.         return pNewSerie;
  578. }
  579.  
  580. void CChartCtrl::AttachCustomSerie(CChartSerie* pNewSeries,
  581.                                                                    bool bSecondaryHorizAxis,
  582.                                                                    bool bSecondaryVertAxis)
  583. {
  584.         size_t ColIndex = m_mapSeries.size()%10;
  585.  
  586.         CChartAxis* pHorizAxis = NULL;
  587.         CChartAxis* pVertAxis = NULL;
  588.         if (bSecondaryHorizAxis)
  589.                 pHorizAxis = m_pAxes[TopAxis];
  590.         else
  591.                 pHorizAxis = m_pAxes[BottomAxis];
  592.         if (bSecondaryVertAxis)
  593.                 pVertAxis = m_pAxes[RightAxis];
  594.         else
  595.                 pVertAxis = m_pAxes[LeftAxis];
  596.  
  597.         ASSERT(pHorizAxis != NULL);
  598.         ASSERT(pVertAxis != NULL);
  599.  
  600.         if (pNewSeries)
  601.         {
  602.                 pNewSeries->SetPlottingRect(m_PlottingRect);
  603.                 pNewSeries->SetColor(pSeriesColorTable[ColIndex]);
  604.                 pNewSeries->m_pHorizontalAxis = pHorizAxis;
  605.                 pNewSeries->m_pVerticalAxis = pVertAxis;
  606.                 m_mapSeries[pNewSeries->GetSerieId()] = pNewSeries;
  607.  
  608.                 EnableRefresh(false);
  609.                 pVertAxis->RegisterSeries(pNewSeries);
  610.                 pVertAxis->RefreshAutoAxis();
  611.                 pHorizAxis->RegisterSeries(pNewSeries);
  612.                 pHorizAxis->RefreshAutoAxis();
  613.  
  614.                 // The series will need to be redrawn so we need to refresh the control
  615.                 RefreshCtrl();
  616.                 EnableRefresh(true);
  617.         }
  618. }
  619.  
  620. CChartSerie* CChartCtrl::GetSerie(size_t uSerieId) const
  621. {
  622.         CChartSerie* pToReturn = NULL;
  623.         TSeriesMap::const_iterator iter = m_mapSeries.find(uSerieId);
  624.         if (iter != m_mapSeries.end())
  625.         {
  626.                 pToReturn = iter->second;
  627.         }
  628.  
  629.         return pToReturn;
  630. }
  631.  
  632. void CChartCtrl::RemoveSerie(unsigned uSerieId)
  633. {
  634.         TSeriesMap::iterator iter = m_mapSeries.find(uSerieId);
  635.         if (iter != m_mapSeries.end())
  636.         {
  637.                 CChartSerie* pToDelete = iter->second;
  638.                 m_mapSeries.erase(iter);
  639.  
  640.                 EnableRefresh(false);
  641.                 pToDelete->m_pVerticalAxis->UnregisterSeries(pToDelete);
  642.                 pToDelete->m_pHorizontalAxis->UnregisterSeries(pToDelete);
  643.                 pToDelete->m_pVerticalAxis->RefreshAutoAxis();
  644.                 pToDelete->m_pHorizontalAxis->RefreshAutoAxis();
  645.                 delete pToDelete;
  646.                 RefreshCtrl();
  647.                 EnableRefresh(true);
  648.         }
  649. }
  650.  
  651. void CChartCtrl::RemoveAllSeries()
  652. {
  653.         TSeriesMap::iterator iter = m_mapSeries.begin();
  654.         for (iter; iter != m_mapSeries.end(); iter++)
  655.         {
  656.                 delete iter->second;
  657.         }
  658.  
  659.         m_mapSeries.clear();
  660.         RefreshCtrl();
  661. }
  662.  
  663.  
  664. CChartAxis* CChartCtrl::GetBottomAxis() const
  665. {
  666.         return (m_pAxes[BottomAxis]);
  667. }
  668.  
  669. CChartAxis* CChartCtrl::GetLeftAxis() const
  670. {
  671.         return (m_pAxes[LeftAxis]);
  672. }
  673.  
  674. CChartAxis* CChartCtrl::GetTopAxis() const
  675. {
  676.         return (m_pAxes[TopAxis]);
  677. }
  678.  
  679. CChartAxis* CChartCtrl::GetRightAxis() const
  680. {
  681.         return (m_pAxes[RightAxis]);
  682. }
  683.  
  684.  
  685. CDC* CChartCtrl::GetDC()
  686. {
  687.         return &m_BackgroundDC;
  688. }
  689.  
  690.  
  691. size_t CChartCtrl::GetSeriesCount() const
  692. {
  693.         return m_mapSeries.size();
  694. }
  695.  
  696. /////////////////////////////////////////////////////////////////////////////
  697. // Mouse events
  698.  
  699. void CChartCtrl::OnMouseMove(UINT nFlags, CPoint point)
  700. {
  701.         if (m_bRMouseDown && m_bPanEnabled)
  702.         {
  703.                 if (point != m_PanAnchor)
  704.                 {
  705.                         EnableRefresh(false);
  706.                         if (m_pAxes[LeftAxis])
  707.                                 m_pAxes[LeftAxis]->PanAxis(m_PanAnchor.y,point.y);
  708.                         if (m_pAxes[RightAxis])
  709.                                 m_pAxes[RightAxis]->PanAxis(m_PanAnchor.y,point.y);
  710.                         if (m_pAxes[BottomAxis])
  711.                                 m_pAxes[BottomAxis]->PanAxis(m_PanAnchor.x,point.x);
  712.                         if (m_pAxes[TopAxis])
  713.                                 m_pAxes[TopAxis]->PanAxis(m_PanAnchor.x,point.x);
  714.                         RefreshCtrl();
  715.                         EnableRefresh(true);
  716.                         // Force an immediate repaint of the window, so that the mouse messages
  717.                         // are by passed (this allows for a smooth pan)
  718.                         UpdateWindow();
  719.  
  720.                         m_PanAnchor = point;
  721.                 }
  722.         }
  723.  
  724.         if (m_bLMouseDown && m_bZoomEnabled)
  725.         {
  726.                 m_rectZoomArea.BottomRight() = point;
  727.                 Invalidate();
  728.         }
  729.  
  730.         for (int i=0; i<4; i++)
  731.         {
  732.                 if (m_pAxes[i])
  733.                         m_pAxes[i]->m_pScrollBar->OnMouseLeave();
  734.         }
  735.         CWnd* pWnd = ChildWindowFromPoint(point);
  736.         if (pWnd != this)
  737.         {
  738.                 CChartScrollBar* pScrollBar = dynamic_cast<CChartScrollBar*>(pWnd);
  739.                 if (pScrollBar)
  740.                         pScrollBar->OnMouseEnter();
  741.         }
  742.  
  743.         if (m_PlottingRect.PtInRect(point))
  744.         {
  745.                 TCursorMap::iterator iter = m_mapCursors.begin();
  746.                 for (iter; iter!=m_mapCursors.end(); iter++)
  747.                         iter->second->OnMouseMove(point);
  748.                
  749.                 Invalidate();
  750.         }
  751.  
  752.         if (!m_bMouseVisible && m_PlottingRect.PtInRect(point))
  753.                 SetCursor(NULL);
  754.         else
  755.                 SetCursor(::LoadCursor(NULL,IDC_ARROW));
  756.  
  757.         SendMouseEvent(CChartMouseListener::MouseMove, point);
  758.         CWnd::OnMouseMove(nFlags, point);
  759. }
  760.  
  761. void CChartCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  762. {
  763.         SetCapture();
  764.         if (m_bZoomEnabled)
  765.         {
  766.                 m_bLMouseDown = true;
  767.                 m_rectZoomArea.TopLeft() = point;
  768.                 m_rectZoomArea.BottomRight() = point;
  769.         }
  770.  
  771.         if (m_PlottingRect.PtInRect(point))
  772.         {
  773.                 TCursorMap::iterator iter = m_mapCursors.begin();
  774.                 for (iter; iter!=m_mapCursors.end(); iter++)
  775.                         iter->second->OnMouseButtonDown(point);
  776.                
  777.                 Invalidate();
  778.         }
  779.  
  780.         SendMouseEvent(CChartMouseListener::LButtonDown, point);
  781.         CWnd::OnLButtonDown(nFlags, point);
  782. }
  783.  
  784. void CChartCtrl::OnLButtonUp(UINT nFlags, CPoint point)
  785. {
  786.         ReleaseCapture();
  787.         m_bLMouseDown = false;
  788.         if (m_bZoomEnabled)
  789.         {
  790.                 if ( (m_rectZoomArea.left > m_rectZoomArea.right) ||
  791.                          (m_rectZoomArea.top > m_rectZoomArea.bottom))
  792.                 {
  793.                         UndoPanZoom();
  794.                 }
  795.                 else if ( (m_rectZoomArea.left!=m_rectZoomArea.right) &&
  796.                                   (m_rectZoomArea.top!=m_rectZoomArea.bottom))
  797.                 {
  798.                         double MinVal = 0;                     
  799.                         double MaxVal = 0;
  800.                        
  801.                         if (m_pAxes[BottomAxis])
  802.                         {
  803.                                 if (m_pAxes[BottomAxis]->IsInverted())
  804.                                 {
  805.                                         MaxVal = m_pAxes[BottomAxis]->ScreenToValue(m_rectZoomArea.left);
  806.                                         MinVal = m_pAxes[BottomAxis]->ScreenToValue(m_rectZoomArea.right);
  807.                                 }
  808.                                 else
  809.                                 {
  810.                                         MinVal = m_pAxes[BottomAxis]->ScreenToValue(m_rectZoomArea.left);
  811.                                         MaxVal = m_pAxes[BottomAxis]->ScreenToValue(m_rectZoomArea.right);
  812.                                 }
  813.                                 m_pAxes[BottomAxis]->SetZoomMinMax(MinVal,MaxVal);
  814.                         }
  815.  
  816.                         if (m_pAxes[LeftAxis])
  817.                         {
  818.                                 if (m_pAxes[LeftAxis]->IsInverted())
  819.                                 {
  820.                                         MaxVal = m_pAxes[LeftAxis]->ScreenToValue(m_rectZoomArea.bottom);
  821.                                         MinVal = m_pAxes[LeftAxis]->ScreenToValue(m_rectZoomArea.top);
  822.                                 }
  823.                                 else
  824.                                 {
  825.                                         MinVal = m_pAxes[LeftAxis]->ScreenToValue(m_rectZoomArea.bottom);
  826.                                         MaxVal = m_pAxes[LeftAxis]->ScreenToValue(m_rectZoomArea.top);
  827.                                 }
  828.                                 m_pAxes[LeftAxis]->SetZoomMinMax(MinVal,MaxVal);
  829.                         }
  830.  
  831.                         if (m_pAxes[TopAxis])
  832.                         {
  833.                                 if (m_pAxes[TopAxis]->IsInverted())
  834.                                 {
  835.                                         MaxVal = m_pAxes[TopAxis]->ScreenToValue(m_rectZoomArea.left);
  836.                                         MinVal = m_pAxes[TopAxis]->ScreenToValue(m_rectZoomArea.right);
  837.                                 }
  838.                                 else
  839.                                 {
  840.                                         MinVal = m_pAxes[TopAxis]->ScreenToValue(m_rectZoomArea.left);
  841.                                         MaxVal = m_pAxes[TopAxis]->ScreenToValue(m_rectZoomArea.right);
  842.                                 }
  843.                                 m_pAxes[TopAxis]->SetZoomMinMax(MinVal,MaxVal);
  844.                         }
  845.  
  846.                         if (m_pAxes[RightAxis])
  847.                         {
  848.                                 if (m_pAxes[RightAxis]->IsInverted())
  849.                                 {
  850.                                         MaxVal = m_pAxes[RightAxis]->ScreenToValue(m_rectZoomArea.bottom);
  851.                                         MinVal = m_pAxes[RightAxis]->ScreenToValue(m_rectZoomArea.top);
  852.                                 }
  853.                                 else
  854.                                 {
  855.                                         MinVal = m_pAxes[RightAxis]->ScreenToValue(m_rectZoomArea.bottom);
  856.                                         MaxVal = m_pAxes[RightAxis]->ScreenToValue(m_rectZoomArea.top);
  857.                                 }
  858.                                 m_pAxes[RightAxis]->SetZoomMinMax(MinVal,MaxVal);
  859.                         }
  860.  
  861.                         RefreshCtrl();
  862.                 }
  863.         }
  864.  
  865.         if (m_PlottingRect.PtInRect(point))
  866.         {
  867.                 TCursorMap::iterator iter = m_mapCursors.begin();
  868.                 for (iter; iter!=m_mapCursors.end(); iter++)
  869.                         iter->second->OnMouseButtonUp(point);
  870.                
  871.                 Invalidate();
  872.         }
  873.  
  874.         SendMouseEvent(CChartMouseListener::LButtonUp, point);
  875.         CWnd::OnLButtonUp(nFlags, point);
  876. }
  877.  
  878. void CChartCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
  879. {
  880.         SendMouseEvent(CChartMouseListener::LButtonDoubleClick, point);
  881.         CWnd::OnLButtonDblClk(nFlags, point);
  882. }
  883.  
  884. void CChartCtrl::OnRButtonDown(UINT nFlags, CPoint point)
  885. {
  886.         SetCapture();
  887.         m_bRMouseDown = true;
  888.         if (m_bPanEnabled)
  889.                 m_PanAnchor = point;
  890.  
  891.         SendMouseEvent(CChartMouseListener::RButtonDown, point);
  892.         CWnd::OnRButtonDown(nFlags, point);
  893. }
  894.  
  895. void CChartCtrl::OnRButtonUp(UINT nFlags, CPoint point)
  896. {
  897.         ReleaseCapture();
  898.         m_bRMouseDown = false;
  899.  
  900.         SendMouseEvent(CChartMouseListener::RButtonUp, point);
  901.         CWnd::OnRButtonUp(nFlags, point);
  902. }
  903.  
  904. void CChartCtrl::OnRButtonDblClk(UINT nFlags, CPoint point)
  905. {
  906.         SendMouseEvent(CChartMouseListener::RButtonDoubleClick, point);
  907.         CWnd::OnRButtonDblClk(nFlags, point);
  908. }
  909.  
  910. void CChartCtrl::SendMouseEvent(CChartMouseListener::MouseEvent mouseEvent,
  911.                                                                 const CPoint& screenPoint) const
  912. {
  913.         if (m_pMouseListener)
  914.         {
  915.                 // Check where the click occured.
  916.                 if (m_pTitles->IsPointInside(screenPoint))
  917.                         m_pMouseListener->OnMouseEventTitle(mouseEvent,screenPoint);
  918.                 if (m_pLegend->IsPointInside(screenPoint))
  919.                         m_pMouseListener->OnMouseEventLegend(mouseEvent,screenPoint);
  920.                 for (int i=0; i<4; i++)
  921.                 {
  922.                         if ( m_pAxes[i] && m_pAxes[i]->IsPointInside(screenPoint) )
  923.                                 m_pMouseListener->OnMouseEventAxis(mouseEvent,screenPoint,m_pAxes[i]);
  924.                 }
  925.                 if (m_PlottingRect.PtInRect(screenPoint))
  926.                         m_pMouseListener->OnMouseEventPlotArea(mouseEvent,screenPoint);
  927.         }
  928.        
  929.         // Check all the series in reverse order (check the series on top first).
  930.         TSeriesMap::const_reverse_iterator rIter = m_mapSeries.rbegin();
  931.         for(rIter; rIter!=m_mapSeries.rend(); rIter++)
  932.         {
  933.                 if (rIter->second->OnMouseEvent(mouseEvent, screenPoint))
  934.                         break;
  935.         }
  936. }
  937.  
  938. void CChartCtrl::OnSize(UINT nType, int cx, int cy)
  939. {
  940.         CWnd::OnSize(nType, cx, cy);
  941.        
  942.         // Force recreation of background DC
  943.         if (m_BackgroundDC.GetSafeHdc() )
  944.                 m_BackgroundDC.DeleteDC();
  945.        
  946.         RefreshCtrl();
  947. }
  948.  
  949. double CChartCtrl::DateToValue(const COleDateTime& Date)
  950. {
  951.         return (DATE)Date;
  952. }
  953.  
  954. COleDateTime CChartCtrl::ValueToDate(double Value)
  955. {
  956.         COleDateTime RetDate((DATE)Value);
  957.         return RetDate;
  958. }
  959.  
  960. void CChartCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  961. {
  962.         CChartScrollBar* pChartBar = dynamic_cast<CChartScrollBar*>(pScrollBar);
  963.         if (pChartBar)
  964.                 pChartBar->OnHScroll(nSBCode, nPos);
  965.  
  966.         CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
  967.         RefreshCtrl();
  968. }
  969.  
  970. void CChartCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  971. {
  972.         CChartScrollBar* pChartBar = dynamic_cast<CChartScrollBar*>(pScrollBar);
  973.         if (pChartBar)
  974.                 pChartBar->OnVScroll(nSBCode, nPos);
  975.  
  976.         CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
  977.         RefreshCtrl();
  978. }
  979.  
  980. void CChartCtrl::Print(const TChartString& strTitle, CPrintDialog* pPrntDialog)
  981. {
  982.         CDC dc;
  983.     if (pPrntDialog == NULL)
  984.     {
  985.         CPrintDialog printDlg(FALSE);
  986.         if (printDlg.DoModal() != IDOK)         // Get printer settings from user
  987.             return;
  988.  
  989.                 dc.Attach(printDlg.GetPrinterDC());     // attach a printer DC
  990.     }
  991.     else
  992.                 dc.Attach(pPrntDialog->GetPrinterDC()); // attach a printer DC
  993.     dc.m_bPrinting = TRUE;
  994.        
  995.     DOCINFO di;                                 // Initialise print doc details
  996.     memset(&di, 0, sizeof (DOCINFO));
  997.     di.cbSize = sizeof (DOCINFO);
  998.         di.lpszDocName = strTitle.c_str();
  999.  
  1000.     BOOL bPrintingOK = dc.StartDoc(&di);        // Begin a new print job
  1001.  
  1002.     CPrintInfo Info;
  1003.     Info.m_rectDraw.SetRect(0,0, dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES));
  1004.  
  1005.     OnBeginPrinting(&dc, &Info);                // Initialise printing
  1006.     for (UINT page = Info.GetMinPage(); page <= Info.GetMaxPage() && bPrintingOK; page++)
  1007.     {
  1008.         dc.StartPage();                         // begin new page
  1009.         Info.m_nCurPage = page;
  1010.         OnPrint(&dc, &Info);                    // Print page
  1011.         bPrintingOK = (dc.EndPage() > 0);       // end page
  1012.     }
  1013.     OnEndPrinting(&dc, &Info);                  // Clean up after printing
  1014.  
  1015.     if (bPrintingOK)
  1016.         dc.EndDoc();                            // end a print job
  1017.     else
  1018.         dc.AbortDoc();                          // abort job.
  1019.  
  1020.     dc.Detach();                                // detach the printer DC
  1021. }
  1022.  
  1023. void CChartCtrl::OnBeginPrinting(CDC *pDC, CPrintInfo *pInfo)
  1024. {
  1025.     // OnBeginPrinting() is called after the user has committed to
  1026.     // printing by OK'ing the Print dialog, and after the framework
  1027.     // has created a CDC object for the printer or the preview view.
  1028.  
  1029.     // This is the right opportunity to set up the page range.
  1030.     // Given the CDC object, we can determine how many rows will
  1031.     // fit on a page, so we can in turn determine how many printed
  1032.     // pages represent the entire document.
  1033.     ASSERT(pDC && pInfo);
  1034.  
  1035.     // Get a DC for the current window (will be a screen DC for print previewing)
  1036.     CDC *pCurrentDC = GetDC();        // will have dimensions of the client area
  1037.     if (!pCurrentDC)
  1038.                 return;
  1039.  
  1040.     CSize PaperPixelsPerInch(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
  1041.     CSize ScreenPixelsPerInch(pCurrentDC->GetDeviceCaps(LOGPIXELSX), pCurrentDC->GetDeviceCaps(LOGPIXELSY));
  1042.  
  1043.     // Create the printer font
  1044.     int nFontSize = -10;
  1045.     CString strFontName = _T("Arial");
  1046.     m_PrinterFont.CreateFont(nFontSize, 0,0,0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,
  1047.                              OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
  1048.                              DEFAULT_PITCH | FF_DONTCARE, strFontName);
  1049.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  1050.  
  1051.     // Get the page sizes (physical and logical)
  1052.     m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  1053.  
  1054.         m_LogicalPageSize.cx = ScreenPixelsPerInch.cx * m_PaperSize.cx / PaperPixelsPerInch.cx * 3 / 4;
  1055.         m_LogicalPageSize.cy = ScreenPixelsPerInch.cy * m_PaperSize.cy / PaperPixelsPerInch.cy * 3 / 4;
  1056.  
  1057.  
  1058.     // Set up the print info
  1059.     pInfo->SetMaxPage(1);
  1060.     pInfo->m_nCurPage = 1;                        // start printing at page# 1
  1061.  
  1062.     ReleaseDC(pCurrentDC);
  1063.     pDC->SelectObject(pOldFont);
  1064. }
  1065.  
  1066. void CChartCtrl::OnPrint(CDC *pDC, CPrintInfo *pInfo)
  1067. {
  1068.     if (!pDC || !pInfo)
  1069.         return;
  1070.  
  1071.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  1072.  
  1073.     // Set the page map mode to use GraphCtrl units
  1074.         pDC->SetMapMode(MM_ANISOTROPIC);
  1075.     pDC->SetWindowExt(m_LogicalPageSize);
  1076.     pDC->SetViewportExt(m_PaperSize);
  1077.     pDC->SetWindowOrg(0, 0);
  1078.  
  1079.     // Header
  1080.     pInfo->m_rectDraw.top    = 0;
  1081.     pInfo->m_rectDraw.left   = 0;
  1082.     pInfo->m_rectDraw.right  = m_LogicalPageSize.cx;
  1083.     pInfo->m_rectDraw.bottom = m_LogicalPageSize.cy;
  1084.  
  1085.         DrawChart(pDC, &pInfo->m_rectDraw);
  1086.  
  1087.     // SetWindowOrg back for next page
  1088.     pDC->SetWindowOrg(0,0);
  1089.  
  1090.     pDC->SelectObject(pOldFont);
  1091. }
  1092.  
  1093. void CChartCtrl::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  1094. {
  1095.     m_PrinterFont.DeleteObject();
  1096.         // RefreshCtrl is needed because the print job
  1097.         // modifies the chart components (axis, ...)
  1098.         RefreshCtrl();
  1099. }
  1100.  
  1101. void CChartCtrl::GoToFirstSerie()
  1102. {
  1103.         m_currentSeries = m_mapSeries.begin();
  1104. }
  1105.  
  1106. CChartSerie* CChartCtrl::GetNextSerie()
  1107. {
  1108.         CChartSerie* pSeries = NULL;
  1109.         if (m_currentSeries != m_mapSeries.end())
  1110.         {
  1111.                 pSeries = m_currentSeries->second;
  1112.                 m_currentSeries++;
  1113.         }
  1114.  
  1115.         return pSeries;
  1116. }
  1117.  
  1118. #if _MFC_VER > 0x0600
  1119. void CChartCtrl::SaveAsImage(const TChartString& strFilename,
  1120.                                                          const CRect& rect,
  1121.                                                          int nBPP,
  1122.                                                          REFGUID guidFileType)
  1123. {
  1124.         CImage chartImage;
  1125.         CRect chartRect = rect;
  1126.         if (chartRect.IsRectEmpty())
  1127.         {
  1128.                 GetClientRect(&chartRect);
  1129.         }
  1130.    
  1131.         chartImage.Create(chartRect.Width(), chartRect.Height(), nBPP);
  1132.         CDC newDC;
  1133.         newDC.Attach(chartImage.GetDC());
  1134.  
  1135.         DrawBackground(&newDC, chartRect);
  1136.         chartRect.DeflateRect(3,3);
  1137.         DrawChart(&newDC, chartRect);
  1138.  
  1139.         newDC.Detach();
  1140.         chartImage.Save(strFilename.c_str(), guidFileType);
  1141.         chartImage.ReleaseDC();
  1142. }
  1143. #endif
  1144.  

回复 "函数绘图程序"

这儿你可以回复上面这条便签

captcha