Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsVB SyntaxEnterprise DevelopmentDatabase AccessControlsCOMWin APICrystal ReportDeploymentGeneralGeneral 2
Related Topics
VB.NET / ASP.NETMS SQL ServerMS AccessOther Database ProductsMore Topics ...

VB Forum / General / July 2008



Tip: Looking for answers? Try searching our database.

insert an Excel graph into a Richtextbox?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Bob O`Bob - 27 Jun 2008 09:01 GMT
A client has a need for something I've just never done.

It doesn't seem like it should be too difficult, but I'm unsure
of where to start.  I've not done much at all with Excel.

I have some data, in a string, in the form of [a portion of] a CSV file,
and I want to instantiate an Excel worksheet, populate it with that data,
graph an X/Y scatter diagram, an then somehow (maybe via the clipboard?)
insert that chart into a RichTextBox.

Suggestions, links, and/or samples would be greatly appreciated.

    Bob
--
Jan Hyde (VB MVP) - 27 Jun 2008 10:25 GMT
Bob O`Bob <filterbob@yahoogroups.com>'s wild thoughts were
released on Fri, 27 Jun 2008 01:01:20 -0700 bearing the
following fruit:

>A client has a need for something I've just never done.
>
[quoted text clipped - 7 lines]
>
>Suggestions, links, and/or samples would be greatly appreciated.

I don't see why the clipboard shouldn't work since you can
do it manually.

I'm gonna play around and see if I can do it
programmatically without the clipboard though.

--
Jan Hyde

https://mvp.support.microsoft.com/profile/Jan.Hyde
Bob O`Bob - 27 Jun 2008 16:33 GMT
> Bob O`Bob <filterbob@yahoogroups.com>'s wild thoughts were
> released on Fri, 27 Jun 2008 01:01:20 -0700 bearing the
[quoted text clipped - 14 lines]
> I don't see why the clipboard shouldn't work since you can
> do it manually.

Huh?
I am not in any way objecting to the clipboard,
that was a *suggestion*

> I'm gonna play around and see if I can do it
> programmatically without the clipboard though.

I don't have any reason to care one way or the other.

People _who know Excel_ can do it manually, yes.
But as I said, I just don't know where to start
to get this done with VB.  Watching an Excel expert
whip through it manually ... cool.  But not helpful.

I don't need to discuss alternatives, because no one
is going to care.  Any way which works ... works.

    Bob
--
Jan Hyde (VB MVP) - 30 Jun 2008 09:08 GMT
Bob O`Bob <filterbob@yahoogroups.com>'s wild thoughts were
released on Fri, 27 Jun 2008 08:33:00 -0700 bearing the
following fruit:

>> Bob O`Bob <filterbob@yahoogroups.com>'s wild thoughts were
>> released on Fri, 27 Jun 2008 01:01:20 -0700 bearing the
[quoted text clipped - 18 lines]
>I am not in any way objecting to the clipboard,
>that was a *suggestion*

I understand that, I just wanted to see if it was possible.
Plus, I'm not a fan of relying on the clipboard.

>> I'm gonna play around and see if I can do it
>> programmatically without the clipboard though.
[quoted text clipped - 8 lines]
>I don't need to discuss alternatives, because no one
>is going to care.  Any way which works ... works.

i'm neither an Excel expert or a richtextbox expert, I just
figured it was an interesting problem.

j

--
Jan Hyde

https://mvp.support.microsoft.com/profile/Jan.Hyde
Karl E. Peterson - 01 Jul 2008 02:06 GMT
>>> I have some data, in a string, in the form of [a portion of] a CSV file,
>>> and I want to instantiate an Excel worksheet, populate it with that data,
[quoted text clipped - 9 lines]
> I am not in any way objecting to the clipboard,
> that was a *suggestion*

It's been years since I had to work with a RTB but, as I recall, the only way to
insert a graphic is indeed from the clipboard.  That'd make this a pretty good
suggestion, if so. <g>

>> I'm gonna play around and see if I can do it
>> programmatically without the clipboard though.
[quoted text clipped - 5 lines]
> to get this done with VB.  Watching an Excel expert
> whip through it manually ... cool.  But not helpful.

Have 'em turn on the Macro recorder before doing the demo.  That's usually my first
shot at discovery in Word or Excel.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Peter T - 01 Jul 2008 10:51 GMT
"Karl E. Peterson" wrote in message

> > I am not in any way objecting to the clipboard,
> > that was a *suggestion*
>
> It's been years since I had to work with a RTB but, as I recall, the only way to
> insert a graphic is indeed from the clipboard.  That'd make this a pretty good
> suggestion, if so. <g>

Via the clipboard is also the only way to get an image of an Excel Chart, at
least AFAIK other than Excel's export-chart method to file (eg as a gif).

The long established "Excel chart to clipboard to picture object" is Stephen
Bullen's PastePicture.xls example (PastePicture.zip from the link in
comments below). I notice your name, Karl, is listed in the credits!

FWIW a quick VB6 example

'' Requires all the code as-is from Stephen Bullen's
'' modPastePicture in PastePicture.xls in a normal module
'' http://www.oaltd.co.uk/Excel/Default.htm
'' with these constants included (if late binding)

'Public Const xlPicture As Long = -4147
'Public Const xlScreen As Long = 1
'Public Const xlBitmap As Long = 2

' in a Form with a Button, an Image and/or PictureBox

Private moCht As Object

Private Sub Command1_Click()
Dim lPicType As Long
Static bBMP As Boolean ' to compare picture types

   On Error Resume Next
   If moCht Is Nothing Then
   'assumes an embedded chart exists on the first sheet
   'of the active workbook in a running instance of Excel
       Set moCht = GetObject(, "excel.application"). _
               ActiveWorkbook.Worksheets(1). _
               ChartObjects(1).Chart
   End If

   If moCht Is Nothing Then
       MsgBox Err.Description
       Exit Sub
   End If
   On Error GoTo 0

   If bBMP Then
       lPicType = xlPicture
       Me.Caption = "xlPicture"
   Else
       lPicType = xlBitmap
       Me.Caption = "xlBitmap"
   End If
   bBMP = Not bBMP

   ' manually: with selected chart, Shift-Edit / Copy Picture
   moCht.CopyPicture xlScreen, lPicType, xlScreen

   Image1.Stretch = True
   Set Image1.Picture = PastePicture(lPicType)

   Picture1.AutoSize = True
   Set Picture1.Picture = PastePicture(lPicType)

End Sub

Thinking aloud, when an Excel embedded chart is selected it appears in it's
own window (there can only ever be one chart window). I wouldn't know how to
go about it but maybe it's possible to get its image without using the
clipboard. If anyone is so inclined ? :-) maybe this might help as a starter

' if not 100% sure there's only one Excel instance
' need to replace vbNullstring with xlApp.Caption (only in the first call)
hWindXL = FindWindow("XLMAIN", vbNullString)

hWndWB = FindWindowEx(hWindXL, 0, "XLDESK", vbNullString)
hWndChart = FindWindowEx(hWndWB, 0, "EXCELE", vbNullString)
' If hWndChart = 0  ensure an embedded chart is selected

hDcChart = GetDC(hWndChart)

' ?

ReleaseDC hWndChart, hDcChart

Regards,
Peter T
Karl E. Peterson - 01 Jul 2008 19:34 GMT
> "Karl E. Peterson" wrote in message
>
[quoted text clipped - 11 lines]
> Bullen's PastePicture.xls example (PastePicture.zip from the link in
> comments below). I notice your name, Karl, is listed in the credits!

Yeah, I remember working with him on that "way back when."  :-)

> FWIW a quick VB6 example
>
[quoted text clipped - 54 lines]
> go about it but maybe it's possible to get its image without using the
> clipboard. If anyone is so inclined ? :-) maybe this might help as a starter

I think the advantage of using the clipboard is the rich array of format options
Excel provides.  You're not just stuck with whatever resolution bitmap the window is
presenting.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Mike Williams - 01 Jul 2008 21:09 GMT
> Thinking aloud, when an Excel embedded chart is selected it
> appears in it's own window (there can only ever be one chart
> window). I wouldn't know how to go about it but maybe it's
> possible to get its image without using the clipboard. If anyone
> is so inclined ? :-) maybe this might help as a starter

