Click here to Skip to main content
15,868,292 members
Articles / Desktop Programming / MFC
Article

Avoiding the MSChart EditCopy / Legend bug in MFC

Rate me:
Please Sign up or sign in to vote.
3.67/5 (2 votes)
1 Feb 20053 min read 65.4K   2.5K   19   5
A workaround to the ActiveX MSChart control EditCopy / Legend bug in MFC.

Sample Image

Introduction

The code provided in this article proposes a workaround to the problem with trying to export charts created with the ActiveX control (MSChart) using the EditCopy method. The problem is, when using the EditCopy method to copy the chart to memory, this method does not export the text in the legend. Instead, it is replaced with "C1", "C2", etc... This issue is acknowledged by Microsoft, which has a page describing the problem and a solution, however they only provide a workaround for Visual Basic 5/6. No solution has been provided for those using this ActiveX control in a MFC project using Visual C++ 6.

As the project I was working on used MSChart frequently and because MSChart is actually quite useful, it was not possible to replace all of it with another type of chart. Hence, I had to workaround this problem and develop a method of avoiding the EditCopy bug.

Background

Fundamentally, everything in Windows is, if you can imagine it, a sort of canvas. Hence, any object on the screen can be referenced to and then extracted as a bitmap. You must determine the coordinates of the object and use the BitBlt function of the Windows API to directly copy that block to memory. It is a bit like taking a screenshot, except that you are only taking a shot of a certain area.

Using the code

As previously mentioned, one would normally use the EditCopy method (as in the code below) to copy a chart to clipboard. It is a simple one line approach, which works well for any type of chart, with one exception. If you are using a chart which contains a legend, a bug prevents the legend from being outputted correctly. Instead, you get "C1", "C2", etc... [refer to Figure 2].

m_mschart1.EditCopy();

Sample Image

In order to avoid this particular problem, an alternative method must be applied in order to retrieve the chart _with_ the legend. As the code below indicates, you will need to get the device context of the MSChart and perform a BitBlt operation of the chart, which then will be copied to the clipboard as a bitmap.

CDC* pChartDC;

// Get device context from MSChart
pChartDC = m_mschart1.GetDC();

// Get dimensions of MSChart
RECT mschartRect;
m_mschart1.GetClientRect( &mschartRect );
int mschartWidth = mschartRect.right - mschartRect.left;
int mschartHeight = mschartRect.bottom - mschartRect.top;

// Create CBitmap
CBitmap myBitmap;

// Create Compatible Bitmap for MSChart
myBitmap.CreateCompatibleBitmap( pChartDC,
           mschartWidth, mschartHeight );

// Define device-context object
CDC myCopy;
myCopy.CreateCompatibleDC( pChartDC );

// Get pointer to object being replaced
myCopy.SelectObject( myBitmap );

// Raster copy Bitmap from object pChartDC is pointing to, which is MSChart

// CAUTION: this process copies _exactly_ what is shown on screen. If MSChart is
// off-screen (ie. if the page is scrollable and it is currently hidden from
// view) OR a dialog or other window is blocking its view, then you will not
// get the correct result, as it will either turn out all black or have
// artifacts from other windows on it. It is for this reason I have chosen to
// copy the MSChart _before_ showing the 'Save File Dialog'
myCopy.BitBlt( 0, 0, mschartWidth, mschartHeight, pChartDC, 0, 0, SRCCOPY );

// Retrieve information about the CBitmap
BITMAP bits;
myBitmap.GetBitmap( &bits );

// Open clipboard and empty its contents
OpenClipboard();
EmptyClipboard();

// Copy our new MSChart bitmap to clipboard and close it
SetClipboardData( CF_BITMAP, myBitmap.GetSafeHandle() );
CloseClipboard();

Points of Interest

Using the BitBlt method is certainly interesting, but not without some shortcomings. Because BitBlt is sort of taking a screenshot of the screen and you are providing it with the coordinates of the area it needs to copy from the screen, if the chart happens to be off screen (i.e., if your chart is on a document, which can be scrolled and it is currently hidden from view) or if it is being obscured by another window, you get an image in the clipboard, which is either black or full of artifacts. My workaround for this method is to copy the data of the MSChart using the EditCopy method and create a new popup dialog containing another MSChart and use the PasteCopy method (this works, since EditCopy also copies data and not just an image to the clipboard). Make sure the dialog is of the right dimensions, and use the OnTimer() event, along with a SetTimer() in the OnInitDialog() (see code below).

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

    // Paste from the EditCopy method from the parent dlg
    m_mschart1.EditPaste();

    // Refresh the MSChart to reflect changes
    m_mschart1.Refresh();

    // Initialize a Timer event.
    //
    // Timer will trigger an event in X milliseconds,
    // the event being the MemDC copy
    // and BitBlt of the resulting MSChart.
    // Then it automatically closes the dialog.
    // All these events should make it seem seamless
    // to the user, which no input is required of.
    //    Refer to function: --> void CMSChartSaveDlg::OnTimer(UINT nIDEvent)
    SetTimer(1, 1000, 0);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}
