Home > Code Library > SmartNetButton Control

Last Update 29 Oct 2001









SmartNetButton Control

About this page

Noticed the new flat buttons available on VS.Net IDE?

On this page you will find a free control that mimics the .Net button behaviour, plus an article that describes how to build such a control.


SmartNetButton Demo

Requires SmartSubclass


The first thing I noticed when I first opened Microsoft .Net Beta was its new flat buttons. When the button is flat it uses a smooth color to show the picture and when the mouse is over it, draws a blue rectangle and also adds a nice shadow. This gives the effect of having a floating picture when the button is raised.

I thought it would be a good idea to have a control that would allow me to have '.Net' buttons, and this is what this page is all about: how to build a '.Net' button.

Because the only difference between 'SmartNetButton' and 'SmartButton' is in its drawing behaviour, I would suggest you read my article 'How to implement a flat button' if you want to get more information about how to build a flat button.

This article will focus on how we can add the '.Net' special effects to a flat button and will also describe the properties, methods and events that are available on my free control 'SmartNetButton'.

Smooth Colors

How can we 'smooth' the button picture when the mouse isn't over the button? This was my first question.

My first thought was that we could have two picture properties, one to be used when the button is flat and the other one to be painted when the mouse is over the button. This would obviously work but... it would be a time-consuming solution: every time we want to change the picture we have to 'smooth' it again.

So then I decided to find out what kind of 'rule' Microsoft uses to smooth the pictures and, after doing several screenshots of 'VS.Net', I found there was a relationship between the original and final colors for every pixel.

Button Flat and Smooth

Button Raised and Normal

As you can see above, the open-folder picture comprises mainly four colors: yellow, white, black and brown. If we compare the original color with its smooth version, we come up with following table:

Smooth Color Original Color Increment
Black RGB(76,76,76) RGB(0,0,0) (76,76,76)
Brown RGB(166,166,76) RGB(128,128,0) (38,38,76)
Yellow RGB(255,255,76) RGB(255,255,0) (0,0,76)
White RGB(255,255,255) RGB(255,255,255) (0,0,0)

We can get new points of reference by repeating the same process to other icons:

Smooth Color Original Color Increment
Grey RGB(211,211,211) RGB(192,192,192) (19,19,19)
Blue RGB(76,76,166) RGB(0,0,128) (76,76,38)
Light Blue RGB(51,102,255) RGB(108,140,255) (57,38,0)
Light Orange RGB(255,204,102) RGB(255,223,140) (0,19,38)

Seems clear that there's a relationship between the original and final colors. This relationship is always expressed in terms of RGB increment. If we now break-down this relationship we come-up with the following table:

R/G/B Value Increment
0 76
32 57
64 57
96 38
128 38
160 19
192 19
224 0
256 0

Things are now getting easy. Now we only need to express this relationship through a mathematical formula:

Private Function pTransform(ByVal X As Long) As Long
      pTransform = 76 - Int((X + 32) / 64) * 19
End Function

What's next then? we have a formula to transform a sub-color (red, green or blue) into a new smoothed sub-color. The next step is to use this formula to transform a whole picture.

As far as I know, VB6 doesn't provide a way for accessing individual pixels of an image. Therefore, we will have to use native APIs. You can find below a VB sample of a function that receives a picture as a parameter and returns the picture handle of the new smoothed picture.

Public Function fSmoothPicture(ByRef m_Picture As StdPicture) As Long

' Local Variables...
Dim memDc1 As Long, memDc2 As Long
oldBitmap1 As Long, oldBitmap2 As Long
memBitmap As Long
x As Long, y As Long, w As Long, h As Long, c As Long
' Get the Width and Height of the picture...
w = UserControl.ScaleX(m_Picture.Width, vbHimetric, vbPixels)
    h = UserControl.ScaleY(m_Picture.Height, vbHimetric, vbPixels)
    ' Create two device contexts in memory...
memDc1 = CreateCompatibleDC(UserControl.hDC)
    memDc2 = CreateCompatibleDC(UserControl.hDC)
    ' Create a bitmap in memory...
memBitmap = CreateCompatibleBitmap(UserControl.hDC, w, h)

    ' Select m_Picture in device1 and the new bitmap in device2...
oldBitmap1 = SelectObject(memDc2, m_Picture.Handle)
    oldBitmap2 = SelectObject(memDc2, memBitmap)
    ' Copy each pixel from device1 to device2
    ' and apply a color transformation...
For x = 0 To w - 1
        For y = 0 To h - 1
            c = GetPixel(memDc1, x, y)
            SetPixel memDc2, x, y, c + pMask(c)
        Next y
    Next x
    ' Restore the original bitmaps...
SelectObject memDc1, oldBitmap1
    SelectObject memDc2, oldBitmap2
    ' Delete device context...
DeleteDC memDc1
    DeleteDC memDc2
    ' Return the new bitmap handle...
fSmoothPicture = memBitmap
End Function

This function works only for bitmaps. If the function parameter 'm_Picture' holds an icon or a complex image, you will have to render the image on the target device context before applying the transformation. 

You have seen in the above code that we're using a function, called 'pMask()', to transform each pixel color. What this function does is to split the color into its RGB components, transform each sub-color and return the new smooth color.

Well, we have the first '.Net' effect sorted out. In the next section we our going to discuss how to add a dynamic shadow to the picture.

How to add a shadow to the picture

As I said in the introduction, the '.Net' button adds a shadow to the picture when the button is raised. We're now going to see how we can easily implement  this effect.

First of all, we've seen that it is a bad idea to have 2 picture properties in our control. So, how then can we add a shadow to the picture? The answer is "by using the API DrawStatus". 

DrawStatus is a very powerful API that allows us to draw a picture on a device context. We can draw the picture using a 'normal' mode or, by using its flag DSS_MONO, draw the picture using a single color. We can use this function to draw a greyed version of the picture (this will be the shadow) to then draw the normal picture 2 pixels away so it will seem to be floating over the canvas.

Draw the picture using a grey 
color to simulate a shadow.

The following VB method draws a picture on the usercontrol device context. The picture will be drawn normal or greyed depending on the value of its parameter 'bShadow'.

Private Sub fDrawPicture( _
    ByRef m_Picture As StdPicture, _
    ByVal X As Long, _
    ByVal Y As Long, _
    ByVal bShadow As Boolean)
    Dim lFlags As Long
hBrush As Long
    Select Case
        Case vbPicTypeBitmap
            lFlags = DST_BITMAP
        Case vbPicTypeIcon
            lFlags = DST_ICON
        Case Else
    End Select
bShadow Then
hBrush = CreateSolidBrush(RGB(128, 128, 128))
    End If
DrawState _
        UserControl.hDC, _
        IIf(bShadow, hBrush, 0), _
        0, _
        m_Picture.Handle, _
        0, _
        X, Y, _
        UserControl.ScaleX(m_Picture.Width, vbHimetric, vbPixels), _
        UserControl.ScaleY(m_Picture.Height, vbHimetric, vbPixels), _
        lFlags Or IIf(bShadow, DSS_MONO, DSS_NORMAL)
    If bShadow Then
DeleteObject hBrush
    End If
End Sub

About the free .Net control

SmartNetButton is a free control that mimics the '.Net' flat button behaviour. You can freely use it in your applications.

The control has two main items: a Caption property and a Picture property. You can set whether to show the Caption or not through property ShowCaption

Each item has its own displaying area:

The size of the caption area is set as a % of the total size and we use property CaptionAreaPercent to change this value. You can place the caption area to the left, right, top or bottom of the picture area. You can change this position using property CaptionAreaLayout

Each individual area (caption or picture) has its own properties to help you positioning. Each area is divided into 9 possible layouts:

You can use properties CaptionLayout and  PictureLayout to change the position of each item within its display area.

There are also 4 properties for each area that will help you to better tune the layout. These properties set the offset between each position and the edge of its display area. You have CaptionOffsetLeft, CaptionOffsetTop, CaptionOffsetRight and CaptionOffsetBottom for the caption. You have PictureOffsetLeft, PictureOffsetTop, PictureOffsetRight and PictureOffsetBottom for the picture:

Finally, you can find below a table containing all SmartNetButton properties, methods and events:

Methods Events
Drag (1) Click
Move (1) DragDrop
ReleaseButton (1) DragOver
ShowWhatsThis (1) Hide
ZOrder (1) MouseDown
(1) MouseMove
(1) MouseUp
(1) Resize
(1) Show
(1) Container
(1) DragIcon
(1) DragMode
(1) Enabled
(1) Font
(1) ForeColor
(1) Height
(1) Index
(1) Left
(1) Name
(1) Object
(1) Parent
(1) Tag
(1) ToolTipText
(1) Top
(1) Visible
(1) WhatsThisHelpID
(1) Width

(1) Please read Visual Basic documentation for a complete description of this property/method/event.


   Last Changes  


  • Application wasn't closing properly when VB 'End' statement was called from 'Click' event.
    This bug is now fixed.

    Private Sub SmartNetButton1_Click()
    End Sub

    Thanks to Victor Henskens for finding and reporting this bug.


  • SmartNetButton didn't support access keys (ALT+Key).
    This bug is now fixed. Event AccessKeyPress is fired when the access key is pressed.

    Thanks to Jan Tielens for finding and reporting this bug.


  • SmartNetButton didn't respond to keyboard events.
    This bug is now fixed. The button now responds to keyboard events just as it does to mouse events.

    Thanks to José Aguado for finding and reporting this bug.


  • SmartNetButton was always 'popped' when it got the focus.
    This bug is now fixed. Property 'TabStop' has now a default value of False and there's a new property 'RespondToKeyEvents' that sets whether or not the control responds to keyboard events. If we want the button to show a push/pop effect when it gets/loses the focus, we must explicitly set both properties to True.

    Thanks to David Moņino for finding and reporting this bug.


  • SmartNetButton was giving an error with MS Access.
    This bug is now fixed.

    Thanks to Ken Young for finding and reporting this bug.


  • SmartNetButton wasn't refreshing properly if DoEvents() was called from its event Click().
    This bug is now fixed. The button now doesn't lose its 'push/pop' effect when DoEvents() is called.

    Thanks again to David Moņino for finding and reporting this bug.

Copyright © 2001, Andrés Pons (andres@vbsmart.com). All rights reserved.