Results 1 to 6 of 6

Thread: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassing

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,652

    [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassing

    Most of the solutions to place an image on a button either use a control or owner drawing. If all you want is a simple image button, with the image on the left and text on the right, it turns out all you need to do is call BM_SETIMAGE. Don't even need to set the style to graphical, or change the style with API. Transparency is preserved, and the button style doesn't change like it does if you set it to 'graphical' in vb6; so if you're using xp style manifests the button still stays that style.

    A bonus with this sample, it shows how the icon you use can be stored in a resource file, as a custom resource, which bypasses VB's limitations. You can use any valid Windows icon, with any size (or multiple sizes) and any color depth, and even more, this sample will load the size closest to what you requested.

    To use this sample, create a project with a form with a command button, and a module. Add a new resource file, then choose add custom resource (NOT icon or bitmap), and name it something like "ICO_01" as the id.

    Then, this code is for the form, and all you need is this one line for any command button:

    Code:
    Option Explicit
    
    Private Sub Form_Load()
    
    Call SendMessage(Command1.hWnd, BM_SETIMAGE, IMAGE_ICON, ByVal ResIconTohIcon("ICO_01"))
    
    End Sub
    and this is the code for the module:

    Code:
    Option Explicit
    
    
    Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
                                                                        Source As Any, _
                                                                        ByVal Length As Long)
                                                                        
    Public Declare Function CreateIconFromResourceEx Lib "user32.dll" (ByRef presbits As Any, _
                                                                        ByVal dwResSize As Long, _
                                                                        ByVal fIcon As Long, _
                                                                        ByVal dwVer As Long, _
                                                                        ByVal cxDesired As Long, _
                                                                        ByVal cyDesired As Long, _
                                                                        ByVal flags As Long) As Long
    
    Public 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 Type IconHeader
        ihReserved      As Integer
        ihType          As Integer
        ihCount         As Integer
    End Type
    
    Private Type IconEntry
        ieWidth         As Byte
        ieHeight        As Byte
        ieColorCount    As Byte
        ieReserved      As Byte
        iePlanes        As Integer
        ieBitCount      As Integer
        ieBytesInRes    As Long
        ieImageOffset   As Long
    End Type
    
    Public Const BM_SETIMAGE = &HF7
    Public Const IMAGE_BITMAP = 0
    Public Const IMAGE_ICON = 1
    
    Public Function ResIconTohIcon(id As String, Optional cx As Long = 24, Optional cy As Long = 24) As Long
    'returns an hIcon from an icon in the resource file
    'For unknown reasons, this will not work with the 'Icon' group in the res file
    'Icons must be added as a custom resource
    
        Dim tIconHeader     As IconHeader
        Dim tIconEntry()    As IconEntry
        Dim MaxBitCount     As Long
        Dim MaxSize         As Long
        Dim Aproximate      As Long
        Dim IconID          As Long
        Dim hIcon           As Long
        Dim I               As Long
        Dim bytIcoData() As Byte
        
    On Error GoTo e0
    
        bytIcoData = LoadResData(id, "CUSTOM")
        Call CopyMemory(tIconHeader, bytIcoData(0), Len(tIconHeader))
    
        If tIconHeader.ihCount >= 1 Then
        
            ReDim tIconEntry(tIconHeader.ihCount - 1)
            
            Call CopyMemory(tIconEntry(0), bytIcoData(Len(tIconHeader)), Len(tIconEntry(0)) * tIconHeader.ihCount)
            
            IconID = -1
               
            For I = 0 To tIconHeader.ihCount - 1
                If tIconEntry(I).ieBitCount > MaxBitCount Then MaxBitCount = tIconEntry(I).ieBitCount
            Next
    
           
            For I = 0 To tIconHeader.ihCount - 1
                If MaxBitCount = tIconEntry(I).ieBitCount Then
                    MaxSize = CLng(tIconEntry(I).ieWidth) + CLng(tIconEntry(I).ieHeight)
                    If MaxSize > Aproximate And MaxSize <= (cx + cy) Then
                        Aproximate = MaxSize
                        IconID = I
                    End If
                End If
            Next
                       
            If IconID = -1 Then Exit Function
           
            With tIconEntry(IconID)
                hIcon = CreateIconFromResourceEx(bytIcoData(.ieImageOffset), .ieBytesInRes, 1, &H30000, cx, cy, &H0)
                If hIcon <> 0 Then
                    ResIconTohIcon = hIcon
                End If
            End With
           
        End If
    
    On Error GoTo 0
    Exit Function
    
    e0:
    Debug.Print "ResIconTohIcon.Error->" & Err.Description & " (" & Err.Number & ")"
    
    End Function
    Thanks to Leandro Ascierto for the basis of the code to load an icon from a resource file into memory.

    Requires Common Controls 6.0 manifest, CommandButton style must be Normal, NOT Graphical.
    Attached Files Attached Files
    Last edited by fafalone; May 4th, 2014 at 10:37 PM.

  2. #2
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassin

    Does not work on themed XP. When not themed seems to work on NT and newer.

    For themed XP I'm using WM_NOTIFY/NM_CUSTOMDRAW on button's owner to draw the button in CDDS_PREPAINT stage.

    cheers,
    </wqw>
    Last edited by wqweto; May 3rd, 2014 at 08:35 AM. Reason: clarification

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,652

    Re: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassin

    Can you clarify, on XP have you tried it when you're not using OwnerDraw?

    The BM_SETIMAGE (and all other API) lists compatibility as Win2000 Pro and above; I'll look into whether it behaves differently on earlier systems but do let me know if you've tried non-owner draw themed xp. The platform I made it on was Win7 x64 ultimate with common control manifests for both ide and compiled (which, and I'll edit the first post to reflect this, is a requirement-- doesn't work without CC6 manifest).

  4. #4
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassin

    Quote Originally Posted by fafalone View Post
    A bonus with this sample, it shows how the icon you use can be stored in a resource file, as a custom resource, which bypasses VB's limitations.
    Icon resources seems to work just fine when loaded via the LoadImage function. The attached demo project below shows 4 ways of associating an icon with a CommandButton by sending the BM_SETIMAGE message.

    Name:  BM_SETIMAGE.png
Views: 3736
Size:  5.4 KB

    Quote Originally Posted by wqweto View Post
    Does not work on themed XP.
    Someone mentioned the following in the Community Additions section of the BM_SETIMAGE message:

    Quote Originally Posted by Jakub Nietrzeba
    BM_SETIMAGE on button without BS_ICON or BS_BITMAP flag has no effect on Windows XP
    Attached Files Attached Files
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,652

    Re: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassin

    Icon resources seems to work just fine when loaded via the LoadImage function.
    The issue isn't loading an icon resource once it's there... the issue is VB will not let you add icons with large sizes and color depths to the icon resources group. Maybe you could force it in with an external resource editor, but this is a much easier solution.

  6. #6
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: [VB6] CommandButton with image and text: No UCs, ActiveX, or OwnerDraw/subclassin

    Quote Originally Posted by fafalone View Post
    ... the issue is VB will not let you add icons with large sizes and color depths to the icon resources group.
    Or more accurately, the IDE's Resource Editor add-in doesn't know about such icon sizes and depths, and consequently doesn't allow them.

    Quote Originally Posted by fafalone View Post
    Maybe you could force it in with an external resource editor, but this is a much easier solution.
    There's no need to resort to third-party resource editors since the underlying resource compiler (RC.EXE) is already capable of compiling the newer icon sizes and depths. So, instead of managing resources through the IDE's very limited Resource Editor add-in, you just need to write a resource script (and optionally, a batch file) and then compile the resource file and add it manually to a VB6 project. Sure, there are more steps involved, but the benefits are definitely worth it. Personally, I only use the IDE's Resource Editor for quick projects that I won't be saving. For most projects, I prefer to manually create a resource file and add it to the project. The attached demo project in this post has an example of how I usually compile icon resources.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width