void CPrintMyMschartLegendsDlgExportForm::OnTimer(UINT nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);

    CDC* pChartDC;
    // Get device context from MSChart
    pChartDC = m_mschart1.GetDC();
    // Get dimensions of MSChart
    RECT mschartRect;
    m_mschart1.GetClientRect( &mschartRect );
    int mschartWidth = mschartRect.right - mschartRect.left;
    int mschartHeight = mschartRect.bottom - mschartRect.top;
    // Create CBitmap
    CBitmap myBitmap;
    // Create Compatible Bitmap for MSChart
    myBitmap.CreateCompatibleBitmap( pChartDC,
                           mschartWidth, mschartHeight);
    // Define device-context object
    CDC myCopy;
    myCopy.CreateCompatibleDC( pChartDC );
    // Get pointer to object being replaced
    myCopy.SelectObject( myBitmap );
    // Raster copy Bitmap from object pChartDC
    // is pointing to, which is MSChart
    //
    // CAUTION: this process copies
    // _exactly_ what is shown on screen.
    myCopy.BitBlt( 0, 0, mschartWidth, mschartHeight,
                           pChartDC, 0, 0, SRCCOPY );
    // Retrieve information about the CBitmap
    BITMAP bits;
    myBitmap.GetBitmap( &bits );
    // Open clipboard and empty its contents
    OpenClipboard();
    EmptyClipboard();
    // Copy our new MSChart bitmap to clipboard and close it
    SetClipboardData( CF_BITMAP, myBitmap.GetSafeHandle() );
    CloseClipboard();

    CDialog::OnTimer(nIDEvent);
    // Kill the timer, so that it only occurs once
    KillTimer(nIDEvent);

    // This will close the dialog
    // and return a IDOK message back to parent
    CDialog::OnOK();
}

What this does is it pastes the chart into another MSChart, which is set up in your dialog. Then, from this, it creates a timer, which waits x amount of milliseconds before capturing the MSChart to clipboard and the kills the time, while closing the dialog automatically.

This way, what you get is a popup flashing up on screen, which copies the chart to clipboard and closes by itself [refer to Figure 3].

Sample Image

This is one way I worked around this issue. It is all in the source code which is provided with this article.

History

Current version: version 1.1.

  • Version 1.1 - Added the popup dialog method.
  • Version 1.0 - BitBlt workaround to the EditCopy bug.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Canada Canada
Hans-Christian began his interest in programming with Visual Basic as a 15 year old, where he first programmed an Blockout clone and a Lunar Landing simulation.

This interest led him to discover the delights in Basic programming on his Texas Instruments TI-82 (a linkup chat program and a simple raytracer) and AmigaBasic on his Amiga 500. He eventually dabbled with HTML, JavaScript, DHTML & CSS, when the internet bloomed.

Eventually Hans-Christian went off to university in England to take a Bachelours in Computer Science and learnt C++, as well as Java, PHP, JSP, J2ME, SQL, Assembly, MFC, DirectX and OpenGL.

Currently Hans-Christian is living in Canada and is focusing on C++ and PHP development. He is also in the process of learning dotNet and C# in his spare time, as well as toying with Linux & BSD.

Have a look at my project website: ->
http://www.antipop.co.uk/projects/


"Dreams of creating digital mice..."

Comments and Discussions

 
GeneralUses CPaintDC Pin
hoang van hiep19-Jun-06 17:58
hoang van hiep19-Jun-06 17:58 
Generaltext over or above the columns Pin
Marc Soleda2-Aug-05 4:54
Marc Soleda2-Aug-05 4:54 
AnswerRe: text over or above the columns Pin
Christopher Stratmann27-Jun-06 6:09
Christopher Stratmann27-Jun-06 6:09 
GeneralMouse Cursor Pin
aerolupus9-Feb-05 8:03
professionalaerolupus9-Feb-05 8:03 
GeneralRe: Mouse Cursor Pin
Hans-Christian Andersen9-Feb-05 21:25
Hans-Christian Andersen9-Feb-05 21:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.