ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  How to add customizable tooltip
Posted by Sai on Nov-14-2012 22:19
Attachments:
I want add customizable tooltip how do I do that?
ChartDirControl.cpp
// zoomscrolltrackDlg.cpp : implementation file
//

#include "stdafx.h"
#include "ChartDirSample.h"
#include "ChartDirControl.h"
#include "chartdir.h"
#include <math.h>
#include <vector>
#include <sstream>
#include <algorithm>

using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl dialog

//
// Constructor
//
CChartDirControl::CChartDirControl(CWnd* pParent /*=NULL*/)
    : CDialog(CChartDirControl::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_dYScale = m_dYScale2 = 0;
	m_eChartType = CHART_BAR;

	m_szTitleY = "Byte In & Out";
	m_szTitleY2 = "Total Byte";
}

//
// Destructor
//
CChartDirControl::~CChartDirControl()
{
    delete m_ranSeries;
    delete m_ChartViewer.getChart();
}

void CChartDirControl::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartDirControl)
	//DDX_Control(pDX, IDC_PointerPB, m_PointerPB);
	DDX_Control(pDX, IDC_HScrollBar, m_HScrollBar);
	DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
	//}}AFX_DATA_MAP
	DDX_Control(pDX, IDC_MFCLINK_AVG_RESPONSE, m_cAvgResponse);
	DDX_Control(pDX, IDC_MFCLINK_MIN, m_cMinLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_LINE, m_cLineLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_SERVER, m_cServerLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_AVG_RESPONSE2, m_cAvgResponse2);
	DDX_Control(pDX, IDC_MFCLINK_MIN2, m_cMinLinkButton2);
	DDX_Control(pDX, IDC_MFCLINK_LINE2, m_cLineLinkButton2);
	DDX_Control(pDX, IDC_MFCLINK_SERVER2, m_cServerLinkButton2);
}

BEGIN_MESSAGE_MAP(CChartDirControl, CDialog)
    //{{AFX_MSG_MAP(CChartDirControl)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_DESTROY()
    ON_WM_HSCROLL()
    ON_WM_MOUSEWHEEL()
    ON_CONTROL(CVN_ViewPortChanged, IDC_ChartViewer, OnViewPortChanged)
    ON_CONTROL(CVN_MouseMovePlotArea, IDC_ChartViewer, OnMouseMovePlotArea)
    //}}AFX_MSG_MAP
	//ON_BN_CLICKED(IDC_BUTTON_SCALEUP, &CChartDirControl::OnBnClickedButtonScaleup)
	//ON_BN_CLICKED(IDC_BUTTON_SCALEDOWN, &CChartDirControl::OnBnClickedButtonScaledown)
	ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_SCALEY1, &CChartDirControl::OnDeltaposSpinScaleY1)
	ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_SCALEY2, &CChartDirControl::OnDeltaposSpinScaleY2)
	ON_WM_SIZE()
	ON_BN_CLICKED(IDC_MFCLINK_AVG_RESPONSE, &CChartDirControl::OnBnClickedMfclinkAvgResponse)
	ON_BN_CLICKED(IDC_MFCLINK_AVG_RESPONSE2, &CChartDirControl::OnBnClickedMfclinkAvgResponse2)
	ON_WM_CTLCOLOR()
	ON_WM_ERASEBKGND()
	ON_NOTIFY(BCN_DROPDOWN, IDC_MFCLINK_AVG_RESPONSE2, &CChartDirControl::OnBnDropDownMfclinkAvgResponse2)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl message handlers

//
// Initialization
//
BOOL CChartDirControl::OnInitDialog()
{
    CDialog::OnInitDialog();

    // *** code automatically generated by VC++ MFC AppWizard ***
    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon
    
    //// Load icons to mouse usage buttons
    //loadButtonIcon(IDC_PointerPB, IDI_PointerPB, 100, 20);  
    //loadButtonIcon(IDC_ZoomInPB, IDI_ZoomInPB, 100, 20);    
    //loadButtonIcon(IDC_ZoomOutPB, IDI_ZoomOutPB, 100, 20);

	ResizeControls();
	
    return TRUE;
}

void CChartDirControl::InitChart()
{
	// Load the data
    loadData();

    // Initialize the CChartViewer
    initChartViewer(&m_ChartViewer);

    // Trigger the ViewPortChanged event to draw the chart
    m_ChartViewer.updateViewPort(true, true);
	return;
}
// *** code automatically generated by VC++ MFC AppWizard ***
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon.  For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CChartDirControl::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        //CDialog::OnPaint();
		CRect rcClient;
		GetClientRect(&rcClient);
        CPaintDC dc(this); // device context for painting
		dc.FillSolidRect(&rcClient,RGB(255,255,255));
    }
}

// *** code automatically generated by VC++ MFC AppWizard ***
// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CChartDirControl::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
}





