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

Contribuez .NET Discussion :

Propriété attachée - FlowDocument WPF [Dvp.NET|A intégrer]


Sujet :

Contribuez .NET

  1. #1
    Membre éprouvé Avatar de yonpo
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2010
    Messages
    617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2010
    Messages : 617
    Points : 947
    Points
    947
    Par défaut Propriété attachée - FlowDocument WPF
    Salut,

    Je propose une nouvelle classe à ajouter (je l'espère ) au projet dvp.net :
    Elle permet, via une propriété attachée, de binder la propriété Blocks d'un FlowDocument à une collection (List<Block> par exemple).

    Code c# : 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
    /// <summary>
        /// Permet d'effectuer une liaison de données sur la propriété Blocks d'un FlowDocument
        /// </summary>
        public static class FlowDocumentBehavior
        {
            /// <summary>
            /// Obtient la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite obtenir la valeur de la propriété</param>
            /// <returns></returns>
            public static IEnumerable<Block> GetBindableBlocks(DependencyObject obj)
            {
                return (IEnumerable<Block>)obj.GetValue(BindableBlocksProperty);
            }
     
            /// <summary>
            /// Définit la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite définir la valeur de la propriété</param>
            /// <param name="value">Valeur de la propriété de dépendance spécifiée</param>
            public static void SetBindableBlocks(DependencyObject obj, IEnumerable<Block> value)
            {
                obj.SetValue(BindableBlocksProperty, value);
            }
     
            /// <summary>
            /// Propriété attachée BindableBlocks
            /// </summary>
            public static readonly DependencyProperty BindableBlocksProperty =
                DependencyProperty.RegisterAttached("BindableBlocks",
                typeof(IEnumerable<Block>), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null, BindableBlocksChanged));
     
            private static void BindableBlocksChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            {
                if (dependencyObject is FlowDocument)
                {
                    var obj = dependencyObject as FlowDocument;
                    obj.Blocks.Clear();
                    if (e.NewValue != null)
                    {
                        foreach (var block in (IEnumerable<Block>)e.NewValue)
                        {
                            obj.Blocks.Add(block);
                        }
                    }
                }
            }
        }

  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 : 42
    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 749
    Points
    39 749
    Par défaut
    Bonne idée

    Juste une petite remarque : ce serait bien de gérer le cas où la collection bindée implémente INotifyCollectionChanged, de façon à ce que les éléments ajoutés ensuite à la collection soient ajoutés au FlowDocument

  3. #3
    Membre éprouvé Avatar de yonpo
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2010
    Messages
    617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2010
    Messages : 617
    Points : 947
    Points
    947
    Par défaut
    Voici la classe modifiée :

    Code c# : 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
    /// <summary>
        /// Permet d'effectuer une liaison de données sur la propriété Blocks d'un FlowDocument
        /// </summary>
        public static class FlowDocumentBehavior
        {
            private static FlowDocument _flowDocument;
     
            /// <summary>
            /// Obtient la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite obtenir la valeur de la propriété</param>
            /// <returns></returns>
            public static IEnumerable<Block> GetBindableBlocks(DependencyObject obj)
            {
                return (IEnumerable<Block>)obj.GetValue(BindableBlocksProperty);
            }
     
            /// <summary>
            /// Définit la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite définir la valeur de la propriété</param>
            /// <param name="value">Valeur de la propriété de dépendance spécifiée</param>
            public static void SetBindableBlocks(DependencyObject obj, IEnumerable<Block> value)
            {
                obj.SetValue(BindableBlocksProperty, value);
            }
     
            /// <summary>
            /// Propriété attachée BindableBlocks
            /// </summary>
            public static readonly DependencyProperty BindableBlocksProperty =
                DependencyProperty.RegisterAttached("BindableBlocks",
                typeof(IEnumerable<Block>), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null, BindableBlocksChanged));
     
            private static void BindableBlocksChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            {
                _flowDocument = dependencyObject as FlowDocument;
     
                if (_flowDocument == null) return;
     
                _flowDocument.Blocks.Clear();
                if (e.NewValue != null)
                {
                    var newValue = (IEnumerable<Block>)e.NewValue;
                    foreach (var block in newValue)
                    {
                        _flowDocument.Blocks.Add(block);
                    }
     
                    var observableList = newValue as INotifyCollectionChanged;
                    if (observableList != null) observableList.CollectionChanged += BlocksCollectionChanged;
                }
            }
     
            private static void BlocksCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (Block item in e.OldItems)
                    {
                        _flowDocument.Blocks.Remove(item);
                    }
                }
     
                if (e.NewItems != null)
                {
                    foreach (Block item in e.NewItems)
                    {
                        _flowDocument.Blocks.Add(item);
                    }
                }
            }
        }

  4. #4
    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 : 42
    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 749
    Points
    39 749
    Par défaut
    Bah non, tu peux pas faire comme ça... si tu stockes le FlowDocument dans un champ statique de la classe, ça fait que tu ne peux jamais utiliser plus d'un FlowDocument dans ton programme. Il faut trouver une autre solution...

    Une possibilité est de créer une classe spécifique pour gérer la "synchronisation" entre la collection et le FlowDocument, et de stocker un objet de cette classe dans une autre propriété attachée (privée et en lecture seule), pour l'associer au FlowDocument.

  5. #5
    Membre éprouvé Avatar de yonpo
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2010
    Messages
    617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2010
    Messages : 617
    Points : 947
    Points
    947
    Par défaut
    Ah oui... Bien vue !

    Je regarderai ça.

  6. #6
    Membre éprouvé Avatar de yonpo
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2010
    Messages
    617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2010
    Messages : 617
    Points : 947
    Points
    947
    Par défaut
    J'ai corrigé le code en m'aspirant de tes remarques :

    Code c# : 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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    /// <summary>
        /// Permet d'effectuer une liaison de données sur la propriété Blocks d'un FlowDocument
        /// </summary>
        public static class FlowDocumentBehavior
        {
            /// <summary>
            /// Obtient la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite obtenir la valeur de la propriété</param>
            /// <returns></returns>
            public static IEnumerable<Block> GetBindableBlocks(DependencyObject obj)
            {
                return (IEnumerable<Block>)obj.GetValue(BindableBlocksProperty);
            }
     
            /// <summary>
            /// Définit la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite définir la valeur de la propriété</param>
            /// <param name="value">Valeur de la propriété de dépendance spécifiée</param>
            public static void SetBindableBlocks(DependencyObject obj, IEnumerable<Block> value)
            {
                obj.SetValue(BindableBlocksProperty, value);
            }
     
            /// <summary>
            /// Propriété attachée BindableBlocks
            /// </summary>
            public static readonly DependencyProperty BindableBlocksProperty =
                DependencyProperty.RegisterAttached("BindableBlocks",
                typeof(IEnumerable<Block>), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null, BindableBlocksChanged));
     
            private static void BindableBlocksChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            {
                var flowDocument = dependencyObject as FlowDocument;
     
                if (flowDocument == null) return;
     
                if (e.NewValue != null)
                {
                    var sync = new FlowDocumentSync(flowDocument, (IEnumerable<Block>)e.NewValue);
                    SetFlowDocumentSync(flowDocument, sync);
                }
            }
     
            private static FlowDocumentSync GetFlowDocumentSync(DependencyObject obj)
            {
                return (FlowDocumentSync)obj.GetValue(_flowDocumentSyncPropertyKey.DependencyProperty);
            }
     
            private static void SetFlowDocumentSync(DependencyObject obj, FlowDocumentSync value)
            {
                obj.SetValue(_flowDocumentSyncPropertyKey, value);
            }
     
            private static readonly DependencyPropertyKey _flowDocumentSyncPropertyKey =
                DependencyProperty.RegisterAttachedReadOnly("flowDocumentSync",
                typeof(FlowDocumentSync), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null));
        }
     
        internal class FlowDocumentSync
        {
            private FlowDocument _flowDocument;
            private IEnumerable<Block> _blocks;
     
            public FlowDocumentSync(FlowDocument flowDocument, IEnumerable<Block> blocks)
            {
                _flowDocument = flowDocument;
                _blocks = blocks;
     
                _flowDocument.Blocks.Clear();
     
                foreach (var block in _blocks)
                {
                    _flowDocument.Blocks.Add(block);
                }
     
                var observableList = _blocks as INotifyCollectionChanged;
                if (observableList != null) observableList.CollectionChanged += BlocksCollectionChanged;
            }
     
            private void BlocksCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (Block item in e.OldItems)
                    {
                        _flowDocument.Blocks.Remove(item);
                    }
                }
     
                if (e.NewItems != null)
                {
                    foreach (Block item in e.NewItems)
                    {
                        _flowDocument.Blocks.Add(item);
                    }
                }
            }
        }

    J'ai testé avec deux collections bindés sur deux FlowDocument et ça fonctionne (ajout, suppression d'éléments).

  7. #7
    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 : 42
    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 749
    Points
    39 749
    Par défaut
    C'est à peu près ce que j'avais en tête

    Par contre, encore un petit truc qui ne va pas : tu ne te désabonnes pas de l'évènement CollectionChanged quand la propriété BindableBlocks change, ce qui peut causer des fuites mémoires (voire des bugs si l'ancienne collection est modifiée alors même qu'elle n'est plus bindée)

    Il faudrait faire quelque chose comme ça :

    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
        /// <summary>
        /// Permet d'effectuer une liaison de données sur la propriété Blocks d'un FlowDocument
        /// </summary>
        public static class FlowDocumentBehavior
        {
            /// <summary>
            /// Obtient la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite obtenir la valeur de la propriété</param>
            /// <returns></returns>
            public static IEnumerable<Block> GetBindableBlocks(DependencyObject obj)
            {
                return (IEnumerable<Block>)obj.GetValue(BindableBlocksProperty);
            }
     
            /// <summary>
            /// Définit la valeur de la propriété attachée BindableBlocks pour l'objet spécifié
            /// </summary>
            /// <param name="obj">Objet dont on souhaite définir la valeur de la propriété</param>
            /// <param name="value">Valeur de la propriété de dépendance spécifiée</param>
            public static void SetBindableBlocks(DependencyObject obj, IEnumerable<Block> value)
            {
                obj.SetValue(BindableBlocksProperty, value);
            }
     
            /// <summary>
            /// Propriété attachée BindableBlocks
            /// </summary>
            public static readonly DependencyProperty BindableBlocksProperty =
                DependencyProperty.RegisterAttached("BindableBlocks",
                typeof(IEnumerable<Block>), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null, BindableBlocksChanged));
     
            private static void BindableBlocksChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            {
                var flowDocument = dependencyObject as FlowDocument;
     
                if (flowDocument == null) return;
     
                // Désabonnement de l'ancienne collection
                if (e.OldValue != null)
                {
                    var sync = GetFlowDocumentSync(flowDocument);
                    if (sync != null)
                    {
                        sync.Dispose();
                        SetFlowDocumentSync(flowDocument, null);
                    }
                }
     
                if (e.NewValue != null)
                {
                    var sync = new FlowDocumentSync(flowDocument, (IEnumerable<Block>)e.NewValue);
                    SetFlowDocumentSync(flowDocument, sync);
                }
            }
     
            private static FlowDocumentSync GetFlowDocumentSync(DependencyObject obj)
            {
                return (FlowDocumentSync)obj.GetValue(_flowDocumentSyncPropertyKey.DependencyProperty);
            }
     
            private static void SetFlowDocumentSync(DependencyObject obj, FlowDocumentSync value)
            {
                obj.SetValue(_flowDocumentSyncPropertyKey, value);
            }
     
            private static readonly DependencyPropertyKey _flowDocumentSyncPropertyKey =
                DependencyProperty.RegisterAttachedReadOnly("flowDocumentSync",
                typeof(FlowDocumentSync), typeof(FlowDocumentBehavior), new UIPropertyMetadata(null));
        }
     
        internal class FlowDocumentSync : IDisposable
        {
            private FlowDocument _flowDocument;
            private IEnumerable<Block> _blocks;
     
            public FlowDocumentSync(FlowDocument flowDocument, IEnumerable<Block> blocks)
            {
                _flowDocument = flowDocument;
                _blocks = blocks;
     
                _flowDocument.Blocks.Clear();
     
                foreach (var block in _blocks)
                {
                    _flowDocument.Blocks.Add(block);
                }
     
                var observableList = _blocks as INotifyCollectionChanged;
                if (observableList != null) observableList.CollectionChanged += BlocksCollectionChanged;
            }
     
            private void BlocksCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (Block item in e.OldItems)
                    {
                        _flowDocument.Blocks.Remove(item);
                    }
                }
     
                if (e.NewItems != null)
                {
                    foreach (Block item in e.NewItems)
                    {
                        _flowDocument.Blocks.Add(item);
                    }
                }
            }
     
            public void Dispose()
            {
                var observableList = _blocks as INotifyCollectionChanged;
                if (observableList != null) observableList.CollectionChanged -= BlocksCollectionChanged;
            }
        }
    Sinon j'aurais aussi déclaré la classe FlowDocumentSync dans la classe FlowDocumentBehavior, mais ça change pas grand chose

    Je fais quelques tests et j'intègre ça à Dvp.NET

  8. #8
    Membre éprouvé Avatar de yonpo
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2010
    Messages
    617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2010
    Messages : 617
    Points : 947
    Points
    947
    Par défaut
    Ok, super !
    Merci pour tes propositions qui ont permis d'améliorer la classe et qui m'ont aussi appris quelques trucs.

    Je mettrai quand ça sera intégré au projet.

  9. #9
    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 : 42
    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 749
    Points
    39 749
    Par défaut
    C'est intégré, ce sera dans la prochaine release ( pas de date prévue pour l'instant)

    Merci pour ta contribution

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

Discussions similaires

  1. ResourceLoader propriétés attachées
    Par Fabiani dans le forum WinRT
    Réponses: 2
    Dernier message: 10/07/2014, 16h45
  2. Syntaxe binding d'une propriété attachée
    Par Deesmon dans le forum Windows Presentation Foundation
    Réponses: 1
    Dernier message: 09/01/2014, 01h16
  3. Créer dynamiquement une propriété attachable
    Par NejNej dans le forum Windows Presentation Foundation
    Réponses: 7
    Dernier message: 16/11/2011, 16h41
  4. Fenêtre de propriété des controls WPF
    Par amandinerenard dans le forum Windows Presentation Foundation
    Réponses: 4
    Dernier message: 25/03/2008, 14h52
  5. Réponses: 12
    Dernier message: 12/12/2004, 14h25

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