ChartDirector 6.0 (C++ Edition)

Crosshair with Axis Labels (MFC)




NOTE: This section describes Crosshair with Axis Labels for MFC, and is available on the Windows edition of ChartDirector for C++ only. For QT, please refer to Crosshair with Axis Labels (QT).

This sample program demonstrates a track cursor programmed as a crosshair cursor with axis labels.

The code first draws the chart. Then in the CVN_MouseMovePlotArea message handler, the track cursor is drawn to reflect the mouse position. The track cursor is configured to automatically hide itself when the mouse leaves the plot area.

The crossHair method is the routine that draws the track cursor. Its key elements are:

Source Code Listing

The following is the main source code of this demo. The complete MFC project is in "mfcdemo/crosshair".

[File: mfcdemo/crosshair/crosshairDlg.cpp]
// crosshairDlg.cpp : implementation file
//

#include "stdafx.h"
#include "crosshair.h"
#include "crosshairDlg.h"
#include "chartdir.h"
#include <sstream>

using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CcrosshairDlg dialog

CcrosshairDlg::CcrosshairDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CcrosshairDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

CcrosshairDlg::~CcrosshairDlg()
{
    delete m_ChartViewer.getChart();
}

void CcrosshairDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
}

BEGIN_MESSAGE_MAP(CcrosshairDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_ERASEBKGND()
    ON_CONTROL(CVN_MouseMovePlotArea, IDC_ChartViewer, OnMouseMovePlotArea)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()


// CcrosshairDlg message handlers

BOOL CcrosshairDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 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

    drawChart(&m_ChartViewer);

    CRect winSize;
    m_ChartViewer.GetWindowRect(winSize);
    m_ChartViewer.MoveWindow(5, 5, winSize.Width(), winSize.Height());

    CalcWindowRect(&winSize, CWnd::adjustBorder);
    SetWindowPos(&wndTop, 0, 0, winSize.Width() + 10, winSize.Height() + 10, SWP_NOMOVE);
 
    return TRUE;  // return TRUE  unless you set the focus to a control
}

// 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 CcrosshairDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<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();
    }
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CcrosshairDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

//
// Handle the ON_WM_ERASEBKGND message to set background color
//
BOOL CcrosshairDlg::OnEraseBkgnd(CDC* pDC) 
{ 
    // Set brush to desired background color 
    CBrush backBrush(RGB(255, 255, 255)); 

    // Save old brush 
    CBrush* pOldBrush = pDC->SelectObject(&backBrush); 

    // Erase the area needed with the given background color
    CRect rect; 
    pDC->GetClipBox(&rect);
    pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); 

    // Restore old brush and exit
    pDC->SelectObject(pOldBrush); 
    return TRUE; 
} 