//
// The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in or 
// out the chart by dragging or clicking on the chart. It can also be triggered by calling
// CChartViewer.updateViewPort.
//
void CChartDirControl::OnViewPortChanged()
{
    // In addition to updating the chart, we may also need to update other controls that
    // changes based on the view port.
    updateControls(&m_ChartViewer);

    // Update the chart if necessary
    if (m_ChartViewer.needUpdateChart())
        drawChart(&m_ChartViewer);

    // We need to update the track line too. If the mouse is moving on the chart (eg. if 
    // the user drags the mouse on the chart to scroll it), the track line will be updated
    // in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
	if (!m_ChartViewer.isInMouseMoveEvent()) 
	{
        trackLineLegend((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX());
        m_ChartViewer.updateDisplay();
    }
}

//
// User clicks on the the horizontal scroll bar 
//
void CChartDirControl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	double newViewPortLeft = moveScrollBar(nSBCode, nPos, pScrollBar);

	// Update the view port if the scroll bar has really moved
	if (newViewPortLeft != m_ChartViewer.getViewPortLeft()) 
	{
		m_ChartViewer.setViewPortLeft(moveScrollBar(nSBCode, nPos, pScrollBar));
		m_ChartViewer.updateViewPort(true, false);
	}
}

//
// The mouse wheel handler
//
BOOL CChartDirControl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// Process the mouse wheel only if the mouse is over the plot area
	if (!m_ChartViewer.isMouseOnPlotArea())
		return FALSE;

    // We zoom in or out by 10% depending on the mouse wheel direction.
	double newVpWidth = m_ChartViewer.getViewPortWidth() * (zDelta > 0 ? 0.9 : 1 / 0.9);
	double newVpHeight = m_ChartViewer.getViewPortHeight() * (zDelta > 0 ? 0.9 : 1 / 0.9);

    // We do not zoom beyond the zoom width or height limits.
    newVpWidth = max(m_ChartViewer.getZoomInWidthLimit(), min(newVpWidth,
        m_ChartViewer.getZoomOutWidthLimit()));
    newVpHeight = max(m_ChartViewer.getZoomInHeightLimit(), min(newVpWidth,
        m_ChartViewer.getZoomOutHeightLimit()));
	
	if ((newVpWidth != m_ChartViewer.getViewPortWidth()) || 
		(newVpHeight != m_ChartViewer.getViewPortHeight()))
	{
		// Set the view port position and size so that the point under the mouse remains under
		// the mouse after zooming.

		double deltaX = (m_ChartViewer.getPlotAreaMouseX() - m_ChartViewer.getPlotAreaLeft()) * 
			(m_ChartViewer.getViewPortWidth() - newVpWidth) / m_ChartViewer.getPlotAreaWidth();
		m_ChartViewer.setViewPortLeft(m_ChartViewer.getViewPortLeft() + deltaX);
		m_ChartViewer.setViewPortWidth(newVpWidth);

		double deltaY = (m_ChartViewer.getPlotAreaMouseY() - m_ChartViewer.getPlotAreaTop()) *
			(m_ChartViewer.getViewPortHeight() - newVpHeight) / m_ChartViewer.getPlotAreaHeight();
		m_ChartViewer.setViewPortTop(m_ChartViewer.getViewPortTop() + deltaY);
		m_ChartViewer.setViewPortHeight(newVpHeight);

	    m_ChartViewer.updateViewPort(true, false);
	}

	return TRUE;
}

