Site hosted by Angelfire.com: Build your free website today!

Save an image from MSChart Control

Level: Intermediate

Save an image from MSChart Control

Sometimes, we need to save an image from our chart that we are showing in our application. Unfortunatelly, MSChart control doesn't allow us to do it like Excel Chart.
That's a pity but with a little API calls, i will show you not one but two ways to achieve this.

Plan "A" _______________________________________
Using BitBlt API
MSChart control doesn't exposes its hwnd property like other controls in Visual Basic (as PictureBox, textBox, the form itself, and so on) which is what we need to control some things from the control to get final results.
Create a new Standard Exe Project and add a MSChart control, A Command Button and a PictureBox control. The data in the chart is not important here, leave the default.
So, the first thing to know is its hwnd value. To do that we will take some API stuff. See the following code (put it in a separate module):
'modEnumChildWindows.bas
Option Explicit

Public Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Public gHwnd As Long
Public strClassName As String
Public Function EnumChildProc(ByVal hWnd As Long, ByVal lParam As Long) As Long
  Dim sSave As String, ret As Long
  Static i As Integer
  sSave = String$(255, Chr$(0))
  Get the name of the target class
  ret = GetClassName(hWnd, sSave, 255)
  'remove the last Chr$(0)
  sSave = Left$(sSave, ret)
  '
  Debug.Print Chr$(34) & sSave & Chr$(34); i
  i = i + 1
  If sSave = strClassName Then gHwnd = hWnd
  'continue enumeration
  EnumChildProc = 1
End Function
That's the code we need to find the hwnd of our Chart (More on this later) and it could be use to find the hwnd for almost any child window of our form, in case that the control doesn't exposes one.
This callback function enumerates all child windows that belongs to our target form, check the class name, if it matchs with our class name, populates the gHwnd variable with its hwnd value.
At General declarations section of our form, paste this API stuff:
Option Explicit
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long
At Form_Load event of our main form, we could paste this code:
Private Sub Form_Load()
'This is the class name of our target window
strClassName = "MSChart20WndClass"
'Enumerate child windows of the form
EnumChildWindows Me.hWnd, AddressOf EnumChildProc, 0
End Sub
Note:I will not describe here how to use AddressOf function, you could get it from MSDN or at Internet.
The "real" action will take place at Form1_Click event:
Private Sub Form_Click()
   Me.AutoRedraw = True
   With Picture1
       .Width = MSChart1.Width
       .Height = MSChart1.Height
       BitBlt .hDC, 0, 0, .Width, .Height, GetWindowDC(hwndChart), 0, 0, vbSrcCopy
       SavePicture .Image, "c:\mychart.bmp"
   End With
End Sub
Instead of use Form1_Click event, we could encapsulate the code inside a sub/function with the name of the resulting bmp as parameter.
The last step is go to Explorer window and do a double-click in c:\mychart.bmp to see the results.


Plan "B" _______________________________________
Using Allapi.net code
This code Not belongs to me, i only did little changes to meets the requirements.
General declarations section of form:
Option Explicit
Const RC_PALETTE As Long = &H100
Const SIZEPALETTE As Long = 104
Const RASTERCAPS As Long = 38
Private Type PALETTEENTRY
    peRed As Byte
    peGreen As Byte
    peBlue As Byte
    peFlags As Byte
End Type
Private Type LOGPALETTE
    palVersion As Integer
    palNumEntries As Integer
    palPalEntry(255) As PALETTEENTRY ' Enough for 256 colors
End Type
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type
Private Type PicBmp
    Size As Long
    Type As Long
    hBmp As Long
    hPal As Long
    Reserved As Long
End Type
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As PicBmp, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal iCapabilitiy As Long) As Long
Private Declare Function GetSystemPaletteEntries Lib "gdi32" (ByVal hdc As Long, ByVal wStartIndex As Long, ByVal wNumEntries As Long, lpPaletteEntries As PALETTEENTRY) As Long
Private Declare Function CreatePalette Lib "gdi32" (lpLogPalette As LOGPALETTE) As Long
Private Declare Function SelectPalette Lib "gdi32" (ByVal hdc As Long, ByVal hPalette As Long, ByVal bForceBackground As Long) As Long
Private Declare Function RealizePalette Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Command1_Click event:
Private Sub Command1_Click()
    'KPD-Team 1999
    'URL: http://www.allapi.net/
    'E-Mail: KPDTeam@Allapi.net
    'Create a picture object from the screen
    With MSChart1
      Picture1.ScaleWidth = .Width
      Picture1.ScaleHeight = .Height
      Set Picture1.Picture = hDCToPicture(GetDC(gHwnd), 0, 0, .Width, .Height)
   End With
End Sub
We STILL need the EnumChildWindows API to get it work, since MSChart doesn't exposes its hwnd property as stated earlier. Due to that, code from "Plan A"'s Form1_Load event has to be used too and we have to add the SavePicture function to save the graph. The only diference is that you don't need .Image property, you could use .Picture directly since, now, it is a "real" picture object.

Hope you have enjoyed that work as i did.
;)


©2003 - Richie Simonetti