Create Windows Phone Colour Palette and Selector using WithEvents

Gustav BrockMVP
CERTIFIED EXPERT
Published:
Updated:
The Windows Phone Theme Colours is a tight, powerful, and well balanced palette.
This tiny Access application makes it a snap to select and pick a value. And it doubles as an intro to implementing WithEvents, one of Access' hidden gems.

The palette

Windows Phone sports a very tight but distinct and carefully balanced color palette. This palette is, however, well suited for many other applications where you need a modern appearance with the typical flat design introduced with Windows 8. 


The palette may be somewhat familiar to you, as I have previously used it in another article: 

Modern/Metro style message box and input box for Microsoft Access 2013


Features

If you plan to use the palette for an application, you soon get tired of converting the color values back and forth. So, to ease this task, I've created a single-form application where you can:


  • Watch the colors
  • Read the color values in the three commonly used representations
  • Click any of these values and have it copied to the clipboard


As you may expect, the layout is extremely simple:



If all you need is exactly this - select and pick a color - just run the application and go ahead.

That said, the construction of the palette could be applied to other areas, so read on.


Behind the scene

The source of the palette is the enumeration:


' Windows Phone colour enumeration.
Public Enum wpThemeColor
    ' Official colour names from WP8.
    Lime = &HC4A4&
    Green = &H17A960
    Emerald = &H8A00&
    Teal = &HA9AB00
    Cyan = &HE2A11B
    Cobalt = &HEF5000
    Indigo = &HFF006A
    Violet = &HFF00AA
    Pink = &HD072F4
    Magenta = &H7300D8
    Crimson = &H2500A2
    Red = &H14E5&
    Orange = &H68FA&
    Amber = &HAA3F0
    Yellow = &HC8E3&
    Brown = &H2C5A82
    Olive = &H64876D
    Steel = &H87766D
    Mauve = &H8A6076
    Sienna = &H2D52A0
    ' Colour name aliases from WP7.5
    Viridian = &HA9AB00
    Blue = &HE2A11B
    Purple = &HFF00AA
    Mango = &H68FA&
    ' Used for black in popups.
    Darken = &H1D1D1D
    ' Additional must-have names for grey scale.
    Black = &H0&
    DarkGrey = &H3F3F3F
    Grey = &H7F7F7F
    LightGrey = &HBFBFBF
    White = &HFFFFFF
End Enum

However, this is of little use as is  because, for any practical use in Access and VBA, other formats are required.

For example, by default, Access uses the #AABBCC format in the Format property of the Property sheet.


Also, in VBA you cannot list the values of an enumeration other than from lowest to highest. Thus, as these values cover from Black to White, all possible color values will be tested. While this can easily be done:


' Loops all(!) possible color values and prints those of the
' Windows Phone Theme Colors.
' This will take nearly 30 seconds.
'
' 2017-04-21. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function ListColors()

    Dim Color   As wpThemeColor
   
    For Color = wpThemeColor.Black To wpThemeColor.White
        If IsWpThemeColor(Color) Then
            Debug.Print Color, LiteralWpThemeColor(Color)
        End If
    Next

End Function

it takes about 30 seconds! Far too long to wait for a palette to show up.


Setting the color display

To speed it up, another method is used that fills an array with the color values:

