getImage and VBA Callback

G

Greg Maxey

Hi,

First post here. I consider myself a dabbler not a programmer. So if you
can help, please help gently ;-)

Off an on for the past year or so I have been puzzling (sometimes fretting)
over how to use the ribbon attribute getImage with a VBA callback to dispaly
a custom image on a Word ribbon control. I have seen code examples using
C+,
..Net, VB, etc., which I don't have and don't know anything about, that make
me believe that it can be done. When I try a VBA
callback like:

Sub GetImage(Control As IRibbonControl, ByRef image)
Select Case Control.ID
Case "gallery1"
image = "ContentControlBuildingBlockGallery" 'This works
Case "gallery2"
image = "CustomeImage" 'Where "CustomImage" is a png format image file
'stored in the Open Office File format zip container images folder. This
doesn't work
Case Else
'Do Nothing
End Select
End Sub

Word throws an error stating "CustomImage" is not a valid office control id.

The key it seems is a process that takes a *.png format image file and
converts it into a IPicture object that Word at least thinks is a valid
office
control id and then displays that image on the ribbon.

I have ordered Ken Puls book RibbonX hoping it will provide a cookbook
explanation of how this is done.

I am awaiting the arrival of the book, but I would certainly appreciate
learning how it is done.

Thanks.
 
B

Bob Phillips

Greg,

Shouldn't you be loading it?

Sub GetImage(Control As IRibbonControl, ByRef image)
Select Case Control.ID
Case "gallery1"
image = "ContentControlBuildingBlockGallery"
Case "gallery2"
Set image = LoadImage("CustomeImage" )
Case Else
'Do Nothing
End Select
End Sub
 
G

Greg Maxey

Bob,

Maybe I should and that looks promising. Unfortunaetly I don't know how.

Based on what you have shown, I assume that there is another procedure
involved name LoadImage. What does it look like? Where does it go?

What I have is a Open Office Format file that I opened in Office 2007 Custom
UI Editor. I used the picture icon on the editor to load a custom picture
named "CustomImage" in the images folder. I can use this image on a control
if I use the attribute image="CustomImage"

I read on one of the many blogs that I have reviewed over the last couple
days that the getImage callback only accepts a valid office conrol Id or a
IPicture object. Does the LoadImage("CustomImage") process you suggest have
some conversion process to process the image to a IPicture object?

Thanks
 
B

Bob Phillips

Yes it does, an IPicture object.

Public Function LoadImage(ByVal strFName As String) As IPicture
Dim uGdiInput As GdiplusStartupInput
Dim hGdiPlus As Long
Dim hGdiImage As Long
Dim hBitmap As Long

uGdiInput.GdiplusVersion = 1

If GdiplusStartup(hGdiPlus, uGdiInput) = 0 Then
If GdipCreateBitmapFromFile(StrPtr(strFName), hGdiImage) = 0 Then
GdipCreateHBITMAPFromBitmap hGdiImage, hBitmap, 0
Set LoadImage = ConvertToIPicture(hBitmap)
GdipDisposeImage hGdiImage
End If
GdiplusShutdown hGdiPlus
End If

End Function


--
HTH

Bob

