避免 MFC 中 MSChart EditCopy / Legend 错误






3.67/5 (2投票s)
2005年2月2日
3分钟阅读

66728

2456
一种在 MFC 中解决 ActiveX MSChart 控件 EditCopy / Legend 错误的方法。
引言
本文提供的代码提出了一种解决方法,用于解决使用 ActiveX 控件(MSChart)的 EditCopy 方法导出图表时遇到的问题。问题在于,当使用 EditCopy
方法将图表复制到内存时,此方法不会导出图例中的文本。 相反,它会被替换为“C1”、“C2”等……微软承认了这个问题,并提供了一个描述该问题的页面和一个解决方案,但他们只为 Visual Basic 5/6 提供了解决方法。 对于在 MFC 项目中使用 Visual C++ 6 的人,没有提供解决方案。
由于我正在从事的项目经常使用 MSChart,并且 MSChart 实际上非常有用,因此无法将其全部替换为另一种类型的图表。 因此,我必须解决这个问题并开发一种避免 EditCopy 错误的方法。
背景
从根本上讲,Windows 中的一切,如果你可以想象,都是一种画布。 因此,屏幕上的任何对象都可以被引用,然后作为位图提取。 您必须确定对象的坐标,并使用 Windows API 的 BitBlt
函数直接将该块复制到内存中。 这有点像截屏,只不过您只拍摄特定区域的屏幕。
使用代码
如前所述,通常会使用 EditCopy
方法(如下面的代码所示)将图表复制到剪贴板。 这是一种简单的一行方法,适用于任何类型的图表,但有一个例外。 如果您使用的是包含图例的图表,则该错误会阻止图例被正确输出。 相反,你会得到“C1”、“C2”等……[参考图 2]。
m_mschart1.EditCopy();
为了避免这个特殊的问题,必须应用另一种方法来检索_带有_图例的图表。 如下面的代码所示,您需要获取 MSChart 的设备上下文并执行图表的 BitBlt
操作,然后将其作为位图复制到剪贴板。
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();
关注点
使用 BitBlt
方法当然很有趣,但并非没有缺点。 因为 BitBlt
就像拍摄屏幕截图一样,并且您为其提供了需要从屏幕复制的区域的坐标,如果图表恰好在屏幕外(即,如果您的图表位于可以滚动的文档上并且当前从视图中隐藏)或者它被另一个窗口遮挡,您会在剪贴板中获得一个图像,该图像要么是黑色的,要么充满了伪影。 我针对此方法的解决方法是使用 EditCopy
方法复制 MSChart 的数据,并创建一个新的弹出对话框,其中包含另一个 MSChart 并使用 PasteCopy
方法(这有效,因为 EditCopy
也复制数据,而不仅仅是将图像复制到剪贴板)。 确保对话框的尺寸正确,并使用 OnTimer()
事件以及 OnInitDialog()
中的 SetTimer()
(请参见下面的代码)。
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(); }
这样做会将图表粘贴到另一个 MSChart 中,该 MSChart 在您的对话框中设置。 然后,由此创建一个计时器,该计时器等待 x 毫秒,然后将 MSChart 捕获到剪贴板,然后终止时间,同时自动关闭对话框。
这样,您得到的是一个在屏幕上闪烁的弹出窗口,该窗口将图表复制到剪贴板并自行关闭[参考图 3]。
这是我解决这个问题的一种方法。 这都在本文提供的源代码中。
历史
当前版本:版本 1.1。
- 版本 1.1 - 添加了弹出对话框方法。
- 版本 1.0 - 针对 EditCopy 错误的
BitBlt
解决方法。