Extracting the chart from the DC of the window displaying it will only get
you a low resolution bitmap of the drawing of the chart. I don't use Excel
myself but I would imagine that the chart itself is created by Excel as a
metafile, and in order to get hold of that metafile you would need to find
the handle of the Excel metafile itself. If you cannot find the Excel
metafile the you would probably be best allowing Excel to dump it to the
clipboard, from where you can then get at it in the way you are currently
doing.

Mike
Peter T - 01 Jul 2008 21:45 GMT
"Mike Williams" wrote in message
> "Peter T" wrote in message
> >
[quoted text clipped - 14 lines]
>
> Mike

Thanks for that, and to Karl too who said pretty much the same thing.

I only brought this up as Jan Hyde had mentioned not wanting to rely on the
clipboard and it seemed like an intriguing idea (not that I've never had the
clipboard fail on me). Even if it was doable it would it would be limited to
an "active" chart only.

Regards,
Peter T
Karl E. Peterson - 01 Jul 2008 22:08 GMT
> I only brought this up as Jan Hyde had mentioned not wanting to rely on the
> clipboard and it seemed like an intriguing idea (not that I've never had the
> clipboard fail on me).

The problem with the clipboard is you run the risk of irritating the user.  How many
times have you copied some code out of a newspost, for example, fired up VB, and
*tried* to paste in the code?  I still do that, and it pixxes me off to no end each
time.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Peter T - 01 Jul 2008 22:55 GMT
"Karl E. Peterson" wrote in message
> > I only brought this up as Jan Hyde had mentioned not wanting to rely on the
> > clipboard and it seemed like an intriguing idea (not that I've never had the
[quoted text clipped - 4 lines]
> *tried* to paste in the code?  I still do that, and it pixxes me off to no end each
> time.

Well that's a very good point!

Can I revert to what I was thinking about then. Mike mentioned at best it
would only be possible to get a low res' bitmap of the chart. But I would
have thought for use as a graphic in some control that would be perfect,
providing it does not need to be resized. The Excel chart could be sized to
suit in advance.  (Not so good for Bob's RTB where user can resize the
pasted image - the pasted metafile works much better for that).

As I mentioned it could only ever work with the active chart. However when
automating Excel and creating a new chart it automatically becomes the
active chart. Its window handle would be available even in an invisible
Excel instance.

Regards,
Peter T
Karl E. Peterson - 01 Jul 2008 23:05 GMT
> "Karl E. Peterson" wrote in message
>> > I only brought this up as Jan Hyde had mentioned not wanting to rely on the
[quoted text clipped - 12 lines]
> have thought for use as a graphic in some control that would be perfect,
> providing it does not need to be resized.

For on-screen display, that'd likely work in a lot of circumstances, yep.

> The Excel chart could be sized to
> suit in advance.  (Not so good for Bob's RTB where user can resize the
> pasted image - the pasted metafile works much better for that).

Not good at all for that, or for anything higher res (printing, etc).

> As I mentioned it could only ever work with the active chart. However when
> automating Excel and creating a new chart it automatically becomes the
> active chart. Its window handle would be available even in an invisible
> Excel instance.

Best option would be if the object model exposed a graphic.  I don't see that, but
that hierarchy's immense!  There is the CopyPicture method of the Chart object, but
that's trashing the clipboard again and only gives you a bitmap (albeit at up to
printer resolution).
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Peter T - 02 Jul 2008 11:02 GMT
"Karl E. Peterson"  wrote in message

> Best option would be if the object model exposed a graphic.  I don't see that,
> but that hierarchy's immense!

I'm sure it doesn't expose the graphic, if it does it keeps very quite about
it!

> There is the CopyPicture method of the
> Chart object, but that's trashing the clipboard again and only gives you
> a bitmap (albeit at up to printer resolution).

The CopyPicture method can also give a metafile (emf)

Regards,
Peter T
Karl E. Peterson - 02 Jul 2008 22:33 GMT
> "Karl E. Peterson"  wrote in message
>
[quoted text clipped - 3 lines]
> I'm sure it doesn't expose the graphic, if it does it keeps very quite about
> it!

Seems so, yeah.

>> There is the CopyPicture method of the
>> Chart object, but that's trashing the clipboard again and only gives you
>> a bitmap (albeit at up to printer resolution).
>
> The CopyPicture method can also give a metafile (emf)

I didn't detect that, from the docs.  <- go figure, huh?
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Peter T - 03 Jul 2008 07:21 GMT
"Karl E. Peterson" wrote in message
> > "Karl E. Peterson"  wrote in message
>
[quoted text clipped - 5 lines]
>
> I didn't detect that, from the docs.  <- go figure, huh?

snippet from Excel-VBA Help -

expression.CopyPicture(Appearance, Format, Size)

Format   Optional Variant. The format of the picture. Can be one of the
following XlCopyPictureFormat constants: xlPicture or xlBitmap. The default
value is xlPicture [ie metafile].

Also there's loads about it in that PastePicture thing you helped write :-)
(link in my first reply to you in this thread)

Regards,
Peter T
Karl E. Peterson - 03 Jul 2008 19:15 GMT
> "Karl E. Peterson" wrote in message
>> > "Karl E. Peterson"  wrote in message
[quoted text clipped - 14 lines]
> following XlCopyPictureFormat constants: xlPicture or xlBitmap. The default
> value is xlPicture [ie metafile].

D'oh!  In retrospect, it's funny they even ask you to specify that, rather than just
load up the clipboard like they do when you manually copy a chart.

> Also there's loads about it in that PastePicture thing you helped write :-)
> (link in my first reply to you in this thread)

Heh, go figure.  :-)
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Peter T - 03 Jul 2008 20:43 GMT
"Karl E. Peterson"  wrote in message
> >> > The CopyPicture method can also give a metafile (emf)
> >>
[quoted text clipped - 11 lines]
> rather than just load up the clipboard like they do when you
> manually copy a chart.

Ah but there are two totally different methods to copy an Excel chart,
manually and/or programmatically.

The normal one is as you say, no arguments, copies the entire chart object
model (lot of stuff) and also an image of the chart.
VB/A: obCht.ChartArea.Copy ' no arg's

Thereafter can paste the entire chart object,  or PasteSpecial chart formats
to another chart, or in another app' paste its image (no choice of image
formats).

The Copy Picture only method is done manually by holding Shift, click Edit,
and a new "Copy Picture" button appears. Select that and dialog appears with
an array of image format options to choose.
VB/A: obCht.CopyPicture(Appearance, Format, Size)

With code no need to select the chart or for it to be visible,
manually of course it must be selected.

You really wanted to know all that, didn't you :-)

Regards,
Peter T
Mike Williams - 02 Jul 2008 05:45 GMT
> Can I revert to what I was thinking about then. Mike mentioned
> at best it would only be possible to get a low res' bitmap of the
[quoted text clipped - 3 lines]
> for Bob's RTB where user can resize the pasted image - the pasted
> metafile works much better for that).

Actually I meant that you would only be able to get a lowres bitmap from the
Excel window displaying the graph, but somewhere Excel will have a reference
to the memory metafile it created, which it must maintain in order to allow
the Excel user to resize the displayed graph. Perhaps Excel VBA exposes that
handle somewhere? Maybe someone with a detailed knowledge of Excel VBA might
know whether it is possible to get hold of it? Otherwise, as a very rough
kludge, it should be possible to use the clipboard anyway (as you are
currently doing I think?) but to save the existing metafile contents (if
there are any) and restore them as soon as the metafile has been transferred
and accessed.

Mike
Peter T - 02 Jul 2008 11:44 GMT
"Mike Williams"  wrote in message
> "Peter T" wrote in message
>
[quoted text clipped - 10 lines]
> to the memory metafile it created, which it must maintain in order to allow
> the Excel user to resize the displayed graph.

