insert an Excel graph into a Richtextbox?
|
|
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 --
|
|
|