' Fill array ColorPalette with the values of wpThemeColor.
'
' 2017-04-21. Gustav Brock, Cactus Data ApS, CPH.
'
Private Sub LoadColors()

    Dim Colors(0 To 29) As Long
   
    Dim Index           As Long
   
    If IsEmpty(ColorPalette) Then
        For Index = LBound(Colors) To UBound(Colors)
            Select Case Index
                Case 0
                    Colors(Index) = wpThemeColor.Lime
                Case 1
                    Colors(Index) = wpThemeColor.Green
                Case 2
                    Colors(Index) = wpThemeColor.Emerald
                Case 3
                    Colors(Index) = wpThemeColor.Teal
                Case 4
                    Colors(Index) = wpThemeColor.Cyan
                Case 5
                    Colors(Index) = wpThemeColor.Cobalt
                Case 6
                    Colors(Index) = wpThemeColor.Indigo
                Case 7
                    Colors(Index) = wpThemeColor.Violet
                Case 8
                    Colors(Index) = wpThemeColor.Pink
                Case 9
                    Colors(Index) = wpThemeColor.Magenta
                Case 10
                    Colors(Index) = wpThemeColor.Crimson
                Case 11
                    Colors(Index) = wpThemeColor.Red
                Case 12
                    Colors(Index) = wpThemeColor.Orange
                Case 13
                    Colors(Index) = wpThemeColor.Amber
                Case 14
                    Colors(Index) = wpThemeColor.Yellow
                Case 15
                    Colors(Index) = wpThemeColor.Brown
                Case 16
                    Colors(Index) = wpThemeColor.Olive
                Case 17
                    Colors(Index) = wpThemeColor.Steel
                Case 18
                    Colors(Index) = wpThemeColor.Mauve
                Case 19
                    Colors(Index) = wpThemeColor.Sienna
                Case 20
                    Colors(Index) = wpThemeColor.Darken
                Case 21
                    Colors(Index) = wpThemeColor.Viridian
                Case 22
                    Colors(Index) = wpThemeColor.Blue
                Case 23
                    Colors(Index) = wpThemeColor.Purple
                Case 24
                    Colors(Index) = wpThemeColor.Mango
                Case 25
                    Colors(Index) = wpThemeColor.Black
                Case 26
                    Colors(Index) = wpThemeColor.DarkGrey
                Case 27
                    Colors(Index) = wpThemeColor.Grey
                Case 28
                    Colors(Index) = wpThemeColor.LightGrey
                Case 29
                    Colors(Index) = wpThemeColor.White
            End Select
        Next
    End If
       
    ColorPalette = Colors()

End Sub

Now, with a tiny helper function, PaletteColor, a simple loop can set the BackGround of the color rectangles ("Box1" etc.) in a snap:


' Returns the color value of Index from array ColorPalette
' which holds the Windows Phone Theme Colors.
' If ColorPalette is empty, LoadColors is called to fill it.
'
' 2017-04-21. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function PaletteColor( _
    ByVal Index As Integer) _
    As Long
    
    If IsEmpty(ColorPalette) Then
        ' Fill array ColorPalette.
        LoadColors
    End If
    
    PaletteColor = ColorPalette(Index)
    
End Function


Private Sub Form_Open(Cancel As Integer)

    Dim Index   As Integer
    
    ' Set colour palette.
    For Index = 0 To 20
        Me("Box" & CStr(Index + 1)).BackColor = PaletteColor(Index)
        Me("Name" & CStr(Index + 1)).Value = LiteralWpThemeColor(PaletteColor(Index))
        Me("Css" & CStr(Index + 1)).Value = RGBHex(PaletteColor(Index))
        Me("Vba" & CStr(Index + 1)).Value = PaletteColor(Index)
        Me("Hex" & CStr(Index + 1)).Value = "&H" & Hex(PaletteColor(Index))
    Next

End Sub

The same loop is used to set the color name and the different value formats. These values are derived from two helper functions:


