DédéAmp arrive ! – part 3

Explorons encore le code de DédéAmp et voyons quels secrets des Grands Anciens celui-ci cache-t-il…

Le formulaire

C’est le gros morceau du code. Car définir un formulaire, c’est bien, lui ajouter des contrôles, c’est mieux, et des contrôles, ce n’est pas ce qui manque à DédéAmp. Nous n’allons évidemment pas tous les détailler mais pour chaque type, voir comment ceux-ci sont construits. Pour que vous puissiez bien visualiser ce dont je parle, je vous remets l’image montrant DédéAmp :

Bon ce n’est toujours pas WinAmp mais ça pète pas mal quand même !

Comme vous pouvez le constater, le formulaire contient tout un tas de choses : des labels (par exemple, « Répertoire »), des textboxes (où s’affiche le répertoire), des boutons (comme celui qui permet de sélectionner le répertoire), des groupboxes (« Piste » et « Informations »), une picturebox (la pochette de l’album) et enfin une barre de progression (la ligne verte, mais sans Tom Hanks).

J’en avais déjà parlé dans l’article sur le sampler, mais un bon rappel ne faisant jamais de mal…

Le formulaire lui-même

Non, je ne recycle pas mes intertitres moi madame. J’ai rajouté « lui-même ». Car si ce que vous voyez sur l’image ci-dessus est bien un formulaire, avant même d’ajouter des boutons et autres machins dessus, il faut d’abord le créer.

#region Formulaire
    $form = New-Object Windows.Forms.Form
    # Taille
    $form.Size = New-Object System.Drawing.Size(470,470)
    # Pour bloquer le resize du form et supprimer Maximize
    $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
    $form.MaximizeBox = $false
    $form.MinimizeBox = $true
    $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
    $form.Text = "DédéAmp Player v$version"
    $form.Icon = $formIcon
    $form.BackgroundImage=[System.Drawing.Image]::FromFile("$path\background.png")
    $form.BackgroundImageLayout = "Stretch"

Rien de plus simple, on ajoute un objet de type Windows.Forms.Form et le tour est joué. A ce stade, vous avez un formulaire (vide). Sauf qu’il faut quand même lui préciser quelques propriétés, sans cela… ben c’est pas terrible.

  • Size : la taille (en pixels) du formulaire. J’utilise pour cela un objet particulier de type System.Drawing.Size, en précisant la largeur puis la hauteur en pixels (et avec deux fois la même valeur, vous vous doutez bien que vous aurez un formulaire carré, n’incluant pas la barre de fenêtre). J’aurai très bien pu à la place préciser les propriétés .Width et .Height, mais pourquoi écrire deux lignes quand on peut n’en écrire qu’une ?
  • FormBorderStyle : le style de bordure du formulaire. J’ai pris celui par défaut, FixedDialog. Il me va très bien. Cette propriété accepte différentes valeurs, expliquées dans cet article du MSDN. Si vous voulez en changer, c’est vous qui voyez.
  • Text : le titre du formulaire (et par conséquence, de la fenêtre). J’ai décidé d’ajouter le numéro de version en passant.
  • Icon : l’icône du formulaire (et oui, de la fenêtre aussi). Il s’agit de ma tête, la variable $formIcon ayant été définie dans la région Images et icônes : il s’agit de la variable $icon.

Une petite digression : cette icône est au format .ICO de Microsoft, et non PNG ou JPEG. Il faut donc ajouter, après la conversion base64 vers image, une petite ligne toute simple :

# On convertit la chaîne base64 $icon en image $iconimage
$iconimage=base64toimage($icon)
# Et on récupère l'icône $formIcon depuis $iconimage
$formIcon = [System.Drawing.Icon]::FromHandle($iconimage.GetHicon())
  • BackgroundImage : il s’agit bien évidemment de l’image de fond du formulaire. Comme elle est plus imposante que les icônes des boutons, je ne la stocke pas directement dans le script sous forme de base64 (mais rien ne vous empêche de le faire, mais ça va vous rajouter des centaines de lignes de texte en base64). Avantage : si vous n’aimez pas la mienne, vous pouvez en mettre une autre (choisissez un fond assez clair, ou sinon changez les propriétés des autres objets en conséquence), de préférence carrée !
  • BackgroundImageLayout : il s’agit de la disposition de l’image dans le formulaire : j’ai choisi la valeur « stretch » afin d’étirer mon image sur l’ensemble de la largeur et de la hauteur du formulaire. J’ai dit il y a deux secondes qu’il fallait de préférence une image carrée (ou la largeur et la hauteur sont identiques), car sinon l’image sera déformée ! Vous pouvez sinon choisir parmi les valeurs suivantes : Center (image centrée), None (Aucun ?), Tile (en tuiles) ou Zoom. Essayez les différentes valeurs pour voir le résultat (qui peut être visuellement catastrophique, je vous préviens !)