An Excel chart starts life as a set of layered objects (aka Items), possibly
several hundred, each with their own properties and methods. User can resize
items individually, as sub-sets of items or the entire chart. Of course
Chart items are also redrawn as underlying data in cells change (not only
data series), and for various other reasons, eg a change in the colour
palette. If you move an entire chart with a large item filled with a
diagonal gradient you can see items redrawin individually, particularly the
fill as it "sweeps" across.

A chart is a bit like a Form that hosts a variety of contol types, some
layered, some in Frames, etc. If a Form or Frame is resized might need to
resize the hosted controls in proportion.

Excel does provide methods to create alternative images of a chart on an
as-needs basis. Eg the CopyPicture method with a choice of image format
arguments, print pre-view, etc.

All this is a long winded way of asking if you still think Excel creates a
"single" metafile for it's charts, whether while a chart temporarily exists
in its own dedicated window or merely as a "DrawingObject" on the drawing
layer of a (spread) sheet. If so is a metafile created similarly for a Form
and its contents (and retrievable).

> Perhaps Excel VBA exposes that
> handle somewhere?

There is definately nothing documented. There are also some un-documented
Excel objects and methods which are supported mainly for legacy, but pretty
sure there's nothing amongst that lot.

The notion that Excel just might create a single graphic for a chart (and
other things) and that just conceivably, somehow, a handle can be retrieved
is even more intriguing than the idea of extractive the image of a chart
from its window!

I'm not holding my breath though...

> Otherwise, as a very rough
> kludge, it should be possible to use the clipboard anyway (as you are
> currently doing I think?) but to save the existing metafile contents (if
> there are any) and restore them as soon as the metafile has been transferred
> and accessed.

Indeed that's the normal way of doing it and, even if a kludge is the right
term, it works absolutely fine. By "normal" I mean the approach developed
with the assistance of Karl and others and that I referred to earlier.
Except of course it destroys the contents of user's clipboard!

Regards,
Peter T
Mike Williams - 02 Jul 2008 14:03 GMT
> All this is a long winded way of asking if you still think Excel
> creates a "single" metafile for it's charts, whether while a chart
> temporarily exists in its own dedicated window or merely as
> a "DrawingObject" on the drawing layer of a (spread) sheet.
> If so is a metafile created similarly for a Form and its contents
> (and retrievable).

As I said, I know virtually nothing about Excel because I never use it. I
was merely "thinking out loud" when I suggested that Excel might create a
memory metafile for a complete chart as a single entity. This is something
it could have done of course, had it been written that way, but after
reading what you have said it would appear that it actually creates a set of
individual objects (be they metafiles or some other kind of object) which it
uses the draw the composite chart, in much the same way the Corel draw will
maintain a list of separate items for a Corel Draw drawing. If that is the
case then it would be very difficult, if not impossible, to "get at" that
chart in its entirety other than by using the methods that Excel does
expose, creating a copy of the chart and sending it to the clipboard, for
example.

> The notion that Excel just might create a single graphic for a chart
> (and other things) and that just conceivably, somehow, a handle
> can be retrieved is even more intriguing than the idea of extractive
> the image of a chart from its window! I'm not holding my breath
> though...

After reading what you have said it would appear unlikely that Excel does
actually create a single metafile representing the entire chart which it
maintains in a way that allows a VBA programmer to get hold of its handle,
or at least if it does so then you have said it is definitely not
documented. The people who wrote Excel could certainly have programmed it
that way though, if they wished to do so, and I therefore thought it might
be worth mentioning just in case somebody who actually knows how to drive
Excel (which is definitely not me!) could "put us wise" on the matter.

> . . . By "normal" I mean the approach developed with the
> assistance of Karl and others and that I referred to earlier.
> Except of course it destroys the contents of user's clipboard!

In that case I have definitely misunderstood what you are doing, which is
not suprising really because what I know about Excel can be written on the
back of a postage stamp with a marker pen!

For example, if I were to write some standard VB6 code to retrieve the
metafile of an MSChart Control using the MSChart EditCopy method to copy the
metafile to the clipboard and then using ClipBoard.GetData(vbCFMetafile) to
transfer the metafile to the Picture property of a PictureBox or to a
StdPicture Object, then I could do so in such a way that it would preserve
the existing contents of the clipboard so that they were the same after the
operation as they were before. Is it not also possible to do that for the
Excel chart?

Mike
Peter T - 02 Jul 2008 16:12 GMT
"Mike Williams"  wrote in message

> After reading what you have said it would appear unlikely that Excel does
> actually create a single metafile representing the entire chart which it
[quoted text clipped - 4 lines]
> be worth mentioning just in case somebody who actually knows how to drive
> Excel (which is definitely not me!) could "put us wise" on the matter.

Depends on what you mean by "knows how to drive Excel" but I'll stand by
what I said about what is definately not documented. As I also mentioned,
there are known "hidden" methods (some extremely hidden) but reasonably sure
nothing relavant there, though I won't be quite as dogmatic as to say
definately none.  Excel keeps quite a lot of what it does very much to
itself, which otherwise might reasonably be exposed.

> For example, if I were to write some standard VB6 code to retrieve the
> metafile of an MSChart Control using the MSChart EditCopy method to copy the
[quoted text clipped - 4 lines]
> operation as they were before. Is it not also possible to do that for the
> Excel chart?

Maybe, if I understood more about how you would go about

"then I could do so in such a way that it would preserve the existing
contents of the clipboard so that they were the same after the operation as
they were before."

Would that not imply some generic method to temporary copy the entire
clipboard memory then restore when done. If so presumably some vbs script
could do all that as well as open VB and keep Karl from getting frustrated
when he loses his clipboard!

Be that as it may, I knew you would come up with something useful <g>

*******ClipBoard.GetData(vbCFMetafile)********

seems to works just as well with Excel's CopyPicture method

If you are feeling inclined (taking you literally when you say you barely
know Excel):
In an Excel sheet put some numbers in say three cells in a column
Select those data cells
Click the Chart icon (starts the chart wizard), just press Finish

In a VB6 form with a button, an Image control and a Picturebox

Option Explicit
Private moCht As Object

Private Sub Command1_Click()
Dim lPicType As Long
Dim cbType As ClipBoardConstants
Dim oXL As Object
Const xlScreen As Long = 1
Static bBMP As Boolean

   On Error Resume Next
   If moCht Is Nothing Then
       'assumes an embedded chart exists on the first sheet
       'of the active workbook in a running instance of Excel
       Set oXL = GetObject(, "excel.application")

       Set moCht = oXL. _
                   ActiveWorkbook.Worksheets(1). _
                   ChartObjects(1).Chart
   End If

   If moCht Is Nothing Then
       MsgBox Err.Description
       Exit Sub
   End If

   On Error GoTo 0

   bBMP = Not bBMP

   If bBMP Then
       lPicType = -4147 'xlPicture
       Me.Caption = "xlPicture" ' emf
       cbType = vbCFEMetafile
   Else
       lPicType = 2 'xlBitmap
       Me.Caption = "xlBitmap"
       cbType = vbCFBitmap
   End If

   ' manually: with selected chart, Shift-Edit / Copy Picture

   moCht.CopyPicture xlScreen, lPicType, xlScreen

   Set Image1.Picture = Clipboard.GetData(cbType)

   Set Picture1.Picture = Clipboard.GetData(cbType)

End Sub

Sadly in VBA can only retrieve text as simply as that from the clipboard.

To get fully back on topic, would you know how to put that clipboard graphic
into an RTB (although the SendMessage with WM_PASTE that Bart Smissaert came
up with, and I borrowed, seems to works fine)

Regards,
Peter T
Mike Williams - 02 Jul 2008 18:54 GMT
> Would that not imply some generic method to temporary copy
> the entire clipboard memory then restore when done. If so
[quoted text clipped - 4 lines]
> *******ClipBoard.GetData(vbCFMetafile)********
> seems to works just as well with Excel's CopyPicture method

Actually I may be oversimplifying this thing but I simply wrap any line that
copies stuff to the clipboard inside some code that gets any existing
bitmap, metafile or text item from the clipboard and then restores it
afterwards. As an example, here is your own "Excel chart" code wrapped in
such a way, so that whatever is on the clipboard when you run it is still on
the clipboard after you have done so. Check it out and see if it works okay
on your own system?