' Returns the literal name of the passed colour value if
' it is one of the Windows Phone Theme Colors.
'
' 2017-04-21. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function LiteralWpThemeColor( _
    ByVal Color As wpThemeColor) _
    As String

    Dim Name    As String
   
    Select Case Color
        Case wpThemeColor.Lime
            Name = "Lime"
        Case wpThemeColor.Green
            Name = "Green"
        Case wpThemeColor.Emerald
            Name = "Emerald"
        Case wpThemeColor.Teal
            Name = "Teal"
        Case wpThemeColor.Cyan
            Name = "Cyan"
        Case wpThemeColor.Cobalt
            Name = "Cobalt"
        Case wpThemeColor.Indigo
            Name = "Indigo"
        Case wpThemeColor.Violet
            Name = "Violet"
        Case wpThemeColor.Pink
            Name = "Pink"
        Case wpThemeColor.Magenta
            Name = "Magenta"
        Case wpThemeColor.Crimson
            Name = "Crimson"
        Case wpThemeColor.Red
            Name = "Red"
        Case wpThemeColor.Orange
            Name = "Orange"
        Case wpThemeColor.Amber
            Name = "Amber"
        Case wpThemeColor.Yellow
            Name = "Yellow"
        Case wpThemeColor.Brown
            Name = "Brown"
        Case wpThemeColor.Olive
            Name = "Olive"
        Case wpThemeColor.Steel
            Name = "Steel"
        Case wpThemeColor.Mauve
            Name = "Mauve"
        Case wpThemeColor.Sienna
            Name = "Sienna"
        Case wpThemeColor.Viridian
            Name = "Viridian"
        Case wpThemeColor.Blue
            Name = "Blue"
        Case wpThemeColor.Purple
            Name = "Purple"
        Case wpThemeColor.Mango
            Name = "Mango"
        Case wpThemeColor.Darken
            Name = "Darken"
        Case wpThemeColor.Black
            Name = "Black"
        Case wpThemeColor.DarkGrey
            Name = "DarkGrey"
        Case wpThemeColor.Grey
            Name = "Grey"
        Case wpThemeColor.LightGrey
            Name = "LightGrey"
        Case wpThemeColor.White
            Name = "White"
    End Select
   
    LiteralWpThemeColor = Name
   
End Function


' Returns the CSS hex representation of a decimal RGB value
' with or without a leading octothorpe.
'
' Example:
'   CSSValue = RGBHex(813466)
'   ' CSSValue = "#9A690C"
'   CSSValue = RGBHex(813466, True)
'   ' CSSValue = "9A690C"
'
' 2017-03-26. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RGBHex( _
    ByVal Color As Long, _
    Optional ByVal NoPrefix As Boolean) _
    As String
    
    ' Format of RGB hex strings.
    Const RGBPrefix As String = "#"
    
    Dim Red     As Integer
    Dim Green   As Integer
    Dim Blue    As Integer
    Dim HexRGB  As String
    
    RGBComponent Color, , Red, Green, Blue
    
    If Not NoPrefix Then
        ' Set prefix.
        HexRGB = RGBPrefix
    End If
    ' Assemble compound string with leading zeroes for small values.
    HexRGB = HexRGB & _
        Right("0" & Hex(Red), 2) & _
        Right("0" & Hex(Green), 2) & _
        Right("0" & Hex(Blue), 2)
    
    RGBHex = HexRGB
    
End Function

Nothing fancy here.


Click-n-copy

Neither does it take much to select and copy  a value of a TextBox to the clipboard:


    ' Select full content.
    TextBox.SelStart = 0
    TextBox.SelLength = Len(TextBox.Value)
    ' Display the clicked value.
    TextBox.Parent!CopyClicked.Value = TextBox.Value
    ' Copy the clicked value to the clipboard.
    DoCmd.RunCommand acCmdCopy

However, we have 3 x 20 textboxes so you would have to write 60 calls to such a helper function. A nightmare.


Now is the time to call for WithEvents


If the purpose and usage of WithEvents is new to you, sadly the official documentation is poor and sparse, nearly missing; but a good intro by John Colby can be found here: Object wrappers and Event sinks


Here we have too many controls to write them up one by one as in JCs' demo, so a collection is used to get hold of them.

This adds up in this class module:

Option Explicit

' Helper class for form Palette for event handling of textboxes.
' 2017-04-19. Gustav Brock, Cactus Data ApS, CPH.
' Version 1.0.0
' License: MIT.

' *

Private Const EventProcedure    As String = "[Event Procedure]"

Private WithEvents ClassTextBox As Access.TextBox


Public Sub Initialize(ByRef TextBox As Access.TextBox)

    Set ClassTextBox = TextBox
   
    ClassTextBox.OnClick = EventProcedure
   
End Sub


Public Sub Terminate()

    Set ClassTextBox = Nothing

End Sub


Private Sub ClassTextBox_Click()

    ' Select full content.
    ClassTextBox.SelStart = 0
    ClassTextBox.SelLength = Len(ClassTextBox.Value)
    ' Display the clicked value.
    ClassTextBox.Parent!CopyClicked.Value = ClassTextBox.Value
    ' Copy the clicked value to the clipboard.
    DoCmd.RunCommand acCmdCopy

End Sub

Yes, that is the complete module.


And to load  and unload  the class module in the form, this is the code:

Option Explicit

' Form to display the Windows Phone 7.5/8.0 colour theme.
' Also works as a basic example of implementing WithEvents for a form.
' 2017-04-19. Gustav Brock, Cactus Data ApS, CPH.
' Version 1.0.0
' License: MIT.

' *

Private ControlCollection   As Collection


Private Sub Form_Load()

    ' Load events for all colour value textboxes.
   
    Dim EventProcedure  As ClassTextboxSelect
    Dim Control         As Access.Control
   
    Set ControlCollection = New Collection
   
    For Each Control In Me.Controls
        If Control.ControlType = acTextBox Then
            Set EventProcedure = New ClassTextboxSelect
            EventProcedure.Initialize Control
            ControlCollection.Add EventProcedure, Control.Name
        End If
    Next
   
    Set EventProcedure = Nothing
    Set Control = Nothing
   
End Sub


Private Sub Form_Unload(Cancel As Integer)

    ' Unload events for all colour value textboxes.
   
    Dim EventProcedure  As ClassTextboxSelect
   
    For Each EventProcedure In ControlCollection
        EventProcedure.Terminate
    Next
   
    Set EventProcedure = Nothing
    Set ControlCollection = Nothing

End Sub

Again, this is the full code.

Compare that to write 60 times a set of code lines.


Check it out

The form is now ready for use. Open it and click any value  - and that value will be copied to the clipboard and also displayed in the textbox in the lower right corner.


Extras

The attached file and its code modules also contains a couple of helper functions that can prove useful, though not used here.


First, this function will check if a color value is equal to one of the palette colors:

' Returns True if the passed colour value is one of the
' Windows Phone Theme Colors.
'
' 2017-04-21. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function IsWpThemeColor(ByVal Color As Long) As Boolean

    Dim Item            As Integer
    Dim IsColor         As Boolean
       
    If IsEmpty(ColorPalette) Then
        ' Fill public array ColorPalette.
        LoadColors
    End If
   
    For Item = LBound(ColorPalette) To UBound(ColorPalette)
        If Color = ColorPalette(Item) Then
            IsColor = True
            Exit For
        End If
    Next
       
    IsWpThemeColor = IsColor

End Function

And the first of these two functions will convert a CSS color value of the format #AABBCC to a color value of VBA, while the other will split a VBA color value into its R, G, and B components

Please study the in-line comments for typical usage:

' Returns the numeric RGB value from an CSS RGB hex representation.
' Will accept strings with or without a leading octothorpe.
'
' Examples:
'   Color = RGBCompound("#9A690C")
'   ' Color = 813466
'   Color = RGBCompound("9A690C")
'   ' Color = 813466
'
' 2017-03-26. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RGBCompound( _
    ByVal HexRGB As String) _
    As Long
    
    ' Format of RGB hex strings.
    Const RGBPrefix As String = "#"
    Const Length    As Integer = 6
    ' Format of Hex values.
    Const HexPrefix As String = "&H"
    
    Dim Start       As Integer
    Dim Color       As Long
    
    If Mid(HexRGB, 1, 1) = RGBPrefix Then
        Start = 1
    End If
    If Len(HexRGB) = Start + Length Then
        Color = RGB( _
            HexPrefix & Mid(HexRGB, Start + 1, 2), _
            HexPrefix & Mid(HexRGB, Start + 3, 2), _
            HexPrefix & Mid(HexRGB, Start + 5, 2))
    End If
    
    RGBCompound = Color
    