//
// Draw track cursor when mouse is moving over plotarea
//
void CcrosshairDlg::OnMouseMovePlotArea()
{
    // Draw crosshair track cursor
    crosshair((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX(),
        m_ChartViewer.getPlotAreaMouseY());
    m_ChartViewer.updateDisplay();

    // Hide the track cursor when the mouse leaves the plot area
    m_ChartViewer.removeDynamicLayer(CVN_MouseLeavePlotArea);
}

/////////////////////////////////////////////////////////////////////////////
// CcrosshairDlg methods

//
// Draw the chart and display it in the given viewer
//
void CcrosshairDlg::drawChart(CChartViewer *viewer)
{
    // The XY data of the first data series
    double dataX[] = {50, 55, 37, 24, 42, 49, 63, 72, 83, 59};
    double dataY[] = {3.6, 2.8, 2.5, 2.3, 3.8, 3.0, 3.8, 5.0, 6.0, 3.3};
    int noOfPoints = (int)(sizeof(dataX) / sizeof(*dataX));

    // Create a XYChart object of size 520 x 490 pixels
    XYChart *c = new XYChart(520, 490);

    // Set the plotarea at (60, 40) and of size 450 x 400 pixels, with white background and a light
    // grey border (0xc0c0c0). Turn on both horizontal and vertical grid lines with light grey color
    // (0xc0c0c0)
    c->setPlotArea(60, 40, 450, 400, 0xffffff, -1, 0xc0c0c0, 0xc0c0c0, -1);

    // Add a title to the chart using 18 point Times Bold Itatic font.
    c->addTitle("      Chemical X Thermal Conductivity", "timesbi.ttf", 18);

    // Add titles to the axes using 12 pts Arial Bold Italic font
    c->yAxis()->setTitle("Thermal Conductivity (W/K)", "arialbi.ttf", 12);
    c->xAxis()->setTitle("Concentration (g/liter)", "arialbi.ttf", 12);

    // Set the axes line width to 3 pixels
    c->yAxis()->setWidth(3);
    c->xAxis()->setWidth(3);

    // Add a scatter layer using (dataX, dataY)
    ScatterLayer *scatterLayer = c->addScatterLayer(DoubleArray(dataX, noOfPoints), 
        DoubleArray(dataY, noOfPoints), "", Chart::GlassSphereShape, 13, 0xcc0000);

    // Tooltip for the scatter layer
    scatterLayer->setHTMLImageMap("", "",
        "title='Concentration: {x} g/liter, Conductivity: {value} W/K'");

    // Add a trend line layer for (dataX, dataY)
    TrendLayer *trendLayer = c->addTrendLayer(DoubleArray(dataX, noOfPoints), 
        DoubleArray(dataY, noOfPoints), 0xcc0000);

    // Set the line width to 3 pixels
    trendLayer->setLineWidth(3);

    // Add a 95% confidence band for the line
    trendLayer->addConfidenceBand(0.95, 0x806666ff);

    // Add a 95% confidence band (prediction band) for the points
    trendLayer->addPredictionBand(0.95, 0x8066ff66);

    // Tool tip for the trend layer
    trendLayer->setHTMLImageMap("", "", "title='Trend Line: y = {slope|P4} x + {intercept|P4}'");

    // Add a legend box at (60, 35) (top of the chart) with horizontal layout. Use 10 pts Arial Bold
    // Italic font. Set the background and border color to Transparent and use line style legend
    // icons.
    LegendBox *legendBox = c->addLegend(60, 35, false, "arialbi.ttf", 9);
    legendBox->setBackground(Chart::Transparent);
    legendBox->setLineStyleKey(true);

    // Add entries to the legend box
    legendBox->addKey("95% Line Confidence", 0x806666ff);
    legendBox->addKey("95% Point Confidence", 0x8066ff66);

    ostringstream trendLegend;
    trendLegend << "Trend Line: y = " << c->formatValue(trendLayer->getSlope(), "{value|4}") << " x";
    trendLegend << " + " << c->formatValue(trendLayer->getIntercept(), "{value|4}");
    legendBox->addKey(trendLegend.str().c_str(), 0xcc0000, 3);

    // Assign the chart to the WinChartViewer
    viewer->setChart(c);

    // Include tool tip for the chart
    viewer->setImageMap(c->getHTMLImageMap(""));
}

//
// Draw cross hair cursor with axis labels
//
void CcrosshairDlg::crosshair(XYChart *c, int mouseX, int mouseY)
{
    // 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();

    // Draw a vertical line and a horizontal line as the cross hair
    d->vline(plotArea->getTopY(), plotArea->getBottomY(), mouseX, d->dashLineColor(0x000000, 0x0101));
    d->hline(plotArea->getLeftX(), plotArea->getRightX(), mouseY, d->dashLineColor(0x000000, 0x0101));

    // Draw y-axis label
    ostringstream ylabel;
    ylabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getYValue(
        mouseY, c->yAxis()), "{value|P4}") << "<*/*>";
    TTFText *t = d->text(ylabel.str().c_str(), "arialbd.ttf", 8);
    t->draw(plotArea->getLeftX() - 5, mouseY, 0x000000, Chart::Right);
    t->destroy();

    // Draw x-axis label
    ostringstream xlabel;
    xlabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getXValue(
        mouseX), "{value|P4}") << "<*/*>";
    t = d->text(xlabel.str().c_str(), "arialbd.ttf", 8);
    t->draw(mouseX, plotArea->getBottomY() + 5, 0x000000, Chart::Top);
    t->destroy();
}