Je viens de terminer une formation WPF et une fois le chapitre sur le binding de données terminé, un stagiaire me pose une colle, à peu près dans ce genre là :
"Il existe beaucoup d'applis de démos en WPF où l'on peut facilement changer l'angle de la caméra, à la souris ou autrement, mais comment faire pour binder la lumière, sur un contrôle Slider par exemple ?"
Bon, j’avais bien une idée en tête, mais sur le coup, je ne voyais pas comment l’implémenter. Je me suis mis au boulot en rentrant, et au bout d’une demi heure, j’ai terminé une petite application WPF que
voici…
Décortiquons un peu le contenu.
On commence par jeter un œil à la définition de la lumière en XAML, ce qui donne ceci :
<DirectionalLight x:Name="light" Color="White"
Direction="-5,-5,-5/>
On creuse un peu et on se rend compte que la propriété Direction est de type Vector3D, défini dans System.Windows.Media.Media3D, et qu’elle contient 3 valeurs : X, Y et Z (entre autres). La valeur « -5,-5,-5 » ne serait donc que le résultat de l’appel à la méthode ToString() sur la classe Vector3D.
On réalise que ça va être un peu dur (ou un peu long) de binder ça sur 3 contrôles différents, ou 3 sources différentes. Je commence donc par créer un UserControl, que j’appelle VectorControl et qui contient 3 contrôles de type Slider. J’ajoute les DependencyProperties qui me permettent de lier les valeurs des Sliders à mes propres propriétés : X, Y, Z et Vector3D, et j’ajoute les notifications correspondantes.
// dependencies
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double),
typeof(VectorControl), new UIPropertyMetadata((double)0));
...
public static readonly DependencyProperty Vector3DProperty =
DependencyProperty.Register("Vector3D", typeof(Vector3D),
typeof(VectorControl), new PropertyMetadata(
new Vector3D(0, 0, 0)));
// constructeur
public VectorControl() {
InitializeComponent();
DependencyPropertyDescriptor.FromProperty(XProperty,
typeof(VectorControl)).AddValueChanged(this,
delegate { Vector3D = new Vector3D(X, Y, Z); });
...
DependencyPropertyDescriptor.FromProperty(Vector3DProperty,
typeof(VectorControl)).AddValueChanged(this,
delegate {
X = Vector3D.X; Y = Vector3D.Y; Z = Vector3D.Z; });
}
Au final, j’obtient un UserControl qui ne paye pas de mine, mais qui est fully functionnal !
L’étape suivante consiste à intégrer mon UserControl dans mon application, qui contient déjà un ViewPort3D hébergeant ma scène 3D, et à renseigner ses propriétés. Au passage, j’en ai profité pour en créer 2 :
• Un pour la lumière
• Un pour la caméra (puisque la direction est aussi une propriété de type Vector3D)
<!-- les usercontrol ici -->
<StackPanel Orientation="Vertical" Grid.Column="0">
<local:VectorControl x:Name="lightVectorControl"
Title="Lumière ambiente" X="-4" Y="-2" Z="-1"/>
<local:VectorControl x:Name="lookDirectionVectorControl"
Title="Caméra" X="-5" Y="-5" Z="-5"/>
</StackPanel>
Il ne me reste plus qu’à modifier la ligne de définition de la lumière (et celle de la caméra), et de binder la propriété Direction à mon UserControl, ce qui donne ceci :
<DirectionalLight x:Name="light" Color="White"
Direction="{Binding ElementName=lightVectorControl,
Path=Vector3D}"/>
Un test en runtime pour vérifier que tout fonctionne bien, et on obtient une application dans laquelle on peut modifier directement la direction de la lumière, et celle de la caméra :
En espérant que ça pourra être utile à quelqu’un ;-)
Dominique Thery