//
// Draw track cursor when mouse is moving over plotarea
//
void CChartDirControl::OnMouseMovePlotArea()
{
    // Get the focus to ensure being able to receive mouse wheel events
    m_ChartViewer.SetFocus();

    trackLineLegend((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX()); 
    m_ChartViewer.updateDisplay();
}

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl methods

//
// Load the data
//
std::vector<std::string> buffer;
std::vector<const char *>labels;


//void CChartDirControl::loadData()
//{
//    // In this example, we just use random numbers as data.
//    m_ranSeries = new RanSeries(127);
//	
//	strStorage.resize(1827);
//	ptrStorage.resize(1827);
//	for (int i = 0; i < 1827; ++i)
//	{
//		ostringstream legendEntry;
//		legendEntry << "label " << i;
//		strStorage[i] = legendEntry.str();
//		ptrStorage[i] = strStorage[i].c_str();
//	}
//	m_timeStamps = StringArray(&(ptrStorage[0]), 1827);
//    m_dataSeriesA = m_ranSeries->getSeries(1827, 150, -10, 10);
//    m_dataSeriesB = m_ranSeries->getSeries(1827, 200, -10, 10);
//    m_dataSeriesC = m_ranSeries->getSeries(1827, 250, -8, 8);
//}

void CChartDirControl::loadData()
{
    // In this example, we just use random numbers as data.
    m_ranSeries = new RanSeries(127);

	map<CString,structByteInOut> mapData;
	ReadFile("DML4.txt",mapData);
	int nSize = mapData.size();
	
	map<CString,structByteInOut>::iterator it;
	int i = 0;
	CString szVerify;
	CString szTime;
	for ( it=mapData.begin() ; it != mapData.end(); it++,i++ )
	{
		structByteInOut stByteInOut;
		CString szDateTime = (*it).first;
		stByteInOut = (*it).second;
		

		m_dataSeriesA.push_back(stByteInOut.m_nByteIn);
		m_dataSeriesB.push_back(stByteInOut.m_nbyteOut);
		m_dataSeriesC.push_back((stByteInOut.m_nbyteOut + stByteInOut.m_nByteIn)/2);		
		m_timeStamps.push_back(atof(szDateTime));
		
	}
}
BOOL CChartDirControl::ReadFile(CString szFilePath,map<CString,structByteInOut> &mapData)
{
	CFile cFile(szFilePath,CFile::modeRead);
	CArchive ar(&cFile,CArchive::load);
	CString szText;
	ar.ReadString(szText);
	
	CString szTime,szByteIn,szByteOut;
	while(!szText.IsEmpty())
	{
		ar.ReadString(szText);
		int Position = 0;
		if(!szText.IsEmpty())
		{
			szTime = szText.Tokenize("\\t",Position);
			szByteIn = szText.Tokenize("\\t",Position);
			szByteOut = szText.Tokenize("\\t",Position);
			
			structByteInOut stByteInOut;
			stByteInOut.m_nByteIn = atoi(szByteIn);
			stByteInOut.m_nbyteOut = atoi(szByteOut);
			
			mapData.insert ( make_pair(szTime,stByteInOut) );
		}
	}
	
	ar.Flush();
	ar.Close();
	return TRUE;
}

//
// Initialize the CChartViewer 
//
void CChartDirControl::initChartViewer(CChartViewer *viewer)
{
    // Set the full x range to be the duration of the data
    viewer->setFullRange("x", 0, m_timeStamps/*m_sztimeArray*/.size() - 1);

    // Initialize the view port to show the latest 20% of the time range
    viewer->setViewPortWidth(0.1);
    viewer->setViewPortLeft(1 - viewer->getViewPortWidth());

    // Set the maximum zoom to 10 points
    viewer->setZoomInWidthLimit(10.0 / m_timeStamps/*m_sztimeArray*/.size());

    // Initially set the mouse to drag to scroll mode.
    //m_PointerPB.SetCheck(1);
    viewer->setMouseUsage(Chart::MouseUsageScroll);
}

//
// Initialize the CChartViewer 
//
void CChartDirControl::updateControls(CChartViewer *viewer)
{
    // In this demo, we need to update the scroll bar to reflect the view port position and
    // width of the view port.

    m_HScrollBar.EnableWindow(viewer->getViewPortWidth() < 1);
    if (viewer->getViewPortWidth() < 1)
    {
        SCROLLINFO info;
        info.cbSize = sizeof(SCROLLINFO);
        info.fMask = SIF_ALL;
        info.nMin = 0;
        info.nMax = 0x1fffffff;
	    info.nPage = (int)ceil(viewer->getViewPortWidth() * (info.nMax - info.nMin));
        info.nPos = (int)(0.5 + viewer->getViewPortLeft() * (info.nMax - info.nMin)) + info.nMin;
        m_HScrollBar.SetScrollInfo(&info);
    }
}

//
// Handle scroll bar events
//
double CChartDirControl::moveScrollBar(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    //
    // Get current scroll bar position
    //
    SCROLLINFO info;
    info.cbSize = sizeof(SCROLLINFO);
    info.fMask = SIF_ALL;
    pScrollBar->GetScrollInfo(&info);

    //
    // Compute new position based on the type of scroll bar events
    //
    int newPos = info.nPos;
    switch (nSBCode)
    {
    case SB_LEFT:
        newPos = info.nMin;
        break;
    case SB_RIGHT:
        newPos = info.nMax;
        break;
    case SB_LINELEFT:
        newPos -= (info.nPage > 10) ? info.nPage / 10 : 1;
        break;
    case SB_LINERIGHT:
        newPos += (info.nPage > 10) ? info.nPage / 10 : 1;
        break;
    case SB_PAGELEFT:
        newPos -= info.nPage;
        break;
    case SB_PAGERIGHT:
        newPos += info.nPage;
        break;
    case SB_THUMBTRACK:
        newPos = info.nTrackPos;
        break;
    }
    if (newPos < info.nMin) newPos = info.nMin;
    if (newPos > info.nMax) newPos = info.nMax;
    
    // Update the scroll bar with the new position
    pScrollBar->SetScrollPos(newPos);

    // Returns the position of the scroll bar as a ratio of its total length
    return ((double)(newPos - info.nMin)) / (info.nMax - info.nMin);
}

//
// Draw the chart and display it in the given viewer
//
void CChartDirControl::drawChart(CChartViewer *viewer)
{
    // Get the array indexes that corresponds to the visible start and end dates
    int startIndex = (int)floor(viewer->getValueAtViewPort("x", viewer->getViewPortLeft()));
	int endIndex = (int)ceil(viewer->getValueAtViewPort("x", viewer->getViewPortLeft() +
        viewer->getViewPortWidth()));
    int noOfPoints = endIndex - startIndex + 1;
	int nScroll = m_HScrollBar.GetScrollPos();

    // Extract the part of the data array that are visible.
    DoubleArray viewPortTimeStamps = DoubleArray(&m_timeStamps[0]/*m_sztimeArray*/ + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesA = DoubleArray(&m_dataSeriesA[0] + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesB = DoubleArray(&m_dataSeriesB[0] + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesC = DoubleArray(&m_dataSeriesC[0] + startIndex, noOfPoints);

    //
    // At this stage, we have extracted the visible data. We can use those data to plot the chart.
    //

	///////////////////////////////////////////////////////////////////////////////////////
    // Configure overall chart appearance. 
    ///////////////////////////////////////////////////////////////////////////////////////

	CRect rcClient;
	m_ChartViewer.GetClientRect(&rcClient);

    // Create an XYChart object of size 650 x 350 pixels, with a white (ffffff) background and grey 
	// (aaaaaa) border
    //XYChart *c = new XYChart(650 + 20, 350 + 100, 0xffffff, 0xaaaaaa);
//	XYChart *c = new XYChart(rcClient.Width(), rcClient.Height(), 0xffffff, 0xaaaaaa);
	XYChart *c = new XYChart(rcClient.Width(), rcClient.Height(), RGB(255,255,255)/*Chart::brushedSilverColor()*/,
        Chart::Transparent,0);
	
    // Set the plotarea at (55, 50) with width 90 pixels less than chart width, and height 115 pixels
    // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff)
    // as background. Set border to transparent and grid lines to white (ffffff).
  //  c->setPlotArea(55, 50, c->getWidth() - 90 - 20, c->getHeight() - 115 - 20, c->linearGradientColor(0, 55, 0, 
		//c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff);
	    c->setPlotArea(55, 25, c->getWidth() - 90 - 20, c->getHeight() - 50, RGB(255,255,255), -1, Chart::Transparent, RGB(200,200,200), RGB(200,200,200));

    // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping.
    c->setClipping();

    // Add a title to the chart using 18 pts Times New Roman Bold Italic font
    //c->addTitle("   Zooming & Scrolling Sample", "timesbi.ttf", 18);<<

    // Set legend icon style to use line style icon, sized for 8pt font
    c->getLegend()->setLineStyleKey();
    c->getLegend()->setFontSize(8);

    // Set the axis stem to transparent
    c->xAxis()->setColors(Chart::Transparent);
    c->yAxis()->setColors(Chart::Transparent);

    // Add axis title using 10pts Arial Bold Italic font
    c->yAxis()->setTitle(m_szTitleY, "arialbi.ttf", 8);
    c->yAxis2()->setTitle(m_szTitleY2, "arialbi.ttf", 8);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Add data to chart
    ///////////////////////////////////////////////////////////////////////////////////////

    // 
    // In this example, we represent the data by lines. You may modify the code below to use other
	// representations (areas, scatter plot, etc).
    //
	Layer *pBaseLayer = NULL,*pBaseLayer2 = NULL;
    // Add a line layer for the lines, using a line width of 2 pixels
	if(m_eChartType == CHART_LINE)
	{
		LineLayer *layer = c->addLineLayer();
		pBaseLayer = layer;
		LineLayer *layer2 = c->addLineLayer();
		pBaseLayer2 = layer2;

		layer->setLineWidth(1);
		layer2->setLineWidth(1);
		layer->setFastLineMode();
		layer2->setFastLineMode();
	}
	else if(m_eChartType == CHART_BAR)
	{
		BarLayer *layer = c->addBarLayer();
		pBaseLayer = layer;
		BarLayer *layer2 = c->addBarLayer();
		pBaseLayer2 = layer2;

		layer->setLineWidth(1);
		layer2->setLineWidth(1);
		layer->setBarShape(Chart::CircleShape);
		layer2->setBarShape(Chart::CircleShape);
	}
	else if(m_eChartType == CHART_SPINE)
	{
		SplineLayer *layer = c->addSplineLayer();
		pBaseLayer = layer;
		SplineLayer *layer2 = c->addSplineLayer();
		pBaseLayer2 = layer2;

		layer->setLineWidth(1);
		layer2->setLineWidth(1);
		layer->setFastLineMode();
		layer2->setFastLineMode();
	}

	//LineLayer *layer2 = c->addLineLayer();
	BarLayer *layer2 = c->addBarLayer();
	layer2->setBarShape(Chart::CircleShape);
    layer2->setLineWidth(1);
	//layer2->setBarWidth(4);

    // In this demo, we do not have too many data points. In real code, the chart may contain a lot
    // of data points when fully zoomed out - much more than the number of horizontal pixels in this
    // plot area. So it is a good idea to use fast line mode.
    

    // Now we add the 3 data series to a line layer, using the color red (ff0000), green
    // (00cc00) and blue (0000ff)
    pBaseLayer->addDataSet(viewPortDataSeriesA, 0xff3333);
    pBaseLayer->addDataSet(viewPortDataSeriesB, 0x008800);
    pBaseLayer2->addDataSet(viewPortDataSeriesC, 0x3333CC);
	pBaseLayer2->setUseYAxis2();

    ///////////////////////////////////////////////////////////////////////////////////////
    // Configure axis scale and labelling
    ///////////////////////////////////////////////////////////////////////////////////////
    
    // Set the x-axis as a date/time axis with the scale according to the view port x range.
    //c->xAxis()->setLabels(viewPortTimeStamps);
	int nStep = c->getWidth() / 50;
	//c->xAxis()->setLabelStep(viewPortTimeStamps.len / nStep/*20*/ + 1);

	//<<
	pBaseLayer->setXData(viewPortTimeStamps);
	pBaseLayer2->setXData(viewPortTimeStamps);
	c->xAxis()->setDateScale(viewPortTimeStamps[0], viewPortTimeStamps[noOfPoints-1]);
	//if ( (viewPortTimeStamps[0] - viewPortTimeStamps[noOfPoints-1]) * viewPortTimeStamps.len / 1000 <= 20 * 60)
	//	c->xAxis()->setLabelFormat("{value|hh:nn}");
	//else
	//	c->xAxis()->setLabelFormat("{value|nn:ss}");
	c->xAxis()->setFormatCondition(">", 20*60);
	c->xAxis()->setLabelFormat("{value|hh:nn}");
	c->yAxis()->setFormatCondition("else");
	c->yAxis()->setLabelFormat("{value|nn:ss}");
	//<<


	//c->xAxis()->setLabelStyle("arial.ttf", 8, Chart::TextColor, 90);
	CString szZoom;
	szZoom.Format(" %f \\n",m_ChartViewer.getZoomInHeightLimit());
	OutputDebugString(szZoom);
	c->yAxis()->setFormatCondition(">", 1000*1000);
	c->yAxis()->setLabelFormat("{={value}/1000000}M");
    c->yAxis()->setFormatCondition(">", 1000);
	c->yAxis()->setLabelFormat("{={value}/1000}K");
	c->yAxis()->setFormatCondition("else");
	c->yAxis()->setLabelFormat("{={value}}");

	c->yAxis()->setWidth(2);
	c->yAxis2()->setWidth(2);
	c->yAxis()->setColors(RGB(0,0,0));
	c->yAxis2()->setColors(RGB(255,0,0));
	c->yAxis2()->setColors(0xbb0000);
	c->yAxis2()->setFormatCondition(">", 1000*1000);
	c->yAxis2()->setLabelFormat("{={value}/1000000}M");
    c->yAxis2()->setFormatCondition(">", 1000);
	c->yAxis2()->setLabelFormat("{={value}/1000}K");
	c->yAxis2()->setFormatCondition("else");
	c->yAxis2()->setLabelFormat("{={value}}");

	if(m_dYScale)
		c->yAxis()->setLinearScale(0,m_dYScale);
	if(m_dYScale2)
		c->yAxis2()->setLinearScale(0,m_dYScale2);
    ///////////////////////////////////////////////////////////////////////////////////////
    // Output the chart
    ///////////////////////////////////////////////////////////////////////////////////////
	// Include tool tip for the chart
    m_ChartViewer.setImageMap(
    	c->getHTMLImageMap("", "", "title='{xLabel}: US${value}K'"));
    delete viewer->getChart();
    viewer->setChart(c);
}

//
// Draw the track line with legend
//
void CChartDirControl::trackLineLegend(XYChart *c, int mouseX)
{
    // Clear the current dynamic layer and get the DrawArea object to draw on it.
    DrawArea *d = c->initDynamicLayer();

    // The plot area object
    PlotArea *plotArea = c->getPlotArea();

    // Get the data x-value that is nearest to the mouse, and find its pixel coordinate.
    double xValue = c->getNearestXValue(mouseX);
    int xCoor = c->getXCoor(xValue);

    // Draw a vertical track line at the x-position
    d->vline(plotArea->getTopY(), plotArea->getBottomY(), xCoor, d->dashLineColor(0x000000, 0x0101));

    // Container to hold the legend entries
	vector<string> legendEntries;

    // Iterate through all layers to build the legend array
    for (int i = 0; i < c->getLayerCount(); ++i) {
        Layer *layer = c->getLayerByZ(i);

        // The data array index of the x-value
        int xIndex = layer->getXIndexOf(xValue);

        // Iterate through all the data sets in the layer
        for (int j = 0; j < layer->getDataSetCount(); ++j) {
            DataSet *dataSet = layer->getDataSetByZ(j);

            // We are only interested in visible data sets with names
            const char *dataName = dataSet->getDataName();
            int color = dataSet->getDataColor();
			if (dataName && *dataName && (color != Chart::Transparent)) {
                // Build the legend entry, consist of the legend icon, name and data value.
                double dataValue = dataSet->getValue(xIndex);
				ostringstream legendEntry;
				legendEntry << "<*block*>" << dataSet->getLegendIcon() << " " << dataName << ": " <<
					((dataValue == Chart::NoValue) ? "N/A" : c->formatValue(dataValue, "{value|P4}"))
					<< "<*/*>";
				legendEntries.push_back(legendEntry.str());

                // Draw a track dot for data points within the plot area
                int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());
                if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY())) {
                    d->circle(xCoor, yCoor, 4, 4, color, color);
                }
            }
        }
    }

    // Create the legend by joining the legend entries
	ostringstream legendText;
	legendText << "<*block,maxWidth=" << plotArea->getWidth() << "*><*block*><*font=arialbd.ttf*>["
		<< c->xAxis()->getFormattedLabel(xValue, "mmm dd, yyyy") << "]<*/*>";
	for (int i = ((int)legendEntries.size()) - 1; i >= 0; --i)
		legendText << "        " << legendEntries[i];
	
    // Display the legend on the top of the plot area
    TTFText *t = d->text(legendText.str().c_str(), "arial.ttf", 8);
	t->draw(plotArea->getLeftX() + 5, plotArea->getTopY() - 3, 0x000000, Chart::BottomLeft);
	t->destroy();
}

/////////////////////////////////////////////////////////////////////////////
// General utilities

//
// Load an icon resource into a button
//
void CChartDirControl::loadButtonIcon(int buttonId, int iconId, int width, int height)
{
    GetDlgItem(buttonId)->SendMessage(BM_SETIMAGE, IMAGE_ICON, (LPARAM)::LoadImage(
        AfxGetResourceHandle(), MAKEINTRESOURCE(iconId), IMAGE_ICON, width, height, 
        LR_DEFAULTCOLOR));  
}

void CChartDirControl::OnBnClickedButtonScaleup()
{
	// TODO: Add your control notification handler code here
	

}



void CChartDirControl::OnBnClickedButtonScaledown()
{
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	//pXYChart->layoutAxes();
	m_dYScale = pXYChart->yAxis()->getMaxValue();
	double dYScale = m_dYScale;
	m_dYScale -= (m_dYScale * .20);
	if(m_dYScale < 1)
		m_dYScale = dYScale;
	m_ChartViewer.updateViewPort(true, true);
}


void CChartDirControl::OnDeltaposSpinScaleY1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	if(!m_dYScale)
	{
		pXYChart->layoutAxes();
		m_dYScale = pXYChart->yAxis()->getMaxValue();
	}
	if(pNMUpDown->iDelta > 0)
	{
		
		//m_dYScale = pXYChart->yAxis()->getMaxValue();
		m_dYScale += (m_dYScale * .10);	
	}
	else
	{
		//m_dYScale = pXYChart->yAxis()->getMaxValue();
		m_dYScale -= (m_dYScale * .20);	
	}
	m_ChartViewer.updateViewPort(true, true);
	*pResult = 0;
}