Mike

Option Explicit
Private moCht As Object

Private Sub Command1_Click()
'
Dim p1 As StdPicture, p2 As StdPicture, s1 As String
'
Dim lPicType As Long
Dim cbType As ClipBoardConstants
Dim oXL As Object
Const xlScreen As Long = 1
Static bBMP As Boolean
   On Error Resume Next
   If moCht Is Nothing Then
       'assumes an embedded chart exists on the first sheet
       'of the active workbook in a running instance of Excel
       Set oXL = GetObject(, "excel.application")
       Set moCht = oXL. _
                   ActiveWorkbook.Worksheets(1). _
                   ChartObjects(1).Chart
   End If
   If moCht Is Nothing Then
       MsgBox Err.Description
       Exit Sub
   End If
   On Error GoTo 0
   bBMP = Not bBMP
   If bBMP Then
       lPicType = -4147 'xlPicture
       Me.Caption = "xlPicture" ' emf
       cbType = vbCFEMetafile
   Else
       lPicType = 2 'xlBitmap
       Me.Caption = "xlBitmap"
       cbType = vbCFBitmap
   End If
   ' manually: with selected chart, Shift-Edit / Copy Picture
'
Set p1 = Clipboard.GetData(vbCFMetafile)
Set p2 = Clipboard.GetData(vbCFBitmap)
s1 = Clipboard.GetText
'
 moCht.CopyPicture xlScreen, lPicType, xlScreen
 Set Image1.Picture = Clipboard.GetData(cbType)
 Set Picture1.Picture = Clipboard.GetData(cbType)
'
Clipboard.SetData p1, vbCFMetafile
Clipboard.SetData p2, vbCFBitmap
Clipboard.SetText s1
End Sub
Peter T - 02 Jul 2008 19:55 GMT
"Mike Williams" wrote in message
> As an example, here is your own "Excel chart" code wrapped in
> such a way, so that whatever is on the clipboard when you run it is still on
> the clipboard after you have done so. Check it out and see if it works okay
> on your own system?
>
> Private Sub Command1_Click()
< snip>
> '
> Set p1 = Clipboard.GetData(vbCFMetafile)
[quoted text clipped - 8 lines]
> Clipboard.SetData p2, vbCFBitmap
> Clipboard.SetText s1

It certainly works for me to some extent but I guess, or rather as expected,
it only stores and restores plain text and any vbCFMetafile and/or
vbCFBitmap picture.

I appreciate a few more clipboard formats could have been similarly stored
but that still leaves a lot of stuff that might potentially have been on the
clipboard, that cannot so easily be stored, that would get wiped.

So, I'm not sure it's correct to say "whatever is on the clipboard" [is
preserved] but something is better than nothing!

Regards,
Peter T
Mike Williams - 02 Jul 2008 20:39 GMT
> It certainly works for me to some extent but I guess, or rather
> as expected, it only stores and restores plain text and any
[quoted text clipped - 4 lines]
> wiped. So, I'm not sure it's correct to say "whatever is on the
> clipboard" [is preserved] but something is better than nothing!

As I said in my post, I was greatly oversimplifying it just for test
purposes. You are of course correct in that all sorts of other data could be
on the clipboard, and you would need to deal with those as well, in whatever
way seems appropriate. For example, modifying the code I posted so that it
is as shown below would also preserve any RichText data:

Dim p1 As StdPicture, p2 As StdPicture
Dim s1 As String, s2 As String
Set p1 = Clipboard.GetData(vbCFMetafile)
Set p2 = Clipboard.GetData(vbCFBitmap)
s1 = Clipboard.GetText(vbCFText)
s2 = Clipboard.GetText(vbCFRTF)
'
 moCht.CopyPicture xlScreen, lPicType, xlScreen
 Set Image1.Picture = Clipboard.GetData(cbType)
 Set Picture1.Picture = Clipboard.GetData(cbType)
'
Clipboard.SetData p1, vbCFMetafile
Clipboard.SetData p2, vbCFBitmap
Clipboard.SetText s1, vbCFText
Clipboard.SetText s2, vbCFRTF

If you want to take this further so that it deals with stuff that the VB
ClipBoard object cannot itself natively deal with (or at least not very
easily) then it might be worth looking into the various clipboard API
functions. I would guess (although it is just a guess, because I never
normally get involved in these things) that there will be a method somewhere
that is capable of storing the entire block of clipboard data, regardless of
what it is.

Again, don't take any of these things I am telling you as "gospel", because
I do not actually use the clipboard for anything in any of my own code
(other than a few simple test programs) and so I have no expereince
whatsoever of handling it.

Mike
Peter T - 03 Jul 2008 07:34 GMT
"Mike Williams" wrote in message
> "Peter T" wrote in message

> > It certainly works for me to some extent but I guess, or rather
> > as expected, it only stores and restores plain text and any
[quoted text clipped - 3 lines]
> As I said in my post, I was greatly oversimplifying it just for test
> purposes.

I did get the general idea.

> For example, modifying the code I posted so that it
> is as shown below would also preserve any RichText data:

Yes that worked too, might as well do the easy stuff.

> If you want to take this further so that it deals with stuff that the VB
> ClipBoard object cannot itself natively deal with (or at least not very
> easily) then it might be worth looking into the various clipboard API
> functions.

Think I'll pass on that, probably overkill for this particular purpose.
Though if it's possible to preserve the entire clipboard I imagine someone
has already done it, wouldn't want to re-invent the wheel!

If it's a straightforward thing to do would you have a quick pointer to get
the lowres bitmap from a window.

Regards,
Peter T
Mike Williams - 03 Jul 2008 09:35 GMT
> If it's a straightforward thing to do would you have a quick
> pointer to get the lowres bitmap from a window.

The most reliable way I know of that works with all windows is to grab a
copy of the window exactly as it is displayed on the screen. This method
however requires that the window you are grabbing is entirely visible on the
display at the time you grab it. Would that be okay? If so then try the
following, which grabs the client area of the window (although it could be
changed to grab the whole window if you want its borders as well):

Mike