(there's no email, no snail mail, but somewhere should be gmail in my addy)
 
B

Bob Phillips

Oops, you also need the ConvertToPicture procedure


Public Function ConvertToIPicture(ByVal hPic As Long) As IPicture

Dim uPicInfo As PICTDESC
Dim IID_IDispatch As GUID
Dim IPic As IPicture

Const PICTYPE_BITMAP = 1

With IID_IDispatch
.Data1 = &H7BF80980
.Data2 = &HBF32
.Data3 = &H101A
.Data4(0) = &H8B
.Data4(1) = &HBB
.Data4(2) = &H0
.Data4(3) = &HAA
.Data4(4) = &H0
.Data4(5) = &H30
.Data4(6) = &HC
.Data4(7) = &HAB
End With

With uPicInfo
.Size = Len(uPicInfo)
.Type = PICTYPE_BITMAP
.hPic = hPic
.hPal = 0
End With

OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic

Set ConvertToIPicture = IPic
End Function


--
HTH

Bob

(there's no email, no snail mail, but somewhere should be gmail in my addy)
 
G

Greg Maxey

Bob,

Thanks for posting back. I think I am getting close, but still not there. I
added the two functions and changed my callback as shown below. I used an
image located on my hard drive because I know how to identify it with a
string. When I try to run the code, I get and error on this line:

Dim uGdiInput As GdiplusStartupInput

Compile error user defined type not defined.

I assume that I am missing a reference, but don't know which one.

Questions:

What reference do I need to load in the project?

How would I change "C:\Test.pgn" in the callback to represent a file name
that I have already placed in the Open XML File file CustomUI\images folder?

Thanks.

Sub GetImage(control As IRibbonControl, ByRef image)
Set image = LoadImage("C:\Nose.pgn")
End Sub

Public Function LoadImage(ByVal strFName As String) As IPicture
Dim uGdiInput As GdiplusStartupInput
Dim hGdiPlus As Long
Dim hGdiImage As Long
Dim hBitmap As Long
uGdiInput.GdiplusVersion = 1
If GdiplusStartup(hGdiPlus, uGdiInput) = 0 Then
If GdipCreateBitmapFromFile(StrPtr(strFName), hGdiImage) = 0 Then
GdipCreateHBITMAPFromBitmap hGdiImage, hBitmap, 0
Set LoadImage = ConvertToIPicture(hBitmap)
GdipDisposeImage hGdiImage
End If
GdiplusShutdown hGdiPlus
End If
End Function

Public Function ConvertToIPicture(ByVal hPic As Long) As IPicture
Dim uPicInfo As PICTDESC
Dim IID_IDispatch As GUID
Dim IPic As IPicture
Const PICTYPE_BITMAP = 1
With IID_IDispatch
.Data1 = &H7BF80980
.Data2 = &HBF32
.Data3 = &H101A
.Data4(0) = &H8B
.Data4(1) = &HBB
.Data4(2) = &H0
.Data4(3) = &HAA
.Data4(4) = &H0
.Data4(5) = &H30
.Data4(6) = &HC
.Data4(7) = &HAB
End With
With uPicInfo
.Size = Len(uPicInfo)
.type = PICTYPE_BITMAP
.hPic = hPic
.hpal = 0
End With
OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic
Set ConvertToIPicture = IPic
End Function
 
B

Bob Phillips

Greg,

I'll try doing it properly and give you all of the code, not piecemeal (I
keep forgetting that you don't have the book yet!)

Option Explicit

Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type

Private Type PICTDESC
Size As Long
Type As Long
hPic As Long
hPal As Long
End Type

Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type

Private Declare Function GdiplusStartup Lib "GDIPlus" (token As Long, _
inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0)
As Long
Private Declare Function GdipCreateBitmapFromFile Lib "GDIPlus" ( _
ByVal filename As Long, bitmap As Long) As Long
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" ( _
ByVal bitmap As Long, _
hbmReturn As Long, _
ByVal background As Long) As Long
Private Declare Function GdipDisposeImage Lib "GDIPlus" ( _
ByVal image As Long) As Long
Private Declare Function GdiplusShutdown Lib "GDIPlus" ( _
ByVal token As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" ( _
PicDesc As PICTDESC, _
RefIID As GUID, _
ByVal fPictureOwnsHandle As Long, _
IPic As IPicture) As Long

Public Function LoadImage(ByVal strFName As String) As IPicture
Dim uGdiInput As GdiplusStartupInput
Dim hGdiPlus As Long
Dim hGdiImage As Long
Dim hBitmap As Long

uGdiInput.GdiplusVersion = 1

If GdiplusStartup(hGdiPlus, uGdiInput) = 0 Then
If GdipCreateBitmapFromFile(StrPtr(strFName), hGdiImage) = 0 Then
GdipCreateHBITMAPFromBitmap hGdiImage, hBitmap, 0
Set LoadImage = ConvertToIPicture(hBitmap)
GdipDisposeImage hGdiImage
End If
GdiplusShutdown hGdiPlus
End If

End Function

Public Function ConvertToIPicture(ByVal hPic As Long) As IPicture

Dim uPicInfo As PICTDESC
Dim IID_IDispatch As GUID
Dim IPic As IPicture

Const PICTYPE_BITMAP = 1

With IID_IDispatch
.Data1 = &H7BF80980
.Data2 = &HBF32
.Data3 = &H101A
.Data4(0) = &H8B
.Data4(1) = &HBB
.Data4(2) = &H0
.Data4(3) = &HAA
.Data4(4) = &H0
.Data4(5) = &H30
.Data4(6) = &HC
.Data4(7) = &HAB
End With

With uPicInfo
.Size = Len(uPicInfo)
.Type = PICTYPE_BITMAP
.hPic = hPic
.hPal = 0
End With

OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic

Set ConvertToIPicture = IPic
End Function


--
HTH

Bob

(there's no email, no snail mail, but somewhere should be gmail in my addy)
 
G

Greg Maxey

Bob,

Much progress.

I can now define and display a custom image with:

Sub GetImage(control As IRibbonControl, ByRef image)
Set image = LoadImage("C:\Test.png")
End Sub

because I know the path and name of the image file.

I use the Office 2007 Custom UI Editor for building my ribbon XML. I use
the "Insert Icon" toolbar button and import the Test.png file into the
document Open Office XML File format container. I can verify that the image
is now stored with the document by unzipping the folder and looking in the
CustomUI\Images folder.

Now I want to create a couple of buttons using the same image. On one
button I can use the static attribut image="Test" When the ribbon loads
button 1 displays the Test image.

In the other button I want to use the dynamic attribute getImage. How do I
modify:

Set image = LoadImage("C:\Test.png")

So that it loads the image named "Test" that is stored in the document
CustomUI\Images folder?

Thank you very much for helping me get this far!!


<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon>
<tabs>
<tab id="custTab1" label="Demo Tab">
<group id="Grp1" label="Demo Group">
<button id="BTN1"
label="Button 1"
image="Test"
size="large"
onAction="ButtonOnAction" />
<button id="BTN2"
label="BTN2"
getImaage="GetImage"
size="large"
onAction="ButtonOnAction" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
 
B

Bob Phillips

Everything looks good Greg, aside from one small type in the XML

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon>
<tabs>
<tab id="custTab1" label="Demo Tab">
<group id="Grp1" label="Demo Group">
<button id="BTN1"
label="Button 1"
image="Test"
size="large"
onAction="ButtonOnAction" />
<button id="BTN2"
label="BTN2"
getImage="GetImage"
size="large"
onAction="ButtonOnAction" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>

Is it not working for you?
 
G

Greg Maxey

Bob,

The part that doesn't work is when I try to identify and load and image that
is stored in the customUI\images folder of the active document.

The callback:
Sub GetImage(control As IRibbonControl, ByRef image)
Set image = LoadImage("C:\Test.png")
End Sub

Works perfectly to load an external image stored on my hard drive

How do I change that call back to display and image that is already located
in the ActiveDocument customUI\images folder?
 
G

Greg Maxey

Bob,

If it is not possible to point to a file stored in the activedocument
customUI\images folder when using the getImages callback then how would you
send a file that contained custom images on the ribbon controls (e.g.,
images located in a folder on my hardrive) to another person?

Say I have a toggle button control that I wanted to display one custom image
if pressed and another custorm image if not pressed? It seems that I would
have to use the getImage attribute?

Thanks
 
B

Bob Phillips

Greg,

The way I would do it is to store those images in the same directory, or at
most an \images sub-directory, of the actual application file. I'm an Excel
guy, so I tend to write these things as addins, and use an installer to
install them (Inno Setup) which takes care of installing everything where
you want. The user can choose the base folder, but your can direct the
installer where to laod and subsidiary files. Although I tend to use addins,
it would work just as well for non-addins.

BTW, in the situation that you describe, the XML should refer to
togglebutton, not button.

<toggleButton id="BTN1"
label="Button 1"
getImage="myGetImage"
size="large"
onAction="ButtonOnAction" />

and you would just have a boolean in your code and load one picture or
another depending upon the boolean state. You can also use a routine to get
the label rather than stick with the same


<toggleButton id="BTN1"
getLabel="myGetLabel"
getImage="myGetImage"
size="large"
onAction="ButtonOnAction" />

--
HTH

Bob

(there's no email, no snail mail, but somewhere should be gmail in my addy)
 
G

Greg Maxey

Bob,

Yes. In actual practice I would use a togglebutton control and the pressed
state to determine which image to apply.

You are way ahead of me as far a programming Word, Excel, or otherwise. I
don't really want to neccessarily write an applicaton. I just want to send
a file to someone that contains a custom toggle button. If it is pressed it
displays one image if not it displays anther. I can store both images in
the customUI\images folder of the OpenXML File container.

Are you saying that there is no way that you know of to point to those two
files using the cal getImage callback?

Thanks.
 
B

Bob Phillips

Greg,

In my contextual definition, here any document that contains code is an
application <g>. Distributing such is always a problem, and increases if you
have subsidiary/dependent files that are needed with it. OCXs and DLLs are
the worst, but the principle here is the same.

What I am saying is that I don't believe that you can access the files in
the customUI\images folder of the OpenXML File container from within your
code, so to toggle the images requires the images to be outside of your
document. I am still checking that it is a correct statement, but it is what
I believe to be the case. A shame I agree, it could open a lot of
possibilities, but that is my understanding that the ribbon is pretty
effectively locked down.

But IMO, having the images outside of the document is not that big a deal,
as long as you accept the application paradigm <bg>.
 
G

Greg Maxey

Bob,

A shame truly if what you believe is true. It doesn't seem to make much
sense. Images in the customUI\images folder are easily pointed to and
accessed with the image attribute, so it seems they should be accessible
with the getImage attribute.

Regardless you have been a tremendous help. I don't mind looking for a
needle in a haystack when I know it is there ;-)

Thanks and please let me know if you confirm your last response.
 
B

Bob Phillips

Greg,

You are right, it is easily pointed to, but it is in the XML, and isn't
exposed to VBA (AFAICS). MS have set out on a deliberate policy of stopping
us from controlling the ribbon, they call it giving full control to the
user, I call it MS deciding what the user wants.

I'll drop you an email if I find anything new at the mvp addy.
 
G

Greg Maxey

Bob,

If those images can't be accessed then to put it bluntly the scheme is not
only stupid, but broken too! IMHO.
 
V

Vladislav Ulin?k

Hi Bob,
I am just looking for solution how to get image from .ico files to MS Access 2007 ribbon button. I have tested your code and it works for .ico files, not for .png files. I dont know why but it's not important for me now.

But I would need help with picture resolution. My source image file "test.ico" resolution is 32x32. Image looks well in ImageControl on form. But when I use this same file in your code, ribbon button (size="large") picture looks misshaped. What I should change in code to keep picture proportions and get same result as in ImageControl? Any idea? Thank you
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top