void CChartDirControl::OnDeltaposSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	if(!m_dYScale2)
	{
		pXYChart->layoutAxes();
		m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
	}
	if(pNMUpDown->iDelta > 0)
	{
		
		//m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
		m_dYScale2 += (m_dYScale2 * .10);	
	}
	else
	{
		//m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
		m_dYScale2 -= (m_dYScale2 * .20);	
	}
	m_ChartViewer.updateViewPort(true, true);
	*pResult = 0;
}


void CChartDirControl::ResizeControls()
{
	CWnd *pWnd = GetDlgItem(IDC_ChartViewer);
	CRect rcWndRect,rcParent;
	int nSpace = 3;
	if(pWnd->GetSafeHwnd())
	{
		int nX = rcParent.left + nSpace;
		int nY = rcParent.top + nSpace;
		GetClientRect(&rcParent);
		//<<
		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();
		
		pWnd = GetDlgItem(IDC_STATIC1);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();
	
		pWnd = GetDlgItem(IDC_MFCLINK_MIN);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();

		pWnd = GetDlgItem(IDC_STATIC2);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();

		pWnd = GetDlgItem(IDC_MFCLINK_LINE);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width() + 50;

		//***
		pWnd = GetDlgItem(IDC_MFCLINK_LINE2);
		pWnd->GetWindowRect(&rcWndRect);
		nX = rcParent.right - nSpace - rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		

		pWnd = GetDlgItem(IDC_STATIC4);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_MFCLINK_MIN2);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_STATIC3);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE2);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		pWnd->GetWindowRect(&rcWndRect);
		ScreenToClient(&rcWndRect);
		nX = rcWndRect.left;
		nY = rcWndRect.bottom;
		
		pWnd = GetDlgItem(IDC_MFCLINK_SERVER2);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE);
		pWnd->GetWindowRect(&rcWndRect);
		ScreenToClient(&rcWndRect);
		nX = rcWndRect.left;
		nY = rcWndRect.bottom;
		pWnd = GetDlgItem(IDC_MFCLINK_SERVER);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		//>>
		rcParent.DeflateRect(25,10+rcWndRect.Height()*2,25,25);
		pWnd = GetDlgItem(IDC_ChartViewer);
		pWnd->MoveWindow(rcParent);

		CRect rcChartView;
		m_ChartViewer.GetClientRect(&rcChartView);
		m_ChartViewer.MoveWindow(rcParent);

		pWnd = GetDlgItem(IDC_SPIN_SCALEY1);
		pWnd->GetClientRect(&rcWndRect);
		pWnd->MoveWindow(rcParent.left - rcWndRect.Width() - nSpace,rcParent.top,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_SPIN_SCALEY2);
		pWnd->GetClientRect(&rcWndRect);
		pWnd->MoveWindow(rcParent.right + nSpace,rcParent.top,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_HScrollBar);
		pWnd->GetClientRect(&rcWndRect);
		pWnd->MoveWindow(rcParent.left,rcParent.bottom + nSpace,rcParent.Width(),rcWndRect.Height());

		if(m_ChartViewer.getChart())
			m_ChartViewer.updateViewPort(true, true);

	}
	return;
}