A ce stade du programme, il ne faut surtout pas afficher le formulaire. C’est pourquoi vous trouverez la ligne suivante à la toute fin du script seulement :

# Affichage du form. Laissez ça où c'est, c'est à dire TOUT EN BAS
$form.ShowDialog()

Pourquoi ? Ben c’est simple : déjà à ce stade on n’a rien sur le formulaire, pas de bouton, de texte, rien, zéro, nada. Et si on lance la méthode ShowDialog() trop tôt, et bien on ne pourra pas faire grand chose… ShowDialog() est bloquant, c’est-à-dire que le formulaire affiché a la main sur le programme tant que ledit formulaire n’est pas fermé… C’est gênant. Donc comme indiqué, laissez cette ligne tout en bas du script, c’est un ordre.

Label de testbox a des boutons de velouuuurs

Désolé Luis Mariano. Je ne referai plus, promis. Sous cet intertitre particulièrement naze, voyons comme nous allons créer un label (une étiquette placée devant autre chose, en l’occurence ce sera une textbox), une textbox (bonjour le style redondant…) et enfin un bouton :

# Label 0
$label0 = New-Object System.Windows.Forms.Label
$label0.AutoSize = $true
$label0.Location = New-Object System.Drawing.Point(20,20)
$label0.Name = 'labeltb0'
$label0.Size = New-Object System.Drawing.Size(100,20)
$label0.Backcolor="Transparent"
$label0.Text = "Répertoire"

# TextBox 0
$textbox0 = New-Object System.Windows.Forms.TextBox
$textbox0.AutoSize = $true
$textbox0.Location = New-Object System.Drawing.Point(90,18)
$textbox0.Name = 'textbox0'
$textbox0.Size = New-Object System.Drawing.Size(310,20)
$textbox0.Text = $MusicFolder

# Bouton Parcourir
$button_browse = New-Object System.Windows.Forms.Button
$button_browse.Image=[System.Drawing.Image]::FromFile("$path\browse.png")
$button_browse.Size = New-Object System.Drawing.Size(30,24)
$button_browse.Location = New-Object System.Drawing.Point(410,16)
$button_browse.UseVisualStyleBackColor = $True
$button_browse.Name="browse"

Rien de bien sorcier là dedans, juste une explication pour la propriété Backcolor du label : si ce n’est pas transparent, l’image de fond du formulaire sera masquée par un rectangle de couleur (gris logiquement) du plus vilain effet.  Et attention à la propriété Name notamment des boutons, je vous ai parlé dans le premier article que je m’en servais pour afficher un tooltip au survol de la souris sur chaque bouton…

Nous pouvons d’ores et déjà ajouter ces trois contrôles au formulaire, car ils en sont les enfants directs :

$form.Controls.Add($label0)
$form.Controls.Add($textbox0)
$form.Controls.Add($button_browse)

Pourquoi directs ? Parce que si vous regardez bien, ces trois contrôles ne sont regroupés dans une sorte d’encadré, mais bien directement situés sur le formulaire $form, comme les boutons Play, Stop, etc… d’ailleurs. Et puisqu’on parle d’encadrer des contrôles, passons maintenant aux groupboxes.

Les groupboxes

Il n’y en a que deux, il s’agit de « Piste » et « Informations ». Voici comment les définir (je n’ai mets qu’une, l’autre c’est pareil, à part les valeurs des propriétés) :

# Groupbox Piste
$gb_track = New-Object System.Windows.Forms.GroupBox 
$gb_track.Location = New-Object System.Drawing.Size(20,50) 
$gb_track.size = New-Object System.Drawing.Size(420,190) 
$gb_track.text = " Piste " 
$gb_track.backcolor="Transparent"
$form.Controls.Add($gb_track)

Pas grand chose à en dire, si ce n’est deux petits points :

  • Notez les espaces avant et après le mot « Piste ». C’est purement cosmétique.
  • Les groupboxes sont des objets enfants de $form

Mais ces groupboxes sont destinées à devenir des objets parents d’autres objets (par exemple dans Piste, il y a les labels et textboxes Titre et Artiste). Donc quand nous allons ajouter ces contrôles, il faut penser à deux choses :

  • Le positionnement est relatif à l’objet parent : ainsi, si je crée un objet à la position (20,20), c’est 20 pixels en bas à droite du coin supérieur gauche de la groupbox, pas du formulaire lui-même
  • Il faut ajouter l’objet enfant à son parent. Par exemple, pour $label1 (Titre), cela nous donne :
$gb_track.Controls.Add($label1)

et non pas

$form.Controls.Add($label1)

Si je m’étais trompé, $label1 ne serait pas contenu dans la groupbox $gb_track mais serait à la même position que $label0, l’écrasant à moitié au passage, ce qui n’est pas très sympa !

Les boutons