Option Explicit
Private Declare Function GetDC Lib "user32" _
 (ByVal hwnd As Long) As Long
Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function ReleaseDC Lib "user32" _
 (ByVal hwnd As Long, 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 Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type

Private Sub Command1_Click()
Dim myWnd As Long, myDC As Long, r1 As RECT
Dim wide As Long, high As Long
Me.ScaleMode = vbPixels
With Picture1
 .BorderStyle = vbBSNone
 .AutoRedraw = True
 .Visible = False
End With
' hWndChart is the handle of the Excel chart
' window as obtained by your own code
myWnd = hWndChart
GetClientRect myWnd, r1
wide = r1.Right - r1.Left
high = r1.Bottom - r1.Top
With Picture1
 .Width = wide
 .Height = high
 .Cls
 myDC = GetDC(myWnd)
 BitBlt .hdc, 0, 0, wide, high, _
   myDC, 0, 0, vbSrcCopy
End With
ReleaseDC myWnd, myDC
' the bitmap of the Excel chart is now in the
' Image property of the Picture1 PictureBox from
' where you can save it as a bitmap or whatever
End Sub
Peter T - 03 Jul 2008 11:07 GMT
"Mike Williams"  wrote in message
> "Peter T"> wrote in message
>
[quoted text clipped - 59 lines]
> ' where you can save it as a bitmap or whatever
> End Sub

Thanks for that, appreciate the declarations too. Unfortunately I couldn't
get it to work. I did though return myDC and the window's dimensions etc.
Not sure if necessary but I changed the Picture control's scalemode to
pixels in design.

I was careful to ensure the chart window was fully visible, fwiw in Excel
there's an option to include have the chart window in a normal style window
with window caption etc (rt-click selected chart, Chart Window), but that
didn't help.

Actually the requirement for the chart window to be fully visible would be a
major limitation in practice, but it would be nice though to get this to
work.

Not sure if it means anything but
   Result = Call BitBit(etc)  ' returned 1

If anyone else is interested here is how to get the chart window (it's light
for testing only), slot into the quoted code above.

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
   ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" _
   Alias "FindWindowExA" ( _
   ByVal hWnd1 As Long, ByVal hWnd2 As Long, _
   ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

Private Function hWndChart() As Long
Dim hWindXL As Long, hWndWB As Long

' Assumes only one running Excel instance
hWindXL = FindWindow("XLMAIN", vbNullString) ' Excel's main window

hWndWB = FindWindowEx(hWindXL, 0, "XLDESK", vbNullString)

' assumes an emedded chart is "selected"
hWndChart = FindWindowEx(hWndWB, 0, "EXCELE", vbNullString) ' chart window

End Function

Regards,
Peter T
Mike Williams - 03 Jul 2008 16:39 GMT
> Thanks for that, appreciate the declarations too. Unfortunately
> I couldn't get it to work. It did though return myDC and the
> window's dimensions etc. Not sure if necessary but I changed
> the Picture control's scalemode to pixels in design.

That's not necessary (although it won't do any harm) because BitBlt will be
using pixels whatever the ScaleMode of the PictureBox. I set the Form's
ScaleMode to pixels because when we set the Width and Height of the
PictureBox the values need to be in accordance with the ScaleMode of its
container, so setting the Form to pixels is an easy way (although there are
other ways) of setting the size of the PictureBox according to the values
returned in the RECT, which are themselves in pixels.

> I was careful to ensure the chart window was fully visible

In that case I can't see why it didn't work?

> Not sure if it means anything but
>    Result = Call BitBit(etc)  ' returned 1

That's fine. A non zero return value indicates success, at least as far as
the BitBlt function is concerned. When you say it didn't work, can you
elaborate on that? What exactly happened? In the code I posted the
PictureBox is set to Visible = False, so you will not see anything on the
display. But it is an Autoredraw PictureBox (Autoredraw = True) and so
BitBlt (which in the code draws to the hDC of the PictureBox) will draw its
output into the PictureBox's hidden Autoredraw memory bitmap, and in order
to access it you need to access the PictureBox's Image property. Have you
done that? Try saving the resultant image to a bmp file and load bmp file
into MS Paint to see what you get. You can do using the following as the
last line of the code:

   SavePicture Picture1.Image, "c:\temp\pictemp.bmp"

Otherwise, just to check that nothing is going wrong at your end regarding
the excel window, try the following slightly modified version of the code,
which leaves the PictureBox visible and which positions it at the top left
corner of the Form. Paste the code into a small standard VB Form (not
maximized) containing a small PictureBox and a Command Button and compile to
an exe on your desktop and run the exe by double clicking it your desktop.
This particular example takes a grab of the entire desktop and so when you
run it and click the button you should be able to see the Desktop in the
PictureBox exactly as it was when you clicked the button. The PictureBox
will itself be the same size as the window you grabbed (in this case, your
desktop) as you will see when you drag the Form to a larger size. You should
also find the image saved as a bmp file as "c:\temp\pictemp.bmp".Does that
work okay? If so then it should work when you give it the hWnd of any fully
visible window.

Mike

Option Explicit
Private Declare Function GetDesktopWindow _
 Lib "user32" () As Long
Private Declare Function GetDC Lib "user32" _
 (ByVal hwnd As Long) As Long
Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function ReleaseDC Lib "user32" _
 (ByVal hwnd As Long, 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 Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type

Private Sub Command1_Click()
Dim myWnd As Long, myDC As Long, r1 As RECT
Dim wide As Long, high As Long
Me.ScaleMode = vbPixels
With Picture1
 .BorderStyle = vbBSNone
 .AutoRedraw = True
 .Visible = True
End With
' hWndChart is the handle of the Excel chart
' window as obtained by your own code
myWnd = GetDesktopWindow 'hWndChart
GetClientRect myWnd, r1
wide = r1.Right - r1.Left
high = r1.Bottom - r1.Top
With Picture1
 .Move 0, 0, wide, high
 .Cls
 myDC = GetDC(myWnd)
 BitBlt .hdc, 0, 0, wide, high, _
   myDC, 0, 0, vbSrcCopy
End With
ReleaseDC myWnd, myDC
Picture1.Refresh
SavePicture Picture1.Image, "c:\temp\pictemp.bmp"
End Sub
Peter T - 03 Jul 2008 17:57 GMT
"Mike Williams" wrote in message
> "Peter T" wrote in message
>
> > Thanks for that, appreciate the declarations too. Unfortunately
> > I couldn't get it to work.

> When you say it didn't work, can you
> elaborate on that? What exactly happened? In the code I posted the
> PictureBox is set to Visible = False, so you will not see anything on the
> display.

This is embarrassing, I simply did not notice the Picturebox.visible = False
After commenting that, got a perfect picture of the Excel chart in the
picturebox using your previous code.

Following probably won't be of interest to you but after de-selecting the
chart in Excel and switching to another sheet, got an image of the top left
corner of the active sheet to same size as the now deactivated chart (which
no longer has a real window yet continues with the previous class name and
handle). Just a curiosity.

I posted a question in excel.programming asking how to get the chart window
in Excel 2007, no reply yet.

Regards,
Peter T
Mike Williams - 03 Jul 2008 10:28 GMT
> would you have a quick pointer to get
> the lowres bitmap from a window.

. . . By the way, as I implied in my previous response there are other ways
of grabbing a window which do work even if the window is not entirely
visible on the display, although I have never found any of them to be as
reliable as the simple BitBlt method I posted earlier, despite its
limitations. For example, you can send WM_PAINT and WM_PRINT messages or, if
you're using XP or later, you can use the PrintWindow function (as long as
the window is not actually minimized). I can't try those at the moment
though on an Excel chart because I am having problems getting a handle on it
using the example code you provided. The chart window always returns a value
of zero, possibly because (as you mentioned) my chart is not embedded? I
cannot follow the instructions you gave to create the Excel chart because
Excel 2007 (which I am using) does not have a "Chart button" which I can
click after I have selected some cells. In order to create the chart in
Excel 2007 I need to select some cells and then use the "Insert a Chart"
option, which does produce a visible chart but which just does not sound
right, and so I don't know whether the chart is "embedded"? I wasn't kidding
before by the way when I said I know nothing about Excel. I'm completely
lost in it! I did learn a few things about it some weeks ago when I needed a
function in standard VB code to perform the same task as the Excel NORMDIST
function, but I really know very little about it otherwise. Anyway, as a
quick example of the PrintWindow method here is an example that prints the
client area of NotePad (as long as you have it running, of course).

Mike

Option Explicit
Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function FindWindow Lib "user32" _
 Alias "FindWindowA" (ByVal lpClassName As String, _
 ByVal lpWindowName As String) As Long
Private Declare Function PrintWindow Lib "user32" _
 (ByVal hwnd As Long, ByVal hdcBlt As Long, _
 ByVal nFlags As Long) As Long
Private Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type
Private Const PW_CLIENTONLY As Long = 1

Private Sub Command1_Click()
Dim myWnd As Long, r1 As RECT
Me.ScaleMode = vbPixels
Picture1.AutoRedraw = True
Picture1.BorderStyle = vbBSNone
myWnd = FindWindow("Notepad", vbNullString)
If myWnd <> 0 Then
 GetClientRect myWnd, r1
 Picture1.Width = r1.Right - r1.Left
 Picture1.Height = r1.Bottom - r1.Top
 Picture1.Cls
 PrintWindow myWnd, Picture1.hDC, PW_CLIENTONLY
 Picture1.Refresh
End If
End Sub
Peter T - 03 Jul 2008 11:48 GMT
"Mike Williams" <mikea@whiskyandCoke.com> wrote in message
> "Peter T" <peter_t@discussions> wrote in message

My reply to your other recent message left my outbox before I saw this

> > would you have a quick pointer to get
> > the lowres bitmap from a window.
[quoted text clipped - 6 lines]
> you're using XP or later, you can use the PrintWindow function (as long as
> the window is not actually minimized).

Anything that works in W98 and later on with the chart window not visible
would be great :-)

> I can't try those at the moment
> though on an Excel chart because I am having problems getting a handle on it
[quoted text clipped - 6 lines]
> option, which does produce a visible chart but which just does not sound
> right, and so I don't know whether the chart is "embedded"?