void CChartDirControl::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	// TODO: Add your message handler code here
	ResizeControls();
}

void CChartDirControl::SetChartType(CHART_TYPE eChartType)
{
	m_eChartType = eChartType;
	m_ChartViewer.updateViewPort(1,1);
}

void CChartDirControl::SetTitleAxixY(CString szTitle)
{
	m_szTitleY = szTitle;
}

void CChartDirControl::SetTitleAxixY2(CString szTitle)
{
	m_szTitleY2 = szTitle;
}

void CChartDirControl::SetChartTitle(CString szTitle)
{
	m_szChartTitle = szTitle;
}


void CChartDirControl::OnBnClickedMfclinkAvgResponse()
{
	// TODO: Add your control notification handler code here m_szChartTitle
	AfxMessageBox("What To do in OnBnClickedMfclinkAvgResponse()");
}


void CChartDirControl::OnBnClickedMfclinkAvgResponse2()
{
	// TODO: Add your control notification handler code here
	AfxMessageBox("What To do in OnBnClickedMfclinkAvgResponse2()");
}


HBRUSH CChartDirControl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  Change any attributes of the DC here
	if(pWnd->GetDlgCtrlID() == IDC_STATIC1 || pWnd->GetDlgCtrlID() == IDC_STATIC2 || pWnd->GetDlgCtrlID() == IDC_STATIC3
		 || pWnd->GetDlgCtrlID() == IDC_STATIC4)
    {
        pDC->SetBkMode(TRANSPARENT);
        return reinterpret_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
    }

	else if(pWnd->GetDlgCtrlID() == IDC_MFCLINK_AVG_RESPONSE || pWnd->GetDlgCtrlID() == IDC_MFCLINK_MIN 
		|| pWnd->GetDlgCtrlID() == IDC_MFCLINK_LINE
		 || pWnd->GetDlgCtrlID() == IDC_MFCLINK_AVG_RESPONSE2 || pWnd->GetDlgCtrlID() == IDC_MFCLINK_MIN2 
		|| pWnd->GetDlgCtrlID() == IDC_MFCLINK_LINE2)
    {
		
        pDC->SetBkMode(TRANSPARENT);
        return reinterpret_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
    }
   
        return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
	// TODO:  Return a different brush if the default is not desired
	return hbr;
}


BOOL CChartDirControl::OnEraseBkgnd(CDC* pDC)
{
	//// TODO: Add your message handler code here and/or call default
	
	return CDialog::OnEraseBkgnd(pDC);
}


void CChartDirControl::OnBnDropDownMfclinkAvgResponse2(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMBCDROPDOWN pDropDown = reinterpret_cast<LPNMBCDROPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	*pResult = 0;
}
ChartDirControl.h
// zoomscrolltrackDlg.h : header file
//

#pragma once

#include "ChartViewer.h"
#include <afxmt.h>
#include <vector>
#include <string>
#include "IWLinkCtrlButton.h"
#include "afxlinkctrl.h"

// CChartDirControl dialog
class CChartDirControl : public CDialog
{
// Construction
public:
	CChartDirControl(CWnd* pParent = NULL);	// standard constructor
	~CChartDirControl();

// Dialog Data
	enum { IDD = IDD_EXACT_ZOOMSCROLL };
	CButton m_PointerPB;
	CScrollBar m_HScrollBar;
	CChartViewer m_ChartViewer;

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support


// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnPointerPB();
	afx_msg void OnZoomInPB();
	afx_msg void OnZoomOutPB();
	afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
	afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
	afx_msg void OnViewPortChanged();	
	afx_msg void OnMouseMovePlotArea();
	DECLARE_MESSAGE_MAP()


private:
	//
	// Data arrays for the scrollable / zoomable chart.
	// - In this demo, we just use a RanSeries object to generate random data for the chart.
	//
	RanSeries *m_ranSeries;
	vector<double> m_timeStamps;
	std::vector<std::string> strStorage;
	std::vector<const char *> ptrStorage;
	vector<double> m_dataSeriesA;
	vector<double> m_dataSeriesB;
	vector<double> m_dataSeriesC;
	CString m_szTitleY,m_szTitleY2;
	CString m_szChartTitle;
	
