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] Binding d'objets


Sujet :

C#

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 14
    Points : 11
    Points
    11
    Par défaut [WPF] Binding d'objets
    Bonjour à tous et à toutes,
    je suis en train de développer un numeric up/down pour mon programme, mais je suis embêter par un problème en apparence tout bête.
    Il s'agit de binder une propriété de type int venant du view-model dans une des propriétés du Usercontrol.

    Cette propriété est-elle même bindée dans une texte box.

    Le fichier XAML de mon numeric up/down est défini comme ceci :

    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
     
    <UserControl x:Class="QuanticStorm.WPF.Controls.IntNumericUpDown"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="22" 
                 d:DesignWidth="120">
        <Grid Background="Transparent">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="22" />
                <ColumnDefinition Width="22" />
            </Grid.ColumnDefinitions>
     
            <TextBox Grid.Column="0"
                     HorizontalContentAlignment="Right"
                     Text="{Binding Path=Value}" />        
            <RepeatButton Grid.Column="1"
                          Content="+"
                          Click="RepeatButton_Click"
                          Name="increaseValueButton" />
     
            <RepeatButton Grid.Column="2"
                          Content="-"
                          Click="RepeatButton_Click"
                          Name="decreaseValueButton" />
        </Grid>
    </UserControl>
    Suivit de son fichier cs
    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
     
    sealed public class IntNumericUpDown : UserControl
    {
            static public DependencyProperty ValueProperty;
            static public DependencyProperty ChangeProperty;
     
            public int Value
            {
                get { return (int)GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
     
            public int Change
            {
                get { return (int)GetValue(ChangeProperty); }
                set { SetValue(ChangeProperty, value); }
            }
     
            public IntNumericUpDown()
            {
                InitializeComponent();
                DataContext = this;
            }
     
            static IntNumericUpDown()
            {
               ValueProperty = DependencyProperty.Register("Value",
                                                            typeof(int),
                                                            typeof(IntNumericUpDown),
                                                            new PropertyMetadata(5));
     
                ChangeProperty = DependencyProperty.Register("Change",
                                                             typeof(int),
                                                             typeof(IntNumericUpDown),
                                                             new PropertyMetadata(1));
            }
     
            private void RepeatButton_Click(object sender, RoutedEventArgs e)
            {
                RepeatButton button = (RepeatButton)e.Source;
     
                if (button == increaseValueButton)
                    Value += Change;
                else if (button == decreaseValueButton)
                    Value -= Change;
            }
    }
    Donc ici, c'est tout simple, je créé un objet int pour la valeur et le pas de changement. Comme cela, un clic sur + incrément de x la valeur et un clic sur - décrémente de x la valeur.

    (J'ai retiré volontairement les valeurs min et max autorisées et la frappe de texte qui détecte si l'on a à faire à un nombre ou non).

    J'ai crée une vue qui contient entre autres 3 numeriques up/down qui vont définir :
    - L'espacement entre chaque image
    - La longueur maximale de la feuille de sprites
    - La hauteur maximale de la feuille de sprites.

    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
     
    <ScrollViewer x:Class="GameMakerStudioX.SpriteSheetPacker.Views.SpriteSheetPackerView"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:viewmodel="clr-namespace:GameMakerStudioX.SpriteSheetPacker.ViewModels"
                  xmlns:controls="clr-namespace:QuanticStorm.WPF.Controls;assembly=QuanticStorm.WPF"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                  mc:Ignorable="d" 
                  d:DesignHeight="600"
                  d:DesignWidth="800"
                  DataContext="{x:Static Member=viewmodel:SpriteSheetPackerViewModel.Singleton}">
            <StackPanel>
                    // ... LISTE DES IMAGES A PACKER ...... //
     
                    <Expander Header="Propriétés de l'image">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
     
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="120" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
     
                    <TextBlock Grid.Row="0"
                               Grid.Column="0"
                               Text="Espacement entre chaque image" 
                               Margin="2" />
     
                    <controls:IntNumericUpDown Grid.Row="0"
                                               Grid.Column="1"
                                               MinValue="1"
                                               MaxValue="10"
                                               Value="{Binding Path=PaddingImage,
                                                               Mode=TwoWay}"
                                               Margin="2"
                                               Name="paddingUpDown" />
     
                    <TextBlock Grid.Row="1"
                               Grid.Column="0"
                                  Text="Largeur maximale de la feuille de sprites"
                               Margin="2" />
     
                    <controls:IntNumericUpDown Grid.Row="1"
                                               Grid.Column="1"
                                               MinValue="128"
                                               MaxValue="8192"
                                               Value="{Binding Path=MaximumSpriteSheetWidth,
                                                               Mode=TwoWay}"
                                               Margin="2"
                                               Name="maxSpriteSheetWidth" />
     
                    <TextBlock Grid.Row="2"
                               Grid.Column="0"
                               Text="Hauteur maximale de la feuille de sprites"
                               Margin="2" />
     
                    <controls:IntNumericUpDown Grid.Row="2"
                                               Grid.Column="1"
                                               MinValue="128"
                                               MaxValue="8192"
                                               Value="{Binding Path=MaximumSpriteSheetHeight,
                                                               Mode=TwoWay}"
                                               Margin="2"
                                               Name="maxSpriteSheetHeight" />
                </Grid>
            </Expander>
     
    // ... RESTE DE L'INTERFACE ...
     
        </StackPanel>
    </ScrollViewer>
    Comme on le voit ici la propriété Value du numeric up/down est bindé à une propriété du view-model.

    La view-model hérite de dependency object.

    On a donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    sealed public class SpriteSheetPackerViewModel : DependencyObject
    {
    }
    Là dedans j'ai créé 3 propriétés de dépendance pour les 3 données spécifiées :

    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
     
    /// <summary>
    /// Represents the padding property of the image.
    /// </summary>
    static public readonly DependencyProperty PaddingImageProperty;
     
    /// <summary>
    /// Represents the maximum width property of the sprite sheet.
    /// </summary>
    static public readonly DependencyProperty SpriteSheetMaxWidthProperty;
     
    /// <summary>
    /// Represents the maximum height property of the sprite sheet.
    /// </summary>
    static public readonly DependencyProperty SpriteSheetMaxHeightProperty;
     
    /// <summary>
    /// Gets or sets the padding between each image.
    /// </summary>
    public int PaddingImage
    {
        get { return (int)GetValue(PaddingImageProperty); }
        set { SetValue(PaddingImageProperty, value); }
    }
     
    /// <summary>
    /// Gets or sets the maximum width of the sprite sheet.
    /// </summary>
    public int MaximumSpriteSheetWidth
    {
        get { return (int)GetValue(SpriteSheetMaxWidthProperty); }
        set { SetValue(SpriteSheetMaxWidthProperty, value); }
    }
     
    /// <summary>
    /// Gets or sets the maximum height of the sprite sheet.
    /// </summary>
    public int MaximumSpriteSheetHeight
    {
        get { return (int)GetValue(SpriteSheetMaxHeightProperty); }
        set { SetValue(SpriteSheetMaxHeightProperty, value); }
    }
     
    /// <summary>
    /// Register the dependency properties and instantiates the singleton.
    /// </summary>
    static SpriteSheetPackerViewModel()
    {
          PaddingImageProperty = DependencyProperty.Register("PaddingImage",
                                                                    typeof(int),
                                                                    typeof(SpriteSheetPackerViewModel),
                                                                    new UIPropertyMetadata(1,
                                                                        new PropertyChangedCallback(OnDependencyPropertyChanged)));
     
     
     
        SpriteSheetMaxHeightProperty = DependencyProperty.Register("MaximumSpriteSheetHeight",
                                                                           typeof(int),
                                                                           typeof(SpriteSheetPackerViewModel),
                                                                           new UIPropertyMetadata(4096,
                                                                        new PropertyChangedCallback(OnDependencyPropertyChanged)));
     
     
     
                SpriteSheetMaxWidthProperty = DependencyProperty.Register("MaximumSpriteSheetWidth",
                                                                           typeof(int),
                                                                           typeof(SpriteSheetPackerViewModel),
                                                                           new UIPropertyMetadata(4096,
                                                                        new PropertyChangedCallback(OnDependencyPropertyChanged)));
     
     // .... etc ....
    }
    Maintenant c'est là que la coquille intervient.
    Quelle que soit la donnée présente dans les propriétés PaddingImage et MaximumSpriteSheetXXXX, elles ne se répercutent pas dans Value et par conséquent ne s'affiche pas dans la textbox...

    La valeur affichée dans celle-ci sera toujours la valeur mise dans la propriété de dépendance Value du numeric up/down

    Je ne vois pas pourquoi cela ne fonctionne pas...

    Si je pouvais avoir quelques éclaircissements...

    PS : Si mon problème n'est pas clair, veuillez-le dire

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    C'est le fait d'affecter le DataContext à this dans le constructeur qui fout la zone...

    Puisque le DataContext est le UserControl lui-même, le binding sur PaddingImage cherche une propriété PaddingImage dans le UserControl, or celle-ci n'existe pas. Donc il ne faut pas toucher au DataContext du UserControl, sinon tu ne peux plus binder ses propriétés dans les vues où tu l'utilises...

    A la place, utilise un binding comme ça pour la TextBox :

    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
            <TextBox Grid.Column="0"
                     HorizontalContentAlignment="Right"
                     Text="{Binding Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:IntNumericUpDown}}" />

    Ou encore, en nommant "this" l'élément racine (<UserControl>) :

    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
            <TextBox Grid.Column="0"
                     HorizontalContentAlignment="Right"
                     Text="{Binding Value, ElementName=this}" />

    Sinon, deux choses :

    1) évite d'hériter de DependencyObject pour tes ViewModels :
    - c'est relativement lourd
    - c'est galère dès que tu as plusieurs threads car seul le thread qui a créé un DependencyObject peut accéder à ses propriétés.
    - c'est nettement plus lourd d'écrire une DependencyProperty qu'une propriété normale (même si le snippet "propdp" facilite un peu les choses)

    Il vaut mieux implémenter l'interface INotifyPropertyChanged, éventuellement en créant une classe de base ViewModelBase dont tous les ViewModels vont hériter. Il y a eu quelques débats à ce sujet quand MVVM a commencé à prendre de l'ampleur, mais ajourd'hui quasiment tout le monde est d'accord que INotifyPropertyChanged est plus adapté que DependencyObject.

    2) A priori ta propriété Value sera toujours utilisée avec un binding TwoWay, donc ce serait plus pratique que ce soit TwoWay par défaut (comme dans un TextBox par exemple). Tu peux facilement indiquer ça au niveau des metadata de la propriété :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
               ValueProperty = DependencyProperty.Register("Value",
                                                            typeof(int),
                                                            typeof(NumericUpDown),
                                                            new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 14
    Points : 11
    Points
    11
    Par défaut
    Merci tomlev, ça fonctionne impec...

    Merci de l'éclaircissement à propos du dependency object sur un viewmodel.
    J'avais effectivement hésité entre lui et le INotifyPropertyChanged.

    Je prends aussi la remarque sur le TwoWays (j'étais au courant, mais je me préoccupais plus du problème du binding)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. binding entre un objet wpf et un objet COM
    Par TERRIBLE dans le forum Windows Presentation Foundation
    Réponses: 4
    Dernier message: 07/05/2010, 15h58
  2. [WPF-Blend] Plusieurs objets mais une seule animation
    Par Tuizi dans le forum Framework .NET
    Réponses: 12
    Dernier message: 11/12/2007, 17h10
  3. [WPF][Binding] Comment binder un fichier XML sur un treeview?
    Par bakonu dans le forum Général Dotnet
    Réponses: 5
    Dernier message: 26/11/2007, 17h09
  4. [WPF] Binding sur app.config
    Par despeludo dans le forum Windows Presentation Foundation
    Réponses: 2
    Dernier message: 24/10/2007, 22h56
  5. Late binding et Objets COM
    Par M4tthieu dans le forum C#
    Réponses: 4
    Dernier message: 07/08/2007, 12h28

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