End Function


' Calculate discrete RGB colours from a composite colour value and
' return one component.
' Also, by reference, return all components.
'
' Examples:
'   Simple print of the components:
'
'   SomeColor = 813466
'   RGBComponent SomeColor
'   ' Debug Print:
'   ' 154           105           12
'
'   Get one component from a colour value:
'
'   Dim SomeColor   As Long
'   Dim Green       As Integer
'   SomeColor = 13466
'   Green = RGBComponent(SomeColor, vbGreen)
'   ' Green ->  52
'
'   Get all components from a colour value:
'
'   Dim SomeColor   As Long
'   Dim Red         As Integer
'   Dim Green       As Integer
'   Dim Blue        As Integer
'   SomeColor = 813466
'   RGBComponent SomeColor, , Red, Green, Blue
'   ' Red   -> 154
'   ' Green -> 105
'   ' Green ->  12
'
' 2017-03-26. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function RGBComponent( _
    ByVal RGB As Long, _
    Optional ByVal Component As Long, _
    Optional ByRef Red As Integer, _
    Optional ByRef Green As Integer, _
    Optional ByRef Blue As Integer) _
    As Integer
    
    Dim Color   As Long
  
    If RGB <= 0 Then
        ' Return Black.
        Red = 0
        Green = 0
        Blue = 0
    Else
        ' Extract the discrete colours from the composite RGB.
        Red = RGB And vbRed
        Green = (RGB And vbGreen) / &H100
        Blue = (RGB And vbBlue) / &H10000
        ' Return chosen colour component.
        Select Case Component
            Case vbRed
                Color = Red
            Case vbGreen
                Color = Green
            Case vbBlue
                Color = Blue
            Case Else
                Color = vbBlack
        End Select
    End If
    
    ' Debug.Print Red, Green, Blue
    
    RGBComponent = Color

End Function


Downloads

The current code can be found at GitHub: VBA.ModernTheme

It contains the separate modules and also the Access 2016 accdb-file.


The full code and the Access application is attached here: ModernTheme 1.0.1.zip


I hope you found this article useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.


Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts.


Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.


4
5,118 Views
Gustav BrockMVP
CERTIFIED EXPERT

Comments (5)

DatabaseMX (Joe Anderson - Former Microsoft Access MVP)Database Architect / Application Developer
CERTIFIED EXPERT
Top Expert 2007

Commented:
Very cool Gustav !
John ClarkData Analyst

Commented:
I was looking for something EXACTLY like this, this will be my first Class Module...thanks for the push!
CERTIFIED EXPERT
Most Valuable Expert 2015
Distinguished Expert 2023

Author

Commented:
Thanks John, love to hear that.

/gustav
John ClarkData Analyst

Commented:
Gustav,

I adapted my application to this and I could not believe how efficient and easy this was to do. I see myself deleting a HUGE amount of redundant code and using Class Module in the future. They have always intimidated me and quite frankly, I did not see the use of them until I needed something similar to what you did here. The link you provided to John Colby's Blog was spot-on and VERY informative.

A question on this method however, the particular functionality I am trying to achieve applies to Text and Combo boxes. Reading JC's Blog led me to believe that I would need another Class to handle each type of Object.

Did I comprehend that correctly or can I include more than one Object in this Class?

Great stuff!

John
CERTIFIED EXPERT
Most Valuable Expert 2015
Distinguished Expert 2023

Author

Commented:
Yes, you implement many classes, perhaps one for each type of control.

Or you can modify this check:

    If Control.ControlType = acTextBox Then

to check for other control types as well. And then either apply the same or different methods.

Or do both. Or add more conditions; for example only apply a method to a control with a control source.

/gustav

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.