IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

WPF - Problème de Binding


Sujet :

C#

  1. #1
    Nouveau Candidat au Club
    Inscrit en
    Mai 2005
    Messages
    2
    Détails du profil
    Informations forums :
    Inscription : Mai 2005
    Messages : 2
    Points : 1
    Points
    1
    Par défaut WPF - Problème de Binding
    Bonjour,
    Je débute en WPF et j'ai encore quelques difficultés à bien assimiler le mécanisme de Binding. J'ai créé un code xaml avec un canvas, dans lequel il y a une image positionnée à l'aide de ses propriétés Canvas.Left et Canvas.Bottom.
    Ces 2 propriétés sont bindés avec ma ViewModel (qui est définie comme étant le DataContext dans mon code behind), de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Canvas.Left="{Binding Coordonnees_X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    Canvas.Bottom="{Binding Coordonnees_Y, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    Dans ma ViewModel, je retrouve logiquement Coordonnees_X et Coordonnees_Y, de la manière suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
            private double coordonnees_X; 
            public double Coordonnees_X
            {
                get { return coordonnees_X; }
                set { coordonnees_X = value; OnPropertyChanged("Coordonnees_X"); }
            }
     
            private double coordonnees_Y;
            public double Coordonnees_Y
            {
                get { return coordonnees_Y; }
                set { coordonnees_Y = value; OnPropertyChanged("Coordonnees_Y"); }
            }
    Et, toujours dans le ViewModel, j'ai défini l'événement OnPropertyChanged :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
            public event PropertyChangedEventHandler PropertyChanged; // J'utilise un évènement de ce type pour propager l'info.
            private void OnPropertyChanged(string nomPropriete) // Je veux propager au graphique le fait que les coordonnées ont changé
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(nomPropriete));
            }
    Sur le fond, tout fonctionne.

    Je souhaite maintenant appliquer une Animation à mon image pour la faire évoluer, en lien avec l'appui sur les flèches du clavier. Si l'on prend l'exemple de l'appui sur la flèche droite, mon code behind se présente de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    private void Fenetre_KeyDown(object sender, KeyEventArgs e)
            {
                DoubleAnimation Anim;
     
                if(e.Key == Key.Right)
                {
                        Storyboard sb = new Storyboard();
                        Anim = new DoubleAnimation(PointDeDepart, PointDArrivee, TimeSpan.FromMilliseconds(Duree));
                        Storyboard.SetTargetName(Anim, "Nom_De_Mon_Image");
                        Storyboard.SetTargetProperty(Anim, new PropertyPath("(Canvas.Left)"));
                        sb.Children.Add(Anim);
                        sb.Begin(this);
                }
    PointDeDepart, PointDArrivee et Duree ont été correctement définis et l'animation fonctionne.

    Par contre, au fur et à mesure que mon image bouge sur la droite, la propriété Coordonnees_X n'est pas mise à jour, alors que mon animation porte bien sur propriété Canvas.Left de mon image, qui est elle-même bindée en mode "TwoWay" avec Coordonnees_X. Je me serais donc attendu à ce que Coordonnees_X soit mis à jour en même temps que mon image bouge via l'animation.
    Pourquoi est-ce que cela ne marche pas comme cela et comment faire pour que cela fonctionne ainsi ?

    Merci de votre aide.

  2. #2
    Membre éprouvé
    Homme Profil pro
    Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique
    Inscrit en
    Décembre 2014
    Messages
    454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique

    Informations forums :
    Inscription : Décembre 2014
    Messages : 454
    Points : 998
    Points
    998
    Par défaut
    Salut !

    Bon déjà pense à mettre [CallerMemberName] au niveau de l'argument dans ta méthode OnPropertyChanged, tu vas moins te casser la tête si tu modifies ton code.

    Sinon, est ce que tu as pensé à mettre des points d'arrêt dans visual studio afin de vérifier quand 'est ce que tes propriétés sont utilisées en get comme en set (ne mets pas sur la même ligne pour avoir une meilleure visibilité quand le point d'arrêt va stopper l'exécution). Tu ne montres pas le datacontext donc j'espère que tu as pensé à mettre "= this" juste après InitializeComponent, ou éventuellement dans l'évènement Loaded.

    En passant je n'ai pas trop fait attention exactement à ce que tu veux faire mais wpf permet des animations que tu peux écrire directement en xaml, et de préférence c'est mieux de le faire comme ça pour respecter le MVVM de ce que j'en sais.

    Cordialement.

  3. #3
    Expert confirmé
    Inscrit en
    Avril 2008
    Messages
    2 564
    Détails du profil
    Informations personnelles :
    Âge : 64

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 564
    Points : 4 442
    Points
    4 442
    Par défaut
    bonjour
    Comme dit par daerlnex ,je ne comprend pas, moi aussi ,ce que tu veux faire.
    Mais point important :les animations sont prévues pour animer des propriétés de controls ou DP.
    L'animation de ces props se fait par "affectation directe " que ce soit en XAML ou en code behind (ton code)
    Ce qu'il faut retenir en WPF c'est que cette "affectation directe" court-circuite ou bypasse le "BINDING".
    Le résultat en est que les valeurs de props modifiées par ce genre d'affectation ne peuvent être reflétées dans le MVVM (ton class data).

    Il faut donc réfléchir plus en profondeur à ce que tu veux obtenir comme résultat.

  4. #4
    Nouveau Candidat au Club
    Inscrit en
    Mai 2005
    Messages
    2
    Détails du profil
    Informations forums :
    Inscription : Mai 2005
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Bonjour,

    Merci à tous les deux. Effectivement, ce que je cherche à faire n'est peut-être pas très clair. Mon objectif, c'est de déplacer une image à l'intérieur d'un cavas grâce aux flèches du clavier. L'utilisation d'une animation doit me permettre d'obtenir un déplacement le plus fluide possible.
    Par ailleurs, je souhaite pouvoir contrôler l'animation et, éventuellement, l'interrompre alors qu'elle n'a pas été jusqu'au bout par exemple. J'ai donc besoin de garder la main sur l'animation, raison pour laquelle je l'ai mise dans mon code behind.
    Et tout cela en essayant de respecter le modèle MVVM ou, tout du moins, ce que j'en ai compris.

    J'ai intégré l'image via le xaml, j'ai bindé les propriétés Canvas.Left et Canvas.Bottom de l'image avec une classe qui contient des propriété coordonnées_X et coordonnées_Y. C'est cette classe, qui est donc indépendante du code behind, qui me sert de DataContext.

    En relisant ce que tu dis Marbouki, j'ai l'impression que ce que je souhaite faire ne peut pas simplement pas fonctionner de la façon dont je veux l'implémenter. Donc je vais essayer de faire autrement. Mais je crains que mon autre façon de faire ne soit pas très orthodoxe d'un point de vue MVVM.

    Je dois avouer que j'ai un peu de mal avec la logique WPF, qui ne m'est pas très naturelle.

    Merci de votre aide.

    Ledur.

  5. #5
    Expert confirmé
    Inscrit en
    Avril 2008
    Messages
    2 564
    Détails du profil
    Informations personnelles :
    Âge : 64

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 564
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par ledur Voir le message
    Bonjour,

    En relisant ce que tu dis Marbouki, j'ai l'impression que ce que je souhaite faire ne peut pas simplement pas fonctionner de la façon dont je veux l'implémenter. Donc je vais essayer de faire autrement. Mais je crains que mon autre façon de faire ne soit pas très orthodoxe d'un point de vue MVVM.

    Je dois avouer que j'ai un peu de mal avec la logique WPF, qui ne m'est pas très naturelle.

    Ledur.
    Rebonjour .
    Tout peut etre en fait programmation , à condition d'employer les outils adequats.
    Pour revenir au sujet ,rien ne n'empêche d'avoir un VM type Dependency Object qui permet d'accéder à l'infrastructure Wpf dont les class Storyboard et les class Animation.
    Nota Bene : heriter de Dependency Object t'exonère d'implémenter INotifyPropertyChanged
    Alors là tes props(x,y) doivent etre declarées comme des DP ( tu peux declarer même des Attached DP et des events comme des RoutedEvents).

    Ce class VM peut acceder aux class Storyboard et aux class Animation.
    Ayant franci ce fleuve Rubicon,l' exemple de code permet de faire ce que tu souhaites en utilisant :
    - un TranslateForm ,
    -2 StoryBoards en ressources ( right-left ,up-down),
    - le pattern RelayCommand pour gérer les 4 touches claviers Home (& concomitamment & en bonus 4 boutons dans l'exemple mais ca reste un problème de choix).
    - le By du class Animation ajusté à un step de 50.0
    Tout cela permet de faire défiler un Image posé sur un Canvas et bindé aux props TranslateX & TranslateY (tes coords).
    En bonus également 2 TextBlocs espions permettent de suivre le "binding" en "direct".

    1/ code xaml de App.xaml:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
     
    <Application x:Class="WpfAnimationVM.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                StartupUri="MainWindow.xaml">
                 <!--StartupUri="Window1.xaml">-->
     
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Dictionary1.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    2/ code xaml de Dictonary1. xaml (definition des storyboards):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Storyboard x:Key="XTranslateStoryboard"
            Storyboard.TargetName="{Binding}"
            TargetProperty="TranslateX"
            Duration="0:0:5">
            <DoubleAnimation   By="50"/>
        </Storyboard>
    <Storyboard x:Key="YTranslateStoryboard"
            Storyboard.TargetName="{Binding}"
            TargetProperty="TranslateY"
            Duration="0:0:5">
            <DoubleAnimation   By="50"/>
        </Storyboard>
    </ResourceDictionary>
    3/ code behind .cs du class MainVM (data):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Media.Animation;
     
    namespace WpfAnimationVM
    {
        public class MainVM : DependencyObject
        {
            private Storyboard XTranslate,YTranslate;
            private DoubleAnimation XAnimation, YAnimation;
            public MainVM()
            {
                XTranslate = Application.Current.Resources["XTranslateStoryboard"] as Storyboard;
                XAnimation = XTranslate.Children[0] as DoubleAnimation;
                Storyboard.SetTarget(XAnimation, this);
                RightTranslateCommand = new RelayCommand(RightTranslateExecute);
                LeftTranslateCommand = new RelayCommand(LeftTranslateExecute);
     
                YTranslate = Application.Current.Resources["YTranslateStoryboard"] as Storyboard;
                YAnimation = YTranslate.Children[0] as DoubleAnimation;
                Storyboard.SetTarget(YAnimation, this);
                UpTranslateCommand = new RelayCommand(UpTranslateExecute);
                DownTranslateCommand = new RelayCommand(DownTranslateExecute);
            }
     
     
            // les DP  TranslateX & TranslateY  sont  les coords X,Y  de l'objet
            // ainsi denommées car "bindées" aux DP du TranslateTransform
            public double TranslateX
            {
                get { return (double)GetValue(TranslateXProperty); }
                set { SetValue(TranslateXProperty, value); }
            }
            public static readonly DependencyProperty TranslateXProperty =
                DependencyProperty.Register("TranslateX", 
                typeof(double), 
                typeof(MainVM), 
                new PropertyMetadata(0D)
               );
            public double TranslateY
            {
                get { return (double)GetValue(TranslateYProperty); }
                set { SetValue(TranslateYProperty, value); }
            }
            public static readonly DependencyProperty TranslateYProperty =
                DependencyProperty.Register("TranslateY",
                typeof(double), 
                typeof(MainVM), 
                new PropertyMetadata(0D)
               );
            // partie  commandes
            public RelayCommand RightTranslateCommand { get; set; }
            public RelayCommand LeftTranslateCommand { get; set; }
            public RelayCommand UpTranslateCommand { get; set; }
            public RelayCommand DownTranslateCommand { get; set; }
     
            private void LeftTranslateExecute(object o)
            {
                XAnimation.By = -50;
                XTranslate.Begin();
     
            }
            private void RightTranslateExecute(object o)
            {
                XAnimation.By = 50;
                XTranslate.Begin();
     
            }
            private void UpTranslateExecute(object o)
            {
                YAnimation.By = -50;
                YTranslate.Begin();
     
            }
            private void DownTranslateExecute(object o)
            {
                YAnimation.By = 50;
                YTranslate.Begin();
     
            }
        }
     
    }
    4/ code behind.cs du class RelayCommand (pattern classique):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    using System;
    using System.Windows.Input;
     
    namespace WpfAnimationVM
    {
        public class RelayCommand : ICommand
        {
            readonly Action<object> _execute;
            readonly Predicate<object> _canExecute;
     
            public RelayCommand(Action<object> execute)
                : this(execute, null)
            {
            }
     
            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                    throw new ArgumentNullException("execute");
     
                _execute = execute;
                _canExecute = canExecute;
            }
     
     
     
     
            public bool CanExecute(object parameter)
            {
                return _canExecute == null ? true : _canExecute(parameter);
            }
     
            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }
     
            public void Execute(object parameter)
            {
                _execute(parameter);
            }
     
        }
     
    }
    5/ code xaml du form User (pas de code behind ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
     
    <Window x:Class="WpfAnimationVM.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfAnimationVM"
            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <local:MainVM/>
        </Window.DataContext>
     
        <!--ici le  binding aux fleches-->
        <Window.InputBindings>
            <KeyBinding Key="Left"  Command="{Binding LeftTranslateCommand}" />
            <KeyBinding Key="Right"  Command="{Binding RightTranslateCommand}" />
            <KeyBinding Key="Up"  Command="{Binding UpTranslateCommand}" />
            <KeyBinding Key="Down"  Command="{Binding DownTranslateCommand}" />
        </Window.InputBindings>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <!--en bonus le  binding aux boutons &  2 TextBlocs espions permmettent de suivre -->
            <!--en bonus egalement  2 TextBlocs espions permettent de suivre le "binding"-->
            <StackPanel Grid.Column="0" Width="100">
                <Button Command="{Binding LeftTranslateCommand}">Left</Button>
                <Button Command="{Binding RightTranslateCommand}">Right</Button>
                <Button Command="{Binding UpTranslateCommand}">Up</Button>
                <Button Command="{Binding DownTranslateCommand}">Down</Button>
                <TextBlock Text="{Binding  TranslateX,Mode=TwoWay}"/>
                <TextBlock Text="{Binding  TranslateY,Mode=TwoWay}"/>
            </StackPanel>
     
            <Canvas 
                Grid.Column="1"
                Background="Gray"  ClipToBounds="True"
                Width="500" Height="500">
                <Image 
                    x:Name="img"
                    Width="100" Source="Images/Mebarek.png"
                    Canvas.Left="{Binding TranslateX}" Canvas.Top="{Binding TranslateY}">
                    <Image.RenderTransform>
                        <TranslateTransform 
                            x:Name="myTranslation" 
                             Changed="myTranslation_Changed"
                            X="{Binding TranslateX,Mode=TwoWay,NotifyOnSourceUpdated=True}" 
                            Y="{Binding TranslateY,Mode=TwoWay,NotifyOnSourceUpdated=True}">
                        </TranslateTransform>
                    </Image.RenderTransform>
                </Image>
            </Canvas>
        </Grid>
    </Window>
    bon code....

Discussions similaires

  1. Réponses: 0
    Dernier message: 30/06/2009, 21h41
  2. [WPF] Probléme de binding dans un UserControl :(
    Par UNi[FR] dans le forum Windows Presentation Foundation
    Réponses: 6
    Dernier message: 17/07/2008, 15h51
  3. [WPF] Problème de binding
    Par tomlev dans le forum Windows Presentation Foundation
    Réponses: 3
    Dernier message: 04/02/2008, 14h08
  4. [WPF] Problème de binding entre une SortedList et une ListBox
    Par JuTs dans le forum Général Dotnet
    Réponses: 4
    Dernier message: 10/12/2007, 13h20
  5. [WPF] Problème de binding
    Par JuTs dans le forum Framework .NET
    Réponses: 42
    Dernier message: 06/11/2007, 11h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo