[VBA] Ajouter un sélecteur de couleur (color picker)
Bonjour,
Bienvenue dans ce tutoriel, où nous allons découvrir comment implémenter un sélecteur de couleurs (a.k.a. color picker🎨) en VBA pour permettre à vos utilisateurs de personnaliser les couleurs dans votre application. Pour ce faire, nous utiliserons la boîte de dialogue de sélection des couleurs fournie par Windows, accessible via l'API comdlg32.dll.
Nous verrons également comment paramétrer cette boîte de dialogue pour qu'elle s'initialise à partir d'une couleur spécifique et comment configurer une palette de couleurs personnalisées. Dans notre exemple de boite de dialogue (ci-dessous), la première ligne des couleurs personnalisés correspond aux couleurs de la configuration actuel, et la seconde ligne sauvegarde les couleurs par défaut.
Etape 1 : Création d'un module ColorPicker
Créer un nouveau module VBA, nommé ColorPicker, qui expose deux fonctions
Fonction CreatePickerColor
La fonction CreatePickerColor ouvre une boite de dialogue Windows de sélection des couleurs et retourne la couleur sélectionné par l'utilisateur. Voila sa signature.
Public Function CreatePickerColor( _
Optional DefaultColor As Variant, _
Optional CustomColorsCollection As Variant _
) As Long- Elle prend en paramètre optionnel une couleur par défaut (
DefaultColor) qui sert à initialiser le color picker sur cette couleur. - Elle prend en second paramètre (
CustomColorsCollection) une palette de couleur qui sert à configurer les couleurs personnalisées de la boîte de dialogue. - Elle renvoi la couleur sélectionné par l'utilisateur au format Long, et renvoi la valeur -1 si l'utilisateur ferme la boîte de dialogue sans sélectionner une nouvelle couleur.
Fonction LongToRGBString
La fonction LongToRGBString convertit une couleur au format Long en une chaîne de caractère RGB. Elle sert à offrir une représentation d'une couleur en format RGB, utile pour mieux visualiser et debugger, via l'utilisation d'un Debug.Print, les couleurs sélectionnées.
Public Function LongToRGBString( _
colorValue As Long _
) As StringCode complet du module ColorPicker
' ------------------------------------ '
' VBA ColorPicker ' '
' Copyright © 2024, 6i software '
' ------------------------------------ '
Option Explicit
' Windows API declarations
#If VBA7 Then
Private Declare PtrSafe Function ChooseColor Lib "comdlg32.dll" Alias "ChooseColorA" (pChoosecolor As ChooseColor) As Long
#Else
Private Declare Function ChooseColor Lib "comdlg32.dll" Alias "ChooseColorA" (pChoosecolor As ChooseColor) As Long
#End If
#If VBA7 Then
Private Type ChooseColor
lStructSize As Long
hwndOwner As LongPtr
hInstance As LongPtr
rgbResult As Long
lpCustColors As LongPtr
flags As Long
lCustData As LongPtr
lpfnHook As LongPtr
lpTemplateName As String
End Type
#Else
Private Type ChooseColor
lStructSize As Long
hwndOwner As Long
hInstance As Long
rgbResult As Long
lpCustColors As Long
flags As Long
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
#End If
' Options for ChooseColor Windows function
Private Const CC_ANYCOLOR = &H100
Private Const CC_FULLOPEN = &H2
Private Const CC_PREVENTFULLOPEN = &H4
Private Const CC_RGBINIT = &H1
'Private Const CC_ENABLEHOOK = &H10
'Private Const CC_ENABLETEMPLATE = &H20
'Private Const CC_ENABLETEMPLATEHANDLE = &H40
'Private Const CC_SHOWHELP = &H8
'Private Const CC_SOLIDCOLOR = &H80
Public Function CreatePickerColor(Optional lDefaultColor As Variant, Optional CustomColorsCollection As Variant) As Long
Dim CC As ChooseColor
Dim lRetVal As Long
Dim CustomColors(16) As Long
Dim i As Integer
If IsMissing(CustomColorsCollection) Or IsNull(CustomColorsCollection) Then
'Default predefined color, there are 16 slots available for predefined colors
CustomColors(0) = RGB(255, 255, 255)
CustomColors(1) = RGB(235, 235, 235)
CustomColors(2) = RGB(215, 215, 215)
CustomColors(3) = RGB(185, 185, 185)
CustomColors(4) = RGB(155, 155, 155)
CustomColors(5) = RGB(155, 155, 155)
CustomColors(6) = RGB(155, 155, 155)
CustomColors(7) = RGB(155, 155, 155)
CustomColors(8) = RGB(255, 255, 255)
CustomColors(9) = RGB(235, 235, 235)
CustomColors(10) = RGB(215, 215, 215)
CustomColors(11) = RGB(185, 185, 185)
CustomColors(12) = RGB(155, 155, 155)
CustomColors(13) = RGB(155, 155, 155)
CustomColors(14) = RGB(155, 155, 155)
CustomColors(15) = RGB(155, 155, 155)
Else
For i = 0 To 15
CustomColors(i) = CustomColorsCollection(i)
Next i
End If
With CC
.lStructSize = LenB(CC)
.hwndOwner = Application.hwnd
.flags = CC_ANYCOLOR Or CC_FULLOPEN Or CC_PREVENTFULLOPEN Or CC_RGBINIT
If IsNull(lDefaultColor) = False _
And IsMissing(lDefaultColor) = False Then .rgbResult = lDefaultColor 'Set the initial color of the dialog
.lpCustColors = VarPtr(CustomColors(0))
End With
lRetVal = ChooseColor(CC)
If lRetVal = 0 Then
'Cancelled by the user
CreatePickerColor = -1
Else
' Return the selected color
CreatePickerColor = CC.rgbResult
End If
End Function
Public Function LongToRGBString(colorValue As Long) As String
Dim red As Long
Dim green As Long
Dim blue As Long
red = (colorValue And &HFF)
green = (colorValue \ &H100 And &HFF)
blue = (colorValue \ &H10000 And &HFF)
LongToRGBString = "RGB(" & red & ", " & green & ", " & blue & ")"
End FunctionEtape 2 : AJOUT d'une feuille "configuration"
Dans un classeur Excel, ajouter une feuille "CONFIGURATION".
Nous allons utiliser cette feuille pour sauvegarder et gérer une palette de couleurs. La colonne C sera utilisée pour sauvegarder les couleurs personnalisées choisies par l'utilisateur, tandis que la colonne D contiendra les couleurs par défaut.
- Les cellules de la colonne C (par exemple C3, C4, C5) permettront aux utilisateurs de sauvegarder leurs choix de couleur personnalisés. Lorsqu'un utilisateur choisit une couleur via le Color Picker, la couleur sélectionnée sera automatiquement enregistrée dans ces cellules.
- La colonne D (par exemple D3, D4, D5) contiendra les couleurs par défaut. Ces couleurs sont utilisés pour l'initialisation de l'application, ou pour permettre une remise à zéro des couleurs
Etape 3 : Initialisation des évenements de la feuille
Nous commençons par utiliser l'évènement SelectionChange pour détecter quand une cellule de la plage définie est sélectionnée. Le but est de déclencher le Color Picker uniquement lorsque les cellules C3 à C5 sont sélectionnées. Remarquons qu'on aura pu opter pour une autre implémentation du color picker, par exemple par l'ajout de bouton, voir même par la création d'un UserForm spécifique avec des frames "cliquables".
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim cell As Range
Dim cellsToCheck As Range
Set cellsToCheck = Me.Range("C3:C5")
For Each cell In cellsToCheck
If Not Intersect(Target, cell) Is Nothing Then
handleColorConfiguration cell
End If
Next cell
End SubEtape 4 : TRAITEMENT DE LA sélection de la nouvelle couleur
La procédure handleColorConfiguration gère l'affichage du Color Picker et le traitement de la sélection de la nouvelle couleur.
Public Sub handleColorConfiguration(colorRange As Range)
' Récupère la palette de couleurs pour alimenter les choix du ColorPicker
Dim palette As Variant
palette = getPaletteForColorPicker()
' Récupère la couleur intérieure de la cellule
Dim currentColor As Long
currentColor = colorRange.DisplayFormat.Interior.Color
' Ouvre le color picker avec la couleur actuelle et la palette personnalisée
Dim newColor As Long
newColor = ColorPicker.CreatePickerColor(currentColor, palette)
' Vérifie si une couleur a été choisie (différente de -1)
If newColor <> -1 Then
' Affiche le code RGB de la nouvelle couleur sélectionnée dans la console VBA
Debug.Print "Nouvelle couleur sélectionnée : " & ColorPicker.LongToRGBString(newColor)
' Colore la cellule avec la nouvelle couleur sélectionnée dans le ColorPicker
colorRange.Interior.Color = newColor
End If
End SubPour personnaliser les couleurs affichées par le Color Picker, nous allons créé une palette de couleurs avec la fonction getPaletteForColorPicker. Cette fonction renvoie un tableau de couleurs personnalisées.
Public Function getPaletteForColorPicker() As Long()
Dim CustomColors(15) As Long
Dim WORKSHEET_CONFIGURATION As Worksheet
Set WORKSHEET_CONFIGURATION = ThisWorkbook.Sheets("CONFIGURATION")
CustomColors(0) = WORKSHEET_CONFIGURATION.Range("C3").DisplayFormat.Interior.Color
CustomColors(1) = WORKSHEET_CONFIGURATION.Range("C4").DisplayFormat.Interior.Color
CustomColors(2) = WORKSHEET_CONFIGURATION.Range("C5").DisplayFormat.Interior.Color
CustomColors(3) = RGB(255, 255, 255)
CustomColors(4) = RGB(235, 235, 235)
CustomColors(5) = RGB(215, 215, 215)
CustomColors(6) = RGB(185, 185, 185)
CustomColors(7) = RGB(155, 155, 155)
CustomColors(8) = WORKSHEET_CONFIGURATION.Range("D3").DisplayFormat.Interior.Color
CustomColors(9) = WORKSHEET_CONFIGURATION.Range("D4").DisplayFormat.Interior.Color
CustomColors(10) = WORKSHEET_CONFIGURATION.Range("D5").DisplayFormat.Interior.Color
CustomColors(11) = RGB(255, 255, 255)
CustomColors(12) = RGB(235, 235, 235)
CustomColors(13) = RGB(215, 215, 215)
CustomColors(14) = RGB(185, 185, 185)
CustomColors(15) = RGB(155, 155, 155)
' Renvoie le tableau de couleurs par défaut
getPaletteForColorPicker = CustomColors
End FunctionCode complet de la feuille "CONFIGURATION"
Private Sub Worksheet_SelectionChange(ByVal Target As range)
Dim cell As range
Dim cellsToCheck As range
Set cellsToCheck = Me.range("C3:C5")
For Each cell In cellsToCheck
If Not Intersect(Target, cell) Is Nothing Then
handleColorConfiguration cell
End If
Next cell
End Sub
Public Sub handleColorConfiguration(colorRange As range)
' Récupère la palette de couleurs pour alimenter les choix du ColorPicker
Dim palette As Variant
palette = getPaletteForColorPicker()
' Récupère la couleur intérieur de la celulle
Dim currentColor As Long
currentColor = colorRange.DisplayFormat.Interior.Color
' Ouvre le color picker avec la cou
Dim newColor As Long
newColor = ColorPicker.CreatePickerColor(currentColor, palette)
' Vérifie si une couleur a été choisie (différent de -1)
If newColor <> -1 Then
' Affiche le code RGB de la nouvelle couleur selectionné dans la console VBA
Debug.Print "New color selected: " & ColorPicker.LongToRGBString(newColor)
' Colorie la cellule avec la nouvelle couleur selectionné dans le ColorPicker
colorRange.Interior.Color = newColor
End If
End Sub
' Exemple d'une palette de couleur à renvoyer au ColorPicker
Public Function getPaletteForColorPicker() As Long()
Dim CustomColors(15) As Long
Dim WORKSHEET_CONFIGURATION As Worksheet
Set WORKSHEET_CONFIGURATION = ThisWorkbook.Sheets("CONFIGURATION")
CustomColors(0) = WORKSHEET_CONFIGURATION.range("C3").DisplayFormat.Interior.Color
CustomColors(1) = WORKSHEET_CONFIGURATION.range("C4").DisplayFormat.Interior.Color
CustomColors(2) = WORKSHEET_CONFIGURATION.range("C5").DisplayFormat.Interior.Color
CustomColors(3) = RGB(255, 255, 255)
CustomColors(4) = RGB(235, 235, 235)
CustomColors(5) = RGB(215, 215, 215)
CustomColors(6) = RGB(185, 185, 185)
CustomColors(7) = RGB(155, 155, 155)
CustomColors(8) = WORKSHEET_CONFIGURATION.range("D3").DisplayFormat.Interior.Color
CustomColors(9) = WORKSHEET_CONFIGURATION.range("D4").DisplayFormat.Interior.Color
CustomColors(10) = WORKSHEET_CONFIGURATION.range("D5").DisplayFormat.Interior.Color
CustomColors(11) = RGB(255, 255, 255)
CustomColors(12) = RGB(235, 235, 235)
CustomColors(13) = RGB(215, 215, 215)
CustomColors(14) = RGB(185, 185, 185)
CustomColors(15) = RGB(155, 155, 155)
' Renvoie le tableau de couleurs par défaut
getPaletteForColorPicker = CustomColors
End FunctionEnjoy ^^.
Bonus - Comment ajouter un color picker dans un UserForm ?
Introduction
En VBA, un UserForm est une interface graphique utilisateur personnalisable (a.k.a. GUI - graphical user interface) qui permet aux utilisateurs de manipuler et d'interagir de manière visuelle. Il se présente sous forme de fenêtre ou de boîte de dialogue contenant divers contrôles comme des boutons, des zones de texte, des listes déroulantes, et plus encore. Ces formulaires offrent une méthode intuitive pour obtenir des entrées utilisateur, afficher des informations et exécuter des actions spécifiques.
L'idée ici est donc d'intégrer l'utilisation d'un color picker dans une fenêtre de configuration de couleurs pour un dashboard. Cette fenêtre de configuration doit offrir la possibilité à l'utilisateur de choisir les couleurs. Pour ce faire on va créer un UserForm qui va contenir des frames (rectangles) colorisés. Chaque frame est cliquable et ouvre un color picker, permettant à l'utilisateur de choisir une nouvelle couleur. Il a également la possibilité de restaurer les couleurs par défaut.
Créer un UserForm POUR CONFIGURER LES COULEURS
En premier, ajouter un nouveau UserForm et nommé le ConfigurationColors. Ajouter dans ce dernier
- 3 frames, une pour la couleur des dépenses en R&D, une pour la couleur des bénéfices et une dernière pour la couleur des revenus
- 3 label associé au frame
- et un label (ou boutton) pour l'action restaurer les couleurs par défaut.
Ci-dessous voila les fichiers pour importer ce UserForm dans votre projet.
Regardons le code associé, étape par étape
Initialisation du UserForm
En premier, la procédure UserForm_Initialize est déclenché lors de l'initialisation de la fenêtre du formulaire utilisateur, grâce à l'évènement Initialize. Elle utilise une fonction privé qui positionne la UserForm au centre de l'écran où se trouve Excel, puis dans un second temps elle colorie les frames en fonction de la configuration des couleurs. Notons que la configuration des couleurs est sauvegardé dans la feuille "CONFIGURATION", mais qu'il est tout a fait possible de persister cette information autre part (base de registre Windows, fichier ini, fichier JSON...)
Private Sub UserForm_Initialize()
' Démarre la UserForm au millieu de l'écran où se trouve Excel
Call StartUpPositionInMiddle(Me)
' Colorie les frames en fonction des couleurs définies dans l'onglet "CONFIGURATION"
Call InitializeFramesColor
End Sub
Private Sub StartUpPositionInMiddle(paramUserForm As Object)
paramUserForm.StartUpPosition = 0
paramUserForm.Left = Application.Left + (0.5 * Application.Width) - (0.5 * paramUserForm.Width)
paramUserForm.Top = Application.Top + (0.5 * Application.Height) - (0.5 * paramUserForm.Height)
End Sub
Private Sub InitializeFramesColor()
Dim WORKSHEET_CONFIGURATION As Worksheet
Set WORKSHEET_CONFIGURATION = ThisWorkbook.Sheets("CONFIGURATION")
FrameColor1.BackColor = WORKSHEET_CONFIGURATION.range("C3").DisplayFormat.Interior.color
FrameColor2.BackColor = WORKSHEET_CONFIGURATION.range("C4").DisplayFormat.Interior.color
FrameColor3.BackColor = WORKSHEET_CONFIGURATION.range("C5").DisplayFormat.Interior.color
End SubGestion de l'interaction avec les frames cliquables
Chaque fois que l'utilisateur clique sur un des frames, le clic déclenche une fonction qui change la couleur de ce frame. Pour cela, on utilise la procédure générique HandleChangeColor qui est associé à chaque frame et qui gère tout le processus de sélection et de mise à jour des couleurs.
Private Sub FrameColor1_Click()
Call HandleChangeColor(FrameColor1, ThisWorkbook.Sheets("CONFIGURATION").range("C3"))
End Sub
Private Sub FrameColor2_Click()
Call HandleChangeColor(FrameColor2, ThisWorkbook.Sheets("CONFIGURATION").range("C4"))
End Sub
Private Sub FrameColor3_Click()
Call HandleChangeColor(FrameColor3, ThisWorkbook.Sheets("CONFIGURATION").range("C5"))
End Sub
Private Sub HandleChangeColor(frameColor As frame, rangeColor As range)
' Récupère la palette pour alimenter les couleurs personnalisé du color picker
Dim palette As Variant
palette = App.getPaletteForColorPicker
' Lecture de la précendente couleur sauvegardé dans la feuille "CONFIGURATION"
Dim previousColor As Long
previousColor = rangeColor.DisplayFormat.Interior.color
' Ouverture du color picker pour demander à l'utilisateur de choisir une nouvelle couleur
Dim newColor As Long
newColor = CreatePickerColor(previousColor, palette)
If newColor <> -1 Then
' Applique la nouvelle couleur dans la UserFrame
frameColor.BackColor = newColor
' Sauvegarde la nouvelle couleur dans l'onglet "CONFIGURATION"
rangeColor.Interior.color = newColor
' Appliquer la nouvelle couleur dans le dashboard
Call UpdateDashboard
End If
End SubMise à jour du dashboard
Lorsqu'une nouvelle couleur est sélectionné dans le color picker, on change la couleur dans la frame associé puis on sauvegarde la couleur choisie dans l'onglet "CONFIGURATION" (dans la bonne cellule). Ensuite le tableau de bord se met à jour
- en changeant la couleur de l'en-tête du tableau
- en changeant la couleur du corps du tableau. Pour cela on utilise la couleur de l'en-tête qu'on éclaircie de 40% avec la fonction
LightenColor. - en changeant la couleur des séries du graphique.
Private Sub UpdateDashboard()
Dim WORKSHEET_CONFIGURATION As Worksheet
Set WORKSHEET_CONFIGURATION = ThisWorkbook.Sheets("CONFIGURATION")
Dim WORKSHEET_DASHBOARD As Worksheet
Set WORKSHEET_DASHBOARD = ThisWorkbook.Sheets("Dashboard")
' Mise à jour des couleurs dans l'en-tête du tableau
WORKSHEET_DASHBOARD.range("C2").Interior.color = WORKSHEET_CONFIGURATION.range("C3").DisplayFormat.Interior.color
WORKSHEET_DASHBOARD.range("D2").Interior.color = WORKSHEET_CONFIGURATION.range("C4").DisplayFormat.Interior.color
WORKSHEET_DASHBOARD.range("E2").Interior.color = WORKSHEET_CONFIGURATION.range("C5").DisplayFormat.Interior.color
' Mise à jour des couleurs dans le corps du tableau (40% plus clair que l'en-tête)
WORKSHEET_DASHBOARD.range("C3:C12").Interior.color = LightenColor(WORKSHEET_CONFIGURATION.range("C3").DisplayFormat.Interior.color, 0.4)
WORKSHEET_DASHBOARD.range("D3:D12").Interior.color = LightenColor(WORKSHEET_CONFIGURATION.range("C4").DisplayFormat.Interior.color, 0.4)
WORKSHEET_DASHBOARD.range("E3:E12").Interior.color = LightenColor(WORKSHEET_CONFIGURATION.range("C5").DisplayFormat.Interior.color, 0.4)
' Mise à jour des couleurs des series du graphique
Dim chartObj As ChartObject
Dim chart As chart
Set chartObj = WORKSHEET_DASHBOARD.ChartObjects("Graphique1")
Set chart = chartObj.chart
chart.SeriesCollection(1).Format.Fill.ForeColor.RGB = WORKSHEET_CONFIGURATION.range("C3").DisplayFormat.Interior.color
chart.SeriesCollection(2).Format.Fill.ForeColor.RGB = WORKSHEET_CONFIGURATION.range("C4").DisplayFormat.Interior.color
chart.SeriesCollection(3).Format.Fill.ForeColor.RGB = WORKSHEET_CONFIGURATION.range("C5").DisplayFormat.Interior.color
End Sub
Private Function LightenColor(colorCode As Long, Optional lightenPercentage As Double = 0.2) As Long
' Extraire les valeurs RGB
Dim red As Integer
Dim green As Integer
Dim blue As Integer
red = colorCode Mod 256
green = (colorCode \ 256) Mod 256
blue = (colorCode \ 65536) Mod 256
' Rendre la couleur plus claire
red = Application.Min(red + lightenPercentage * (255 - red), 255)
green = Application.Min(green + lightenPercentage * (255 - green), 255)
blue = Application.Min(blue + lightenPercentage * (255 - blue), 255)
' Convertir les valeurs RGB en un code couleur
LightenColor = RGB(red, green, blue)
End FunctionImplémentation de la restauration des couleurs par défaut
Dans le UserForm, vous pouvez ajouter un gestionnaire d'événement pour le clic sur le label LabelResetColors (ou sur un bouton) en utilisant la procédure ci-dessous. Elle récupère les couleurs par défaut en lisant les cellules de la colonne D de la feuille "CONFIGURATION" où elles sont stockés. Puis dans un second temps, elle met à jour le dashboard avec les couleurs par défaut.
Private Sub LabelResetColors_Click()
Dim defaultColors(1 To 3) As Long
Dim CurrentFrame As control
Dim i As Integer
For i = 1 To 3
defaultColors(i) = ThisWorkbook.Sheets("CONFIGURATION").range("D" & i + 2).DisplayFormat.Interior.color
Set CurrentFrame = Me.Controls("FrameColor" & i)
CurrentFrame.BackColor = defaultColors(i)
ThisWorkbook.Sheets("CONFIGURATION").range("C" & i + 2).Interior.color = defaultColors(i)
Next i
Call UpdateDashboard
End SubHere we go !
Bonjour,
C'est pas mal comme idée, on a un résultat très propre. Que penses-tu d'un éventuel transfert dans un module de classe pour (encore) faciliter l'interaction utilisateur.
Quelque chose qui donnerait maRange.Interior.Color = colorPicker.Pick() ou bien colorPicker.ChangeColor maRange
Par rapport à ton dernier message, tu pourrais avoir quelque chose du genre
colorPicker.UpdateGraph monGraph
Dans lequel tu vas chercher les 3 premières couleurs favorites ou tu demandes à l'utilisateur d'en choisir 3.
Bon a la limite ces choses-là sont secondaires, l'idée du module de classe m'est surtout venue pour faciliter la copie d'un projet à un autre sans rajouter trop de modules.
Hello Saboh12617,
Que penses-tu d'un éventuel transfert dans un module de classe pour (encore) faciliter l'interaction utilisateur.
C'est une super idée ! Tu as tout a fait raison, et ca faciliterait clairement l'intégration dans d'autres projets.
Si je trouve (un peu de temps) je reprendrais ton idée pour en faire un add-in xlam.
Pour la partie de mise à jour d'un graphique, c'était juste pour illustrer avec un exemple concret, l'implémentation d'un colorPicker
Cordialement,