In the first part of this tutorial, we built a basic custom button control using the DesktopCanvas
class, giving it a custom appearance and handling the basic mouse events to provide visual feedback and raise a Pressed
event when clicked.
In this second part, we will enhance our CanvasButton
by adding more customization with new properties to:
- set the button’s colors for different states (default, hovered, pressed)
- adjust the corner radius of the button
- implement a disabled state

Let’s get started!
Adding New Properties for Customization
We need to add several properties to our CanvasButton
class to store the custom colors for different states, the border color, the text color, the corner radius, and the enabled state.
- In the Project Navigator, select your
CanvasButton
class. - Go to Insert > Property (or right-click the class and select Add to “CanvasButton” > Property).
- Add the following properties with the specified names, types, and default values:
- Name:
BackgroundColor
Type:Color
Default Value:&c5e2d8b
- Name:
HoverColor
Type:Color
Default Value:&c729fcf
- Name:
HighlightColor
Type:Color
Default Value:&c628eff
- Name:
BorderColor
Type:Color
Default Value:&c525252
- Name:
TextColor
Type:Color
Default Value:&ceeeeee
- Name:
CornerRadius
Type:Integer
Default Value:4
- Name:
These properties will hold the values that determine the button’s appearance and behavior.
Updating Event Handlers for the Enabled State
We need to modify the existing mouse event handlers (MouseDown
, MouseEnter
, MouseExit
, MouseUp
) to check if the button is Enabled
before processing any mouse actions.
Select each of the following event handlers in the CanvasButton
class and update the code as shown:
MouseDown(x As Integer, y As Integer) As Boolean
#Pragma unused x
#Pragma unused y
// Only process if the button is enabled.
If Enabled Then
// Set internal state to indicate the button is being pressed.
IsPressed = True
// Refresh the control to show the pressed state visually.
Refresh(False)
// Return True to indicate that this event was handled.
Return True
Else
// If disabled, do not handle the event.
Return False
End If
MouseEnter()
// Only process if the button is enabled.
If Enabled Then
// Set internal state to indicate the mouse is hovering over the button.
IsHovered = True
// Refresh the control to show the hover state visually.
Refresh(False)
End If
MouseExit()
// Only process if the button is enabled.
If Enabled Then
// Set internal state to indicate the mouse is no longer hovering.
IsHovered = False
// Reset pressed state if mouse leaves while pressed (prevents accidental clicks).
IsPressed = False
// Refresh the control to revert from the hover state.
Refresh(False)
End If
MouseUp(x As Integer, y As Integer) As Boolean
#Pragma unused x
#Pragma unused y
// Only process if the button is enabled.
If Enabled Then
// Check if the button was pressed down AND the mouse is still hovering over it.
If IsPressed And IsHovered Then
// If true, the button was successfully clicked. Raise the custom Pressed event.
RaiseEvent Pressed
End If
// Reset the pressed state regardless of whether the click was successful.
IsPressed = False
// Refresh the control to revert from the pressed state.
Refresh(False)
End If
In these updated event handlers, we added an If Enabled Then
check at the beginning. If the button is not enabled, the code inside the If
block is skipped, preventing the button from reacting to mouse interactions. We also added #Pragma unused
to the parameters x
and y
in MouseDown
and MouseUp
as they are not used in the code, which helps avoid compiler warnings. In MouseExit
, we added IsPressed = False
to ensure the pressed state is reset if the mouse leaves the button while the mouse button is held down.
Updating the Paint Event for Enhanced Appearance
The most significant changes will be in the Paint
event where we will use the new properties to draw the button based on its current state (enabled/disabled, hovered, and pressed).
Select the Paint(g As Graphics, areas() As Rect)
event handler and replace its content with the following code:
#Pragma unused areas
// Use the custom CornerRadius property.
Var currentCornerRadius As Integer = CornerRadius
// Declare variables for the colors used in drawing.
Var currentBgColor As Color
Var currentBorderColor As Color
Var currentTextColor As Color
// Determine colors based on the button's current state (enabled, pressed, hovered).
If Enabled Then
If IsPressed Then
// Use highlight color if pressed.
currentBgColor = HighlightColor
currentBorderColor = BorderColor
currentTextColor = TextColor
ElseIf IsHovered Then // Check for hover only if not pressed
// Use hover color if hovered.
currentBgColor = HoverColor
currentBorderColor = BorderColor
currentTextColor = TextColor
Else
// Use the custom background color for the default state.
currentBgColor = BackgroundColor
currentBorderColor = BorderColor
currentTextColor = TextColor
End If
Else
// Use appropriate system or standard gray colors for the disabled state.
currentBgColor = Color.LightGray
currentBorderColor = Color.Gray
currentTextColor = Color.DisabledTextColor // Use system disabled text color
End If
// Set the drawing color and draw the background shape with rounded corners.
g.DrawingColor = currentBgColor
g.FillRoundRectangle(0, 0, g.Width, g.Height, currentCornerRadius, currentCornerRadius)
// Set the drawing color and pen size for the border.
g.DrawingColor = currentBorderColor
g.PenSize = 2
// Draw the border shape just inside the background rectangle.
g.DrawRoundRectangle(1, 1, g.Width-2, g.Height-2, currentCornerRadius, currentCornerRadius)
// Enable anti-aliasing for smoother text rendering.
g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
g.AntiAliased = True
// Calculate the width and height of the button text.
Var tw As Double = g.TextWidth(ButtonText)
Var th As Double = g.TextHeight
// Calculate the X position to center the text horizontally.
Var tx As Double = (g.Width - tw) / 2
// Calculate the Y position to center the text vertically, with a small adjustment.
Var ty As Double = (g.Height + th) / 2 - 3
// Set the drawing color for the text.
g.DrawingColor = currentTextColor
// Draw the button text at the calculated centered position.
g.DrawText(ButtonText, tx, ty)
Let’s break down the changes in the Paint
event:
- We now use the
CornerRadius
property instead of a static constant for drawing the rounded rectangles. - We introduced an
If Enabled Then ... Else
block. - Inside the
If Enabled Then
block, we have a nestedIf IsPressed Then ... ElseIf IsHovered Then ... Else
structure. This determines thecurrentBgColor
:- If
IsPressed
is True,currentBgColor
is set toHighlightColor
. - If
IsPressed
is False butIsHovered
is True,currentBgColor
is set toHoverColor
. - If neither
IsPressed
norIsHovered
is True,currentBgColor
is set toBackgroundColor
. - The
currentBorderColor
andcurrentTextColor
are set directly from theBorderColor
andTextColor
properties when the button is enabled.
- If
- Inside the
Else
block (whenEnabled
is False), we set the colors to standard gray values (Color.LightGray
for background,Color.Gray
for border, andColor.DisabledTextColor
for text) to give the button a disabled appearance. - Finally, the drawing commands use these
currentBgColor
,currentBorderColor
,currentTextColor
, andcurrentCornerRadius
variables.
Using the Enhanced Custom Control
Now that our CanvasButton
has more customization options and supports a disabled state, let’s see how to use these features.
- If you don’t already have an instance, drag the
CanvasButton
class from the Project Navigator onto a window in the Layout Editor. - You can now access and set the new properties programmatically. For example, in a button’s
Opening
event, you could add code like this:
// Customize colors
Me.BackgroundColor = Color.RGB(200, 50, 50) // A shade of red
Me.HoverColor = Color.RGB(255, 100, 100) // A lighter red for hover
Me.HighlightColor = Color.RGB(150, 0, 0) // A darker red for pressed
Me.BorderColor = Color.Black
Me.TextColor = Color.White
// Adjust corner radius
Me.CornerRadius = 10
You can experiment with different values to see how they affect the button’s appearance. To test the disabled state, you can set the Enabled
property to False
.
Conclusion
We’ve now taken our custom button to the next level by adding extensive color customization, adjustable corners, and a disabled state.
This project can be downloaded from GitHub at: https://github.com/xolabsro/CanvasButton
Hope you enjoyed making the button your own! While it’s much more flexible now, there might be other features we could add down the road, so stay tuned!
Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!