I don't have Excel 2007 but it sounds like you created an embedded chart
fine. In Excel a chart can be "embedded" on a sheet as an object or appear
as a chart sheet (will have a sheet tab like worksheet tabs at the bottom).
If XL2007 probably has same shortcut keys as older versions, a simple way to
create a chart-sheet is, with some data cells selected, press F11, select
some cells and press F11.

Typically an embedded chart is ON a worksheet but it can also be embedded as
an additional chart on a chart-sheet.

I assume a chart window is similarly created in Excel 2007 when it is
selected (left click near the outside of the embedded chart (a window can
continue to exist after it has been deselected but it is illusionary).

I have read of others having difficulty finding its window handle in '2007,
possibly the classname for Excel's main window is NetUIHWND iso XLMAIN (in
all previous versions). If you can find either of those in Spy maybe you can
see EXCELE as a grandchild (below XLDESK).

> I wasn't kidding
> before by the way when I said I know nothing about Excel. I'm completely
> lost in it! I did learn a few things about it some weeks ago when I needed a
> function in standard VB code to perform the same task as the Excel NORMDIST
> function, but I really know very little about it otherwise.

Sounds like you know as much about Excel as I do about VB6 then <g>

. Anyway, as a
> quick example of the PrintWindow method here is an example that prints the
> client area of NotePad (as long as you have it running, of course).

Can't test that at the moment as I run my VB on W98, PrintWindow not
supported.

Regards,
Peter T
Mike Williams - 03 Jul 2008 16:43 GMT
> Can't test that at the moment [PrintWindow] as I run
> my VB on W98, PrintWindow not supported.

That's not a problem because you should still be able to use the WM_PAINT
and WM_PRINT messages in Win98, as far as I know, although I don't know what
they'll do with an Excel graph window. But first it might be best to get the
simple "window grab of a fully visible window" code working (see the other
response I just posted).

Mike
Peter T - 03 Jul 2008 18:27 GMT
"Mike Williams" wrote in message
> "Peter T" wrote in message
>
[quoted text clipped - 8 lines]
>
> Mike

Following your adjacent post, and correcting my mental lapse, I've got the
Excel chart into a picturebox working with 'the simple "window grab of a
fully visible window" code' .

Could you elaborate on how I might use those WM_PAINT and WM_PRINT messages
with a view to getting an image of a non visible window in W98. Thanks.

Regards,
Peter T
Mike Williams - 03 Jul 2008 19:40 GMT
> Following your adjacent post, and correcting my mental lapse,
> I've got the Excel chart into a picturebox working with 'the
> simple "window grab of a fully visible window" code' .
> Could you elaborate on how I might use those WM_PAINT
> and WM_PRINT messages with a view to getting an image
> of a non visible window in W98. Thanks.

I'm not sure whether they will help you or not with your Excel window. The
PAINT and PRINT messages work with some windows and not with others. For
example, they work fine (subject to limitations with certain controls that
need special attention) if you want a bitmap copy of a VB PictureBox
containing some background colour or drawing and some contained controls,
where you will end up with a bitmap image of the complete picture box,
including a drawing of the controls it contains. They don't seem to work
with a NotePad window though (at least from the few tests I've just carried
out). I think essentially the target window (the window you wish to copy)
needs to have been created in such a way that it will respond correctly to
those messages, which is not the case with all windows. They might work fine
with your Excel chart window though. The only way is to try it and see. Here
is an example which "grabs a bitmap" of a VB PictureBox window, including
its backcolour and a drawing of its contained controls. Paste the code into
a Form containing two PictureBoxes and one Command Button. Place a few
controls (Comand Buttons, textBoxes, Labels, etc) inside Picture2 (so that
they are actually contained within Picture2 and not just sitting on top of
it) and set its BackColor to whatever colour you wish. When run, the code
should position Picture1 at the top left corner of the Form and it should
draw the entire contents of Picture2 into it. Once you've got that working
okay then replace the line mywnd = Picture2.hwnd with mywnd = chartwnd (the
window handle of your Excel chart) and see if it still works. Fingers
crossed ;-)

Mike

Option Explicit
Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nindex As Long) As Long
Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type
Private Const WM_PAINT = &HF
Private Const WM_PRINT = &H317
Private Const PRF_CLIENT = &H4&
Private Const PRF_CHILDREN = &H10&
Private Const PRF_OWNED = &H20&

Private Sub Form_Load()
Picture1.AutoRedraw = True
Picture1.BorderStyle = vbBSNone
Picture1.Visible = True
Picture1.BackColor = vbCyan
End Sub

Private Sub Command1_Click()
Dim mywnd As Long, r1 As RECT
Dim s1 As String
mywnd = Picture2.hwnd ' or the window you want to copy
If mywnd <> 0 Then
 GetClientRect mywnd, r1
 Picture1.Container.ScaleMode = vbPixels
 Picture1.Move 0, 0, _
   r1.Right - r1.Left, r1.Bottom - r1.Top
 SendMessage mywnd, WM_PAINT, Picture1.hdc, 0
 SendMessage mywnd, WM_PRINT, Picture1.hdc, _
   PRF_CHILDREN + PRF_CLIENT + PRF_OWNED
 Picture1.Refresh
End If
End Sub
Peter T - 03 Jul 2008 20:37 GMT
"Mike Williams"  wrote in message
> "Peter T"  wrote in message
>
[quoted text clipped - 19 lines]
> those messages, which is not the case with all windows.
> They might work fine with your Excel chart window though.

> The only way is to try it and see. Here
> is an example which "grabs a bitmap" of a VB PictureBox window, including
[quoted text clipped - 5 lines]
> should position Picture1 at the top left corner of the Form and it should
> draw the entire contents of Picture2 into it.

That worked fine, got a perfect image of the contents of PictureBox2 in
PictureBox1

> Once you've got that working
> okay then replace the line mywnd = Picture2.hwnd with mywnd = chartwnd
> (the  window handle of your Excel chart) and see if it still works.
> Fingers  crossed ;-)

Sadly that didn't work at all (fully visible or otherwise), although
PictureBox1 did size to the chart window. I tried the chart's respective
parent windows and got some internal window captions, but that's all.

Tantalizing !

Regards,
Peter T
Mike Williams - 03 Jul 2008 21:33 GMT
> That worked fine, got a perfect image of the contents of
> PictureBox2 in PictureBox1
[quoted text clipped - 8 lines]
> respective parent windows and got some internal window captions,
> but that's all. Tantalizing !

Pity. As I said, the way a window reacts to the PAINT and PRINT messages is
controlled by the window itself, and it looks as though you are out of luck
using that method on a Excel chart window. Still, at least the first method
I posted works (grabbing a copy of a fully displayed Excel chart window),
although of course the requirement of having it fully displayed is a serious
limitation. Causing the chart window to become visible only for the very
short time it takes to grab a copy of it and then hiding it is the only
other option I can think of, but the user would almost certainly still see
the "flicker" as it was displayed for perhaps just one frame.