 	// Load data into data arrays
	void loadData();
	
    // Initialize the CChartViewer
    void initChartViewer(CChartViewer *viewer);

	// Draw chart
	void drawChart(CChartViewer *viewer);
    void trackLineLegend(XYChart *c, int mouseX);

	// Moves the scroll bar when the user clicks on it
	double moveScrollBar(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

	// Update controls when the view port changed
    void updateControls(CChartViewer *viewer);

	// utility to load icon resource to a button
	void loadButtonIcon(int buttonId, int iconId, int width, int height);
	BOOL ReadFile(CString szFilePath,map<CString,structByteInOut> &mapData);
	StringArray m_sztimeArray;
public:
	afx_msg void OnBnClickedButtonScaleup();
	afx_msg void OnBnClickedButtonScaledown();
	double m_dYScale,m_dYScale2;
	afx_msg void OnDeltaposSpinScaleY1(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnDeltaposSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMReleasedcaptureSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMThemeChangedSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnSize(UINT nType, int cx, int cy);
	void ResizeControls();
	void InitChart();
	void SetChartType(CHART_TYPE eChartType);
	CHART_TYPE m_eChartType;

	void SetTitleAxixY(CString szTitle);
	void SetTitleAxixY2(CString szTitle);
	void SetChartTitle(CString szTitle);
	afx_msg void OnBnClickedMfclinkAvgResponse();
	afx_msg void OnBnClickedMfclinkAvgResponse2();
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnBnDropDownMfclinkAvgResponse2(NMHDR *pNMHDR, LRESULT *pResult);
	IWLinkCtrlButton m_cAvgResponse,m_cAvgResponse2;
	IWLinkCtrlButton m_cMinLinkButton,m_cMinLinkButton2;
	IWLinkCtrlButton m_cLineLinkButton,m_cLineLinkButton2;
	IWLinkCtrlButton m_cServerLinkButton,m_cServerLinkButton2;
};

  Re: How to add customizable tooltip
Posted by Peter Kwan on Nov-15-2012 04:09
Hi Sai,

Your code has already included the tooltip:

    m_ChartViewer.setImageMap(
     c->getHTMLImageMap("", "", "title='{xLabel}: US${value}K'"));

However, the above may have no effect, because after including the tooltip, your code changes the chart by executing viewer->setChart(c). Changing a chart cancels the tooltips. (The tooltips applies to the "current chart" when setImageMap is called, not a chart in the future.) For the tooltip to work, please apply it after viewer->setChart(c).

One issue with tooltips is that if the chart has many data points, the tooltips may become large and make take something like 0.1 second to process. If your chart is updating very fast (like using mouse wheel to zoom in/out, using scroll bars, etc., which may update the chart many times per second), the 0.1 second in tooltips processing may become significant.

On the other hand, most of the tooltips update are not necessary. The tooltips are really only needed when the chart is "stable" and when the mouse is really moving over the chart.  So one method you may try is:

- After viewer->setChart, stores the chart update time (obtained using GetTickCount) in a member variable.

- In MouseMovePlotArea, check if the chart has not been updated for more than 1 second and if the image map handler (obtained using viewer->getImageMapHandler()) is NULL. If both conditions are met, update the image map.

Hope this can help.

Regards
Peter Kwan

  Re: How to add customizable tooltip
Posted by Sai on Nov-16-2012 18:11
Hi Peter,

I tried
m_ChartViewer.setImageMap(c->getHTMLImageMap("", "", "title='{yLabel}: {value}'"));

after setting chart  it works

But I want both X & Y values and that should be customizable how do I do that?


Regards,
Sai

Peter Kwan wrote:

Hi Sai,

Your code has already included the tooltip:

    m_ChartViewer.setImageMap(
     c->getHTMLImageMap("", "", "title='{xLabel}: US${value}K'"));

However, the above may have no effect, because after including the tooltip, your code changes the chart by executing viewer->setChart(c). Changing a chart cancels the tooltips. (The tooltips applies to the "current chart" when setImageMap is called, not a chart in the future.) For the tooltip to work, please apply it after viewer->setChart(c).

One issue with tooltips is that if the chart has many data points, the tooltips may become large and make take something like 0.1 second to process. If your chart is updating very fast (like using mouse wheel to zoom in/out, using scroll bars, etc., which may update the chart many times per second), the 0.1 second in tooltips processing may become significant.

On the other hand, most of the tooltips update are not necessary. The tooltips are really only needed when the chart is "stable" and when the mouse is really moving over the chart.  So one method you may try is:

- After viewer->setChart, stores the chart update time (obtained using GetTickCount) in a member variable.

- In MouseMovePlotArea, check if the chart has not been updated for more than 1 second and if the image map handler (obtained using viewer->getImageMapHandler()) is NULL. If both conditions are met, update the image map.

Hope this can help.

Regards
Peter Kwan

  Re: How to add customizable tooltip
Posted by Peter Kwan on Nov-17-2012 01:28
Hi Sai,

For your case, the x-value is {x}, and the y-value is {value}. For example:

m_ChartViewer.setImageMap(c->getHTMLImageMap("", "", "title='{x|mmm dd, yyyy}: {value|P4}'"));

I am not sure how you would like to customize the tooltips. If you just want to customize the formatting, you can always modify the formatting mmm dd, yyyy or P4 to some other format. If you would like some specify formatting or display some special variable text (like displaying some special events on certain dates), you would need to use extra fields (see BaseChart.addExtraField).

Hope this can help.

Regards
Peter Kwan