Ah ça c’est du recyclage d’intertitre mais d’un article précédent, alors ça ne compte pas, na ! Pour les boutons Play, Stop, Next et Prev, que du très classique : au lieu d’utiliser la propriété Text, j’indique une propriété Image, avec au choix une image stockée en base64 et reconvertie, ou directement un nom de fichier. En programmation objet, une propriété n’est pas forcément figée à la création : dans le cas du bouton Play, une fois en lecture, je change l’icône pour celle du symbole Pause. Et bien pour cela c’est simple, je change la valeur de sa propriété Image et je demande gentiment à Windows de me rafraîchir tout ça (en gros, de le redessiner avec la bonne icône) avec la méthode Refresh() :

$button_play.Image=[System.Drawing.Image]::FromFile("$path\pause.png")
$button_play.Refresh()

J’aurai pu tout aussi bien rafraîchir le formulaire $form en entier, mais sachez que :

  • Même si c’est très rapide, cela prend plus de temps.
  • Le formulaire risque de clignoter (car tout est redessiné, y compris l’image de fond).

Donc autant que possible, ne rafraîchissez que les contrôles nécessaires, c’est-à-dire ceux qui ont réellement changé d’aspect ou de contenu. Imaginez que pour la textbox position je rafraîchisse le formulaire toutes les secondes… L’épileptique que je suis n’aurait pas supporté ça longtemps !

Il y a un bouton sur lequel je me dois de m’arrêter quelques instants : le bouton Mute ($button_mute).  Car ce n’est pas un bouton !

« Q… quoi ?! » vous entends-je dire d’ici. Et non, ce n’est vraiment pas un bouton, mais une checkbox ! Regardez plutôt :

# bouton (enfin, checkbox plutôt) Mute
$checkbox1 = New-Object System.Windows.Forms.CheckBox
$checkbox1.Size = New-Object System.Drawing.Size(30,30)
$checkbox1.Location = New-Object System.Drawing.Size(370,370)
$checkbox1.Appearance="button"
$checkbox1.Image=[System.Drawing.Image]::FromFile("$path\muteoff.png")
$checkbox1.Name="mute"
$checkbox1.add_MouseHover($help)
$form.Controls.Add($checkbox1)

« Mais… une checkbox, c’est une case à cocher ! »

Oui tout à fait, vous avez parfaitement raison. Mais tout est dans l’apparence, enfin surtout dans la propriété Appearance : je transforme cette case à cocher en bouton ! Et hop !

Pourquoi ? Tout simplement parce qu’une checkbox a deux états : cochée, ou pas cochée. Et je veux aussi que mon bouton ait deux états (son coupé ou allumé). Or la seule possibilité native pour cela est d’utiliser une checkbox, mais qui a une tête de bouton ! Et ça marche très bien ! La fonction Mute a ainsi été écrite comme ceci :

function mute
{
    if($checkbox1.Checked -eq $true)
    {
        $checkbox1.Image=[System.Drawing.Image]::FromFile("$path\muteon.png")
        $MediaPlayer.Volume = 0
    }
    else
    {
        $checkbox1.Image=[System.Drawing.Image]::FromFile("$path\muteoff.png")
        $MediaPlayer.Volume = 1
    }
}

Traduisons ceci :

  • Si $checkbox1 est cochée, alors mets l’icône du haut-parleur barré et coupe le son
  • Si $checkbox1 n’est pas cochée, alors mets l’icône normale du haut-parleur et met le son

Simple comme bonjour ! Je n’ai juste qu’à indiquer qu’en cas de changement d’état (cochée, pas cochée… mais comme c’est devenu un bouton, c’est plutôt sélectionné/pas sélectionné) de $checkbox1, il faut appeler la fonction Mute. Ceci est fait en ajoutant le gestionnaire d’événement CheckedChanged à $checkbox1 via la méthode Add_CheckedChanged() :

$checkbox1.add_CheckedChanged({mute})

Et le tour est joué !

Pour les autres boutons par contre, j’ajouterai plutôt le gestionnaire d’événement Click pour déclencher leurs fonctions respectives, via la méthode Add_Click() :

$button_play.Add_Click(
{
    play
}
)

Il s’agit de la forme explosée de la syntaxe, rien ne vous empêche de faire la même chose en une seule ligne :

$button_play.Add_Click({play})

Attention aux parenthèses et aux accolades :

  • les parenthèses définissent le paramètre de la méthode Add_Click()
  • …paramètre qui n’est autre qu’un bloc de code compris entre accolades, et qui dans notre cas appelle la fonction play (celle-ci ne nécessitant pas de paramètres, pas de parenthèses supplémentaires à écrire).

De même, pour le tooltip d’aide dont je vous ai parlé dans le premier article, je le définis pour chaque bouton comme ceci :

$button_play.add_MouseHover($help)

Car c’est l’événement MouseHover (quand la souris survole le bouton) qui m’intéresse dans ce cas.

Et voilà, la prochaine fois nous allons nous intéresser un peu plus à la logique de DédéAmp, en étudiant chaque fonction ! D’ici là, portez-vous bien et tchüss !

Montre ton amour pour Dédé en partageant cet article !

Laisser un commentaire