Up to present I really cannot think of anything else to try, so it looks as
though you might be better off using your original "copy via the clipboard"
method, in which case investigating the various API Clipboard functions to
save and restore all possible clipboard contents would be the next thing to
do, although the simple code I posted the other day manages to preserve many
typical clipboard contents, including bitmaps and metafiles and text and
RTF, which is better than nothing :-(

Sorry I can't be of any more help to you.

Mike
Peter T - 04 Jul 2008 07:57 GMT
"Mike Williams" wrote in message
> Pity. As I said, the way a window reacts to the PAINT and PRINT
> messages is controlled by the window itself, and it looks as though
[quoted text clipped - 10 lines]
>
> Sorry I can't be of any more help to you.

Not at all, I wondered and now I know. Still no reply to my post in
excel.programming as to the availability of a chart window in Excel 2007.
Suggests it's n/a, if so it was never going to be possible to grab it's
window image in this version anyway.

Apart from that it's now become just two lines to represent an Excel chart
on a form

objChart.CopyPicture(format arg's)
Set Picture1.Picture = Clipboard.GetData(image-format)

It does use the clipboard but as you say even that can be mitigated to some
extent.

Thanks for your help in exploring the possibilities.

Regards,
Peter T
RB Smissaert - 01 Jul 2008 22:34 GMT
> Even if it was doable

Never tried, but I would guess it must be doable and it would be a useful
thing.
One for the graphics experts? ...

RBS

> "Mike Williams" wrote in message
>> "Peter T" wrote in message
[quoted text clipped - 31 lines]
> Regards,
> Peter T
Henning - 27 Jun 2008 12:08 GMT
>A client has a need for something I've just never done.
>
[quoted text clipped - 10 lines]
> Bob
> --

If the data is a series of values, maybe the MSChart control can do it
without Excel.

The documentation though is terrible...

/Henning
Jan Hyde (VB MVP) - 27 Jun 2008 12:55 GMT
"Henning" <computer_hero@coldmail.com>'s wild thoughts were
released on Fri, 27 Jun 2008 13:08:24 +0200 bearing the
following fruit:

>>A client has a need for something I've just never done.
>>
[quoted text clipped - 15 lines]
>
>The documentation though is terrible...

MSChart control sucks in every way possible ;-)

I've failed to do it in code thus far but I know very little
about the richtextbox and don't have the help files on this
PC.

--
Jan Hyde

https://mvp.support.microsoft.com/profile/Jan.Hyde
RB Smissaert - 27 Jun 2008 17:46 GMT
Here is an example that works with an image control:
http://www.j-walk.com/ss/excel/tips/tip66.htm

RBS

>A client has a need for something I've just never done.
>
[quoted text clipped - 10 lines]
> Bob
> --
Bob O`Bob - 27 Jun 2008 18:00 GMT
> Here is an example that works with an image control:
> http://www.j-walk.com/ss/excel/tips/tip66.htm

Thanks, but...

Again:  That is Excel.  I do not know Excel.
"[step] 1.  Create your chart or charts as usual."

There is no "spare time" between now and Monday
(project deadline) for me to learn enough Excel.

What I need is the steps it takes to *drive* that process
from VB and add the chart into an RTB.  I have not interacted
significantly with Excel from VB before.

It _does_ look like having Excel save the chart to a file
is an alternative to using the clipboard, but I have no
objections to using the clipboard, and preference FOR
whatever just works.

    Bob
--
RB Smissaert - 27 Jun 2008 18:15 GMT
Start the macro recorder in Excel and create a chart. This is from the Excel
menubar:
Insert, Chart and just follow the wizard. You will need to have some data in
the sheet
to make the chart from, say a 2 column range with dates in column 1 and
values in column 2.
Stop the macro recorder and look at the produced code.
I take it you know how to start Excel from VB. Then make sure all Excel code
is fully referenced to your oXL object and also make sure all objects are
set to Nothing after
the job is done.

RBS

>> Here is an example that works with an image control:
>> http://www.j-walk.com/ss/excel/tips/tip66.htm
[quoted text clipped - 18 lines]
> Bob
> --
RB Smissaert - 27 Jun 2008 18:18 GMT
I forgot to say that maybe a RichText is not the best option here. Firstly,
because there are security problems
with the RT control in Excel and secondly I am not sure
it can accept an image file.

RBS

>> Here is an example that works with an image control:
>> http://www.j-walk.com/ss/excel/tips/tip66.htm
[quoted text clipped - 18 lines]
> Bob
> --
Bob O`Bob - 27 Jun 2008 19:01 GMT
> I forgot to say that maybe a RichText is not the best option here.
> Firstly, because there are security problems
> with the RT control in Excel and secondly I am not sure
> it can accept an image file.

There aren't any choices in that regard at this stage in the game.
Since I can use paint to create a jpg and successfully paste that
into my RTB, I don't think there are any worries there.

I just need a greater degree of "hand holding" regarding driving
the Excel object.  I can look at the VBA code generated by recording
a macro (having an Excel expert create the chart), and somehow
that just doesn't help.  I have my data in a VB string.

    Bob
--
RB Smissaert - 27 Jun 2008 20:21 GMT
OK, here is some code to get you going.
This is in a plain .exe file with nil else than this code in a Module.
All late binding, so no project reference to Excel.

Option Explicit
Private Const xlLineMarkers As Long = 65
Private Const xlColumns As Long = 2
Private Const xlLocationAsNewSheet As Long = 1

Sub Main()

 Dim oXL As Object
 Dim oWorkbook As Object
 Dim oSheet As Object
 Dim oChart As Object

 Set oXL = CreateObject("Excel.Application")
 Set oWorkbook = oXL.WorkBooks.Add
 Set oSheet = oXL.WorkSheets.Add
 Set oChart = oXL.Charts.Add

 'not sure if .SetSourceData can accept anything else than a worksheet
range
 With oSheet
   .cells(1) = "date"
   .cells(2, 1) = "01/02/2008"
   .cells(3, 1) = "01/03/2008"
   .cells(4, 1) = "01/04/2008"
   .cells(5, 1) = "01/05/2008"

   .cells(2) = "value"
   .cells(2, 2) = 2
   .cells(3, 2) = 3
   .cells(4, 2) = 3
   .cells(5, 2) = 2
 End With

 With oChart
   .ChartType = xlLineMarkers
   .SetSourceData Source:=oSheet.Range("A1:B5"), PlotBy:=xlColumns
   .Location Where:=xlLocationAsNewSheet
   .Export FileName:="C:\ChartTest.gif", FilterName:="GIF"
 End With

 'to avoid the message about saving the workbook
 oXL.DisplayAlerts = False

 'make sure to clear all Excel objects
 Set oChart = Nothing
 Set oSheet = Nothing
 Set oWorkbook = Nothing
 oXL.Quit
 Set oXL = Nothing

End Sub

RBS

>> I forgot to say that maybe a RichText is not the best option here.
>> Firstly, because there are security problems
[quoted text clipped - 12 lines]
> Bob
> --
RB Smissaert - 28 Jun 2008 07:42 GMT
Here all the essential code that is needed. Add a Form and a RTB.

Option Explicit
Private Const xlLineMarkers As Long = 65
Private Const xlXYScatter As Long = -4169
Private Const xlColumns As Long = 2
Private Const xlLocationAsNewSheet As Long = 1

Private Declare Function SendMessage Lib "user32" _
                                    Alias "SendMessageA" _
                                    (ByVal hwnd As Long, _
                                     ByVal wMsg As Long, _
                                     ByVal wParam As Long, _
                                     lParam As Any) As Long
Private Const WM_PASTE As Long = 770
Private Const WM_LBUTTONDOWN As Long = 513
Private Const WM_LBUTTONUP As Long = 514

Sub Main()

 Dim oXL As Object
 Dim oWorkbook As Object
 Dim oSheet As Object
 Dim oChart As Object

 Set oXL = CreateObject("Excel.Application")
 Set oWorkbook = oXL.WorkBooks.Add
 Set oSheet = oXL.WorkSheets.Add
 Set oChart = oXL.Charts.Add

 'not sure if .SetSourceData can accept anything else than a worksheet
range
 With oSheet
   .cells(1) = "date"
   .cells(2, 1) = "01/02/2008"
   .cells(3, 1) = "01/03/2008"
   .cells(4, 1) = "01/04/2008"
   .cells(5, 1) = "01/05/2008"

   .cells(2) = "value"
   .cells(2, 2) = 2
   .cells(3, 2) = 3
   .cells(4, 2) = 3
   .cells(5, 2) = 2
 End With

 With oChart
   .ChartType = xlXYScatter
   .SetSourceData Source:=oSheet.Range("A1:B5"), PlotBy:=xlColumns
   .Location Where:=xlLocationAsNewSheet

   'size the chart
   With .PlotArea
     .Height = 200
     .Width = 300
   End With

   '.Export FileName:="C:\ChartTest.gif", FilterName:="GIF"
   .ChartArea.Copy
 End With

 'to avoid the message about saving the workbook
 oXL.DisplayAlerts = False

 'make sure to clear all Excel objects
 Set oChart = Nothing
 Set oSheet = Nothing
 Set oWorkbook = Nothing
 oXL.Quit
 Set oXL = Nothing

 Load Form1
 Form1.Show

 'paste the clipboard image to the RTF
 SendMessage Form1.RichTextBox1.hwnd, WM_PASTE, 0, 0

 'to size and position the image; must be simpler way to do this
 SendMessage Form1.RichTextBox1.hwnd, WM_LBUTTONDOWN, 0, 0
 SendMessage Form1.RichTextBox1.hwnd, WM_LBUTTONUP, 0, 0

 'InsertPictureInRichTextBox Form1.RichTextBox1,
LoadPicture("C:\ChartTest.gif")

End Sub

Sub InsertPictureInRichTextBox(RTB As RichTextBox, Picture As StdPicture)

 Clipboard.Clear
 Clipboard.SetData Picture

 SendMessage RTB.hwnd, WM_PASTE, 0, 0

End Sub

You will need to further fill in the Excel code to specify the chart and the
only way
to do that is play with the Excel macro recorder, but that should be no
problem.
Not sure if there is a way to get the chart in the RTB without the
clipboard.
I take it there is no problem to fill the Excel range with your data in your
string.

RBS

>> I forgot to say that maybe a RichText is not the best option here.
>> Firstly, because there are security problems
[quoted text clipped - 12 lines]
> Bob
> --
Peter T - 28 Jun 2008 17:18 GMT
I didn't know you could simply paste an Excel chart (image) into an RTB.
There is another series of API's to extract the chart from the clipboard
into any control with a picture property, handy if it also has an AutoSize
property.

Following is along similar lines to RBS's but with some different Excel
methods, namely

- Data directly into series formulas (see the limitation note in the
comments, probably better in cells but just for ideas)

- Create an embedded chart, easier to size than a chart sheet

- for better re-scalability of the pasted chart and other things, use
cht.CopyPicture arg's
vs
cht.ChartArea.Copy

' in a Form with an RTB
' keep clicking the form

Option Explicit
Private Const xlLineMarkers As Long = 65
Private Const xlXYScatter As Long = -4169
Private Const xlColumns As Long = 2
Private Const xlLocationAsNewSheet As Long = 1

Private Const xlPicture As Long = -4147
Private Const xlScreen As Long = 1
Private Const xlBitmap As Long = 2

Private Declare Function SendMessage Lib "user32" _
                                    Alias "SendMessageA" _
                                    (ByVal hwnd As Long, _
                                     ByVal wMsg As Long, _
                                     ByVal wParam As Long, _
                                     lParam As Any) As Long
Private Const WM_PASTE As Long = 770
Private Const WM_LBUTTONDOWN As Long = 513
Private Const WM_LBUTTONUP As Long = 514

Dim moXL As Object, moCht As Chart

Sub ChartToRTB(Optional w As Single, Optional h As Single)
Dim vArr, x As Long
Dim oWB As Object, oWS As Object
Dim oSR As Object

   'normally better always create a new Excel instance
   ' but for developing  ...

On Error Resume Next    ' lazy just for this post
   If moXL Is Nothing Then
       Set moXL = GetObject(, "excel.application")
       '
       If moXL Is Nothing Then
           Set moXL = CreateObject("excel.application")
           moXL.Visible = True    ' for testing
       End If
       If moXL Is Nothing Then
       ' ?????????
           MsgBox Err.Description
           Exit Sub
       End If
   End If

'    Set oWB = moXL.Workbooks(sName)
'    If oWB Is Nothing Then
'        Set oWB = moXL.Workbooks.Open(sPath)
'    End If
   Set oWB = moXL.ActiveWorkbook ' for testing

   If oWB Is Nothing Then
       Set oWB = moXL.Workbooks.Add
   End If

   Set oWS = oWB.Worksheets(1)

   Set moCht = oWS.ChartObjects(1).Chart

   If moCht Is Nothing Then
       If w = 0 Then w = 270    ' points
       If h = 0 Then h = 180

       Set moCht = oWS.ChartObjects.Add(10#, 10#, w, h).Chart
       moCht.ChartType = xlXYScatter

   Else
       If w Then moCht.Parent.Width = w
       If h Then moCht.Parent.Height = h

   End If

   Set oSR = moCht.SeriesCollection(1)
   If oSR Is Nothing Then
       Set oSR = moCht.SeriesCollection.NewSeries
       oSR.Name = "Apples"
   End If
   oSR.XValues = SomeArray(5)    ' * see note
   oSR.Values = SomeArray(10)

   Set oSR = Nothing
   Set oSR = moCht.SeriesCollection(2)
   If oSR Is Nothing Then
       Set oSR = moCht.SeriesCollection.NewSeries
       oSR.Name = "Pears"
   End If

   oSR.XValues = SomeArray(5)
   oSR.Values = SomeArray(10)

   With moCht.Axes(xlCategory, xlPrimary)
       .HasTitle = True
       .AxisTitle.Text = "Ripeness"
   End With

   With moCht.Axes(xlValue, xlPrimary)
       .HasTitle = True
       .AxisTitle.Text = "Size"
   End With

   moCht.HasTitle = True
   moCht.ChartTitle.Text = "Orchard"
   moCht.HasLegend = True
   moCht.PlotArea.Fill.ForeColor.SchemeColor = 2

   moCht.CopyPicture xlScreen, xlPicture, xlScreen
   SendMessage Form1.RichTextBox1.hwnd, WM_PASTE, 0, 0

   ' * Applying an array directly to sr.Values writes values
   '  into the series formula, no need for cells
   ' BUT will fail if len("{1,2,3.1,123.456}"} > 255
   ' (ie len all the values + comma's + curly brackets > 255
   ' within one "section" of the 4-part series formula

   ' Normal way is with series data in cells, record an Excel nacro
   '  record an Excel nacro to get the syntax
End Sub

Function SomeArray(x As Long)
Dim i&
Dim vArr

   ReDim vArr(1 To 10)
   For i = 1 To 10
       vArr(i) = Int(Rnd() * x) + 1
   Next

   SomeArray = vArr

End Function

Private Sub Form_Click()
   ChartToRTB

End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
   On Error Resume Next
'    If Not moXL Is Nothing Then
'        moXL.DisplayAlerts = False
'        ' careful all workbooks'll close w/out save
'        moXL.Quit
'    End If
End Sub

Above really is for ideas only and would need a lot more.

For testing, In Excel select the chart, Shift-Edit, Copy Picture. Try the
various options then Ctrl-V into the RTB.

Also record a macro to format the chart as required, new series etc to get
all the basic syntax (select & activate stuff will need to be removed), most
of which can be adapted into DoChart above.

Regards,
Peter T
Bob O`Bob - 28 Jun 2008 18:44 GMT
Thanks very much for the examples - both of you.

With a little bit of luck in fighting the /other/ wildfires
this project faces, I may be able to install some of it later
today.  I had already experienced a small measure of success
with RB's code yesterday, but I'm pretty sure that the
combination of ideas from both of you has put me
on a track that can reach my goal.

    Bob
--
Peter T - 29 Jun 2008 10:57 GMT
Looks like I forgot to remove an Early binding reference set to Excel, just
tried the code again without one and had to include the following (in the
code below)

Private Const xlCategory As Long = 1
Private Const xlPrimary As Long = 1
Private Const xlValue As Long = 2

Dim moXL As Object, moCht As Object ' Excel.Chart

Sorry about that but it looks like you (Bob) managed to sort that out and
are now well under way.

Regards,
Peter T

"Peter T" <peter_t@discussions> wrote in message

> ' in a Form with an RTB
> ' keep clicking the form
[quoted text clipped - 143 lines]
> '    End If
> End Sub
Bob O`Bob - 29 Jun 2008 18:47 GMT
> Looks like I forgot to remove an Early binding reference set to Excel, just
> tried the code again without one and had to include the following (in the
[quoted text clipped - 8 lines]
> Sorry about that but it looks like you (Bob) managed to sort that out and
> are now well under way.

Well ... there's hope, anyway.

The previous contractor took off less than a week before the deadline, so
if I don't pull a rabbit out of this hat, it won't be the end of the world.

But my stuff is already early bound (to Excel9 and Word9 specifically) so
that little quibble didn't even get noticed.

    Bob
--
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.