Menu

WPF Rafraîchissement d'une fenêtre Binder

Messages postés
42
Date d'inscription
jeudi 7 février 2019
Statut
Membre
Dernière intervention
22 mars 2019
- - Dernière réponse : Whismeril
Messages postés
13451
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
26 juin 2019
- 25 mars 2019 à 22:21
Bonjour à tous et bien sur à Whismeril qui connaît bien mon projet et dont le code suivant est inspiré de son exemple de carnet de contact

J'affiche dans une fenêtre des Cr (Composants réseau) décrit dans un fichier XML et pour lesquels j'ai créé une classe Cr.
J'ai une classe Carnet dans la quel je récupère tous les Cr du fichier XML
Je crée une ObservableCollection Crs, pluriel de Cr aucun rapport avec ...
Crs = new ObservableCollection<Cr>(xml.LireFichierCRS());

dans le XAML j'utise un Canvas pour pouvoir positionner mes Contrôles et un ItemsControl pour les énumérer, le Binding est fait sur ItemsControl ItemsSource="{Binding Crs}"
Ma fenêtre s'affiche correctement
Mon problème est décrit en fin de sujet


<Canvas x:Name="MainCanvas" DataContext="{Binding .}"  Width="6000" Height="4000" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5"    MouseMove="MainCanvas_MouseMove"      >
                <!--MouseLeftButtonDown="MainCanvas_MouseLeftButtonDown" MouseLeftButtonUp="MainCanvas_MouseLeftButtonUp" MouseMove="MainCanvas_MouseMove" MouseWheel="MainCanvas_MouseWheel"-->
        <Canvas.Background>
                <SolidColorBrush Color="LightYellow" ></SolidColorBrush> 
        </Canvas.Background>
        <!-- Contrôles liés aux fonctions Mode Commande // Mode Edition ********************************************************************************************** -->
            <ItemsControl x:Name="itcListCr"  DataContext="{Binding .}" ItemsSource="{Binding Crs}" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Canvas x:Name="DescriptionReseau" >
                            <!-- PreviewMouseMove="MouseMove" -->
                        <Button x:Name="btnCr" Style="{StaticResource styleBouton}" Width="{Binding Longueur}" Height="{Binding Largeur}" 
                                Canvas.Top="{Binding PosY}" Canvas.Left="{Binding PosX}" 
                                PreviewMouseLeftButtonDown="BtnCr_PreviewMouseLeftButtonDown" PreviewMouseRightButtonDown="BtnCr_PreviewMouseRightButtonDown"  >
                            <Button.Background>
                                <ImageBrush  ImageSource="{Binding ImageCr}" Stretch="Uniform"  />
                            </Button.Background>
                            <Button.RenderTransform>
                                <TransformGroup>
                                    <RotateTransform Angle="{Binding Angle}"/>
                                </TransformGroup>
                            </Button.RenderTransform>
                        </Button>
                    </Canvas>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <!-- ***************************************************************************************************************************************************** -->
        </Canvas>





Dans le code suivant, de la classe Carnet, j'ai une méthode Retirer et une Dupliquer



{
    class Carnet
    {
        FichierXML xml;
        public ObservableCollection<Cr> Crs { get; set; }

        //*********************************************************************************************************************************************/
        //**** Constructeurss **********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public Carnet()
        {
            xml = new FichierXML("E:/5-Train/300-appWPF/CRGareSecrete1.xml");
            Crs = new ObservableCollection<Cr>(xml.LireFichierCRS());
        }

        //*********************************************************************************************************************************************/
        //**** fonctions publiques ********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public void Lire()
        {
            Crs = new ObservableCollection<Cr>(xml.LireFichierCRS());
        }

        public void Enregistrer()    
        {
            xml.EcrireFichierCRS(Crs);
        }

        //*********************************************************************************************************************************************/
        //**** fonctions publiques ********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public void Retirer(string _numCr)
        {
            foreach (Cr cr in Crs)
            {
                if (cr.Num.Equals(_numCr))
                {   Crs.Remove(cr); break;  }
            }
            Enregistrer();
        }

        public void Dupliquer(string _numCr)
        {
            Cr x = new Cr(); Cr y = new Cr();
            //* recherche du Cr à dupliquer
            foreach (Cr cr in Crs)
            {
                if (cr.Num.Equals(_numCr))
                { x = cr; break; }
            }
            //* adaptation du nouveau Cr par rapport au Cr source
            y.Num = RechercheNewNumOrdre(); y.Type = x.Type; y.Valeur = x.Valeur; y.EtatNb = x.EtatNb; y.Ard = x.Ard;
            y.FilePic1 = x.FilePic1; y.FilePic2 = x.FilePic2; y.FilePic3 = x.FilePic3;
            y.PosX = x.PosX + 50; y.PosY = x.PosY + 50; y.Angle = x.Angle; y.Longueur = x.Longueur; y.Largeur = x.Largeur;
            y.AncreNb = x.AncreNb; y.Ancre1 = x.Ancre1; y.Ancre2 = x.Ancre2; y.Ancre3 = x.Ancre3; y.Ancre4 = x.Ancre4;
            //* Ajout du nouveau Cr
            Crs.Add(y);
            Enregistrer();

        }


Ce qui va bien
Crs Remove et Crs.Add modifie bien Crs, vérification par .Count
Enregistrer() met bien à jour le fichier XML
Crs.remove le contrôle concerné est bien effacer dans la fenêtre

Ce qui ne va pas
Crs.Add le nouveau contrôle n'est pas affiché dans la fenêtre
bien sur si je relance l'application il apparaît puisque présent dans le fichier XML

merci de votre aide


Configuration: Windows / Chrome 72.0.3626.121
Afficher la suite 

8 réponses

Meilleure réponse
Messages postés
13451
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
26 juin 2019
350
1
Merci
Bonsoir,

sans essayer pour l’instant ( il me faudrait le code de la classe CR), je vois que carnet n’implemente pas iNotifyPropertyChanged. Ça peut venir de là.

Pour rechercher un élément dans une collection, il y a beaucoup plus simple (et rapide d’executions) : Linq.
Exemple avec la clause SingleOrDefault qui présuppose qui si l’enregistrement existe il n’y a qu’une occurrence.

Cr item = crs.SingleOrDefault(x => x.num == _numcr);

x représente une instance du type de la collection, tu peux l’appeler comme tu veux c’est une variable comme les autre.
S’il existe un enregistrement qui correspond à la clause, elle sera passée à item.
S’il n’y en a pas item vaudra la valeur par défaut du type, dans ton cas null.
S’il y en a plusieurs y’aura une erreur.

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CommentCaMarche

CCM 46534 internautes nous ont dit merci ce mois-ci

Commenter la réponse de Whismeril
Messages postés
13451
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
26 juin 2019
350
1
Merci
CR oui mais pas Carnet.

Ici
        public void MajIllustration()
        {
            if (type.Equals("AID") || type.Equals("AIG"))
            { 
                if (valeur == 1) { valeur = 0; App.W10GS1.MajCommande("Aig " + Num + " DE", ard); CheminImage = pathRails + filePic2 + ".png"; }
                            else { valeur = 1; App.W10GS1.MajCommande("Aig " + Num + " DI", ard); CheminImage = pathRails + filePic1 + ".png"; }
            }
            if (type.Equals("VDR") || type.Equals("VCO"))
            {

            }
            if (type.Equals("FEU"))
            {

            }
        }


Plutôt que des if, utilise un switch

switch(type)
{
     case "AID":
     case "AIG":
            if (valeur == 1)
            { 
                   valeur = 0; App.W10GS1.MajCommande("Aig " + Num + " DE", ard); CheminImage = pathRails + filePic2 + ".png"; 
}
              else 
              { 
                    valeur = 1; App.W10GS1.MajCommande("Aig " + Num + " DI", ard); CheminImage = pathRails + filePic1 + ".png"; }
           break;

       case "VDR":
       case "VCO":


            break;
    

     case "FEU":

        break;


     default:
         //cas non prévus
         break;
}

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CommentCaMarche

CCM 46534 internautes nous ont dit merci ce mois-ci

Commenter la réponse de Whismeril
Messages postés
13451
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
26 juin 2019
350
1
Merci
Bonsoir,

je n'ai pas encore eu le temps de tester ton histoire d'affichage, par contre ta dernière question est assez "simple" à répondre.


Quand on crées une variable, on fait 2 choses, chacune utilise une zone de mémoire.
int toto;
string bidule;
Cr machin;

Ces 3 instructions créent la premières zone mémoire, dans laquelle est stocké "la variable toto pointe vers rien", "la variable bidule pointe vers rien", "la variable Cr pointe vers rien"

toto = 1;
bidule = "coucou";
machin = new Cr();

Ces 3 instructions créent la seconde zone mémoire dans laquelle est stockée la valeur, en parallèle dans la première zone on mets à jour vers où pointe la variable.
Mais ça c'est pas un pointeur, c'est une référence.
(attention, il y a des types valeurs et des types référence)

            //Cr newCr = new Cr();//cette ligne ne sert à rien, car après tu pointes vers  Crs.Last<Cr>();
            
            //* recherche du Cr à dupliquer
            Cr item = Crs.SingleOrDefault(x => x.Num == _numCr);//ici tu crées "juste" item, mais son contenu pointe vers le contenu de Crs.SingleOrDefault(x => x.Num == _numCr), item est une nouvelle référence au même contenu
            
           //Crs.Add(item);//c'est ça qu'il ne faut pas faire, parce que là tu ajoutes une deuxième référence au même contenu à ta liste.

          //voila ce qu'il faut faire
           Cr newCr = new Cr
           {
                  Num = RechercheNewNumOrdre(),
                  PosX = item.PosX + 50,//j'affecte bien une nouvelle valeur, pour 2 raisons, d'abord PosX est un double, un type valeur, la référence n'est pas copiée, c'est la valeur qui est copiée, et ensuite il y a une opération
                  PosY = newCr.PosY + 50//idem
           };
          Crs.Add(newCr);

            Enregistrer();

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CommentCaMarche

CCM 46534 internautes nous ont dit merci ce mois-ci

Commenter la réponse de Whismeril
Messages postés
42
Date d'inscription
jeudi 7 février 2019
Statut
Membre
Dernière intervention
22 mars 2019
0
Merci
Bonsoir Whismeril merci de ta réponse

Penses-tu qu'une vie suffise à assimiler toutes les instruction de WPF ?

en attendant voici la Classe Cr qui comporte bien INotifyPropertyChanged
je vais essayer dans Carnet et te tiens au courant
A+

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace RobTrainV0
{
    public class Cr : INotifyPropertyChanged
    {
        public string pathRails = "E:/5-Train/300-appWPF/Resources/Rails/";
        //*********************************************************************************************************************************************/
        //**** Constructeurs **************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public Cr()
        {
        }

        public Cr(string _crt, string _filePic1)
        {
            CheminImage = "E:/5-Train/300-appWPF/Resources/Ai000DrDirecte.png";
            if (_crt.Equals("AID")) { CheminImage = pathRails + _filePic1 + ".png"; }
            if (_crt.Equals("AIG")) { CheminImage = pathRails + _filePic1 + ".png"; }
            if (_crt.Equals("VDR")) { CheminImage = "E:/5-Train/300-appWPF/Resources/Rails/" + _filePic1 + ".png"; }
            //MessageBox.Show("_filePic" + _filePic + "   CheminImage:   " + CheminImage);
        }
        //*********************************************************************************************************************************************/

        private string num;
        public string Num
        {   get {   return num; }
            set {   if (num != value)   {   num = value;        GenerePropertyChanged("Num");   }   }
        }
        private string type;
        public string Type
        {   get { return type; }
            set { if (type != value) { type = value; GenerePropertyChanged("Type"); } }
        }
        private Int32 valeur;
        public Int32 Valeur
        {   get { return valeur; }
            set { if (valeur != value) { valeur = value; GenerePropertyChanged("Valeur"); } }
        }
        private Int32 etatNb;
        public Int32 EtatNb
        {   get { return etatNb; }
            set { if (etatNb != value) { etatNb = value; GenerePropertyChanged("EtatNb"); } }
        }
        private string ard;
        public string Ard
        {   get { return ard; }
            set { if (ard != value) { ard = value; GenerePropertyChanged("Ard"); } }
        }

        //*********************************************************************************************************************************************/
        //** Méthodes public **************************************************************************************************************************/
        //*********************************************************************************************************************************************/


        public void MajIllustration()
        {
            if (type.Equals("AID") || type.Equals("AIG"))
            { 
                if (valeur == 1) { valeur = 0; App.W10GS1.MajCommande("Aig " + Num + " DE", ard); CheminImage = pathRails + filePic2 + ".png"; }
                            else { valeur = 1; App.W10GS1.MajCommande("Aig " + Num + " DI", ard); CheminImage = pathRails + filePic1 + ".png"; }
            }
            if (type.Equals("VDR") || type.Equals("VCO"))
            {

            }
            if (type.Equals("FEU"))
            {

            }
        }

        //*********************************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        private string cheminImage;
        /// <summary>
        /// Chemin de l'image
        /// </summary>
        public string CheminImage
        {
            get { return cheminImage; }
            set
            {
                if (cheminImage != value && !string.IsNullOrWhiteSpace(value))
                {
                    cheminImage = value;
                    GenerePropertyChanged("CheminImage");

                    var yourImage = new BitmapImage(new Uri(cheminImage, UriKind.Relative));
                    yourImage.Freeze(); // -> to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
                    ImageCr = yourImage;
                }
            }
        }


        //*********************************************************************************************************************************************/
        private ImageSource imageCr;
        /// <summary>
        /// Image de la loco
        /// </summary>
        public ImageSource ImageCr
        {   get { return imageCr; }
            set { if (imageCr != value) { imageCr = value; GenerePropertyChanged("ImageCr"); } }
        }


        /// Non de fichier des trois illustrations du composant
        private string filePic1;
        public string FilePic1
        {   get { return filePic1; }
            set { if (filePic1 != value) { filePic1 = value; GenerePropertyChanged("FilePic1"); } }
        }
        private string filePic2;
        public string FilePic2
        {   get { return filePic2; }
            set { if (filePic2 != value) { filePic2 = value; GenerePropertyChanged("FilePic2"); } }
        }
        private string filePic3;
        public string FilePic3
        {   get { return filePic3; }
            set { if (filePic3 != value) { filePic3 = value; GenerePropertyChanged("FilePic3"); } }
        }

        //*********************************************************************************************************************************************/
        private Double posX;
        public Double PosX
        {   get {   return posX; }
            set {   if (posX != value)      {   posX = value;       GenerePropertyChanged("PosX");      }   }
        }

        private Double posY;
        public Double PosY
        {   get {   return posY; }
            set {   if (posY != value)      {   posY = value;       GenerePropertyChanged("PosY");      }   }
        }

        private Decimal angle;
        public Decimal Angle
        {   get {   return angle; }
            set {   if (angle != value)     {   angle = value;      GenerePropertyChanged("Angle");     }   }
        }

        private Int32 longueur;
        public Int32 Longueur
        {   get {   return longueur; }
            set {   if (longueur != value)  {   longueur = value;   GenerePropertyChanged("Longueur");  }   }
        }

        private Int32 largeur;
        public Int32 Largeur
        {   get { return largeur; }
            set { if (largeur != value)     { largeur = value;      GenerePropertyChanged("Largeur");   }   }
        }

        //*********************************************************************************************************************************************/
        private string tileMode1;
        public string TileMode1
        {   get { return tileMode1; }
            set { if (tileMode1 != value) { tileMode1 = value; GenerePropertyChanged("TileMode1"); } }
        }

        private string stretch1;
        public string Stretch1
        {   get { return stretch1; }
            set { if (stretch1 != value) { stretch1 = value; GenerePropertyChanged("Stretch1"); } }
        }

        private string borderBrush;
        public string BorderBrush
        {   get { return borderBrush; }
            set { if (borderBrush != value) { borderBrush = value; GenerePropertyChanged("BorderBrush"); } }
        }

        private Int16 borderThickness1;
        public Int16 BorderThickness1
        {   get { return borderThickness1; }
            set { if (borderThickness1 != value) { borderThickness1 = value; GenerePropertyChanged("BorderThickness1"); } }
        }

        //*********************************************************************************************************************************************/
        private Int32 ancreNb;
        public Int32 AncreNb
        {   get { return ancreNb; }
            set { if (ancreNb != value) { ancreNb = value; GenerePropertyChanged("AncreNb"); } }
        }
        private Int32 ancre1;
        public Int32 Ancre1
        {   get { return ancre1; }
            set { if (ancre1 != value) { ancre1 = value; GenerePropertyChanged("Ancre1"); } }
        }
        private Int32 ancre2;
        public Int32 Ancre2
        {   get { return ancre2; }
            set { if (ancre2 != value) { ancre2 = value; GenerePropertyChanged("Ancre2"); } }
        }
        private Int32 ancre3;
        public Int32 Ancre3
        {   get { return ancre3; }
            set { if (ancre3 != value) { ancre3 = value; GenerePropertyChanged("Ancre3"); } }
        }
        private Int32 ancre4;
        public Int32 Ancre4
        {   get { return ancre4; }
            set { if (ancre4 != value) { ancre4 = value; GenerePropertyChanged("Ancre4"); } }
        }



        /// <summary>
        /// Collection  du composant reseau
        /// </summary>
        /// <remarks>le type KeyValuePair est le type de base d'un dictionnaire, il associe une clé à une valeur, là je m'en sert pour dire si le numéro est le domicile, le bureau etc...</remarks>
        //public ObservableCollection<KeyValuePair<string, string>> Aiguillages { get; set; }



        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        private void GenerePropertyChanged(string Propriete)
        {
            if (this.PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(Propriete));
        }

        #endregion 
    }
}
Commenter la réponse de robunccm
Messages postés
42
Date d'inscription
jeudi 7 février 2019
Statut
Membre
Dernière intervention
22 mars 2019
0
Merci
Re tests non concluant dans Carnet

ce que je ne comprends vraiment pas
c'est qu'un Crs.remove provoque bien la mise à jour de la fenêtre avec l'effacement du Contrôle
alors que le Crs.add ne le fait pas en tout cas le Contrôle ne s'affiche pas.
Je vais cherché s'il y a un événement associé à la mise à jour de la fenêtre pour affiner mon diagnostic
Commenter la réponse de robunccm
Messages postés
42
Date d'inscription
jeudi 7 février 2019
Statut
Membre
Dernière intervention
22 mars 2019
0
Merci
Bonjour Whismeril
merci pour les deux conseils surtout le premier
pour le second je connaît bien sur le Switch mais je n'avais jamais pensé à cette manière pour faire un OU

Je reviens sur mon problème initial
Je pense que l'affichage se faisait que mais la duplication se superpose au modèle


Par contre ci-dessous je ne comprends ce qui se passe

        public void Dupliquer(string _numCr)
        {
            Cr newCr = new Cr();
            //* recherche du Cr à dupliquer
            Cr item = Crs.SingleOrDefault(x => x.Num == _numCr);
            Crs.Add(item);
            //* adaptation du nouveau Cr par rapport au Cr source
            newCr = Crs.Last<Cr>();
            newCr.Num = RechercheNewNumOrdre();
            newCr.PosX = newCr.PosX + 50;
            newCr.PosY = newCr.PosY + 50;
            Enregistrer();
        }

dans ce code Crs.Add(item) ajoute bien le Cr correspondant au Contrôle cliqué
la fonction RechercheNewNumOrdre() renvois un numéro de Clé correspondant au plus .Num incrémené
je le sauvegarde dans newCr.num
j'incrémente également newCr.PosX et newCr.PosY pour décaler le nouveau des deux Contrôles

hors si les modifications se font bien dans le newCr
elles sont également reportées dans item
dit autrement si l'élément à dupliquer était le cinquième de la liste et l'élément additionné est en onzième
les deux éléments sont strictement identiques avec les nouveaux newCr.Num newCr.PosX newCr.PosY

je suis assez dérouté par ce constat, dois-je chercher du côté des pointeurs, du clonage ... ?

si tu as une piste merci d'avance
A+ Roland
Commenter la réponse de robunccm
Messages postés
42
Date d'inscription
jeudi 7 février 2019
Statut
Membre
Dernière intervention
22 mars 2019
0
Merci
Merci de ton explication aussi claire qu' efficace.
Cela a résolu mon problème d'Ajout qui fonctionne maintenant comme je le souhaite.

Malheureusement le problème de l'affichage subsiste
Il n'y a pas d'urgence j'ai bien d'autres choses à traiter

Je continue toutefois à chercher par moi-même car au début de ton assistance nous avions
traité un besoin similaire avec l'affichage des locomotives disponibles ou choisies
et cela fonctionne très bien.

A priori la seule différence est que c'était dans un ListBox alors que là je suis dans un Canvas
mais je ne pense pas que se soit une bonne piste
Commenter la réponse de robunccm
Messages postés
13451
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
26 juin 2019
350
0
Merci
Bonsoir

il me manquait pas mal de choses, donc j'ai soit commenté des parties de codes, soit bouchés des trous.
Et ce faisant, chez moi ça marche et je ne sais pas vraiment pourquoi pas chez toi.

Voici donc ma "version"

Pour CR j'ai juste ajouté une valeur par défaut à Largeur et Longueur
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Test_WPF
{
    public class Cr : INotifyPropertyChanged
    {
        public string pathRails = "E:/5-Train/300-appWPF/Resources/Rails/";
        //*********************************************************************************************************************************************/
        //**** Constructeurs **************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public Cr()
        {
        }

        public Cr(string _crt, string _filePic1)
        {
            CheminImage = "E:/5-Train/300-appWPF/Resources/Ai000DrDirecte.png";
            if (_crt.Equals("AID")) { CheminImage = pathRails + _filePic1 + ".png"; }
            if (_crt.Equals("AIG")) { CheminImage = pathRails + _filePic1 + ".png"; }
            if (_crt.Equals("VDR")) { CheminImage = "E:/5-Train/300-appWPF/Resources/Rails/" + _filePic1 + ".png"; }
            //MessageBox.Show("_filePic" + _filePic + "   CheminImage:   " + CheminImage);
        }
        //*********************************************************************************************************************************************/

        private string num;
        public string Num
        {
            get { return num; }
            set { if (num != value) { num = value; GenerePropertyChanged("Num"); } }
        }
        private string type;
        public string Type
        {
            get { return type; }
            set { if (type != value) { type = value; GenerePropertyChanged("Type"); } }
        }
        private Int32 valeur;
        public Int32 Valeur
        {
            get { return valeur; }
            set { if (valeur != value) { valeur = value; GenerePropertyChanged("Valeur"); } }
        }
        private Int32 etatNb;
        public Int32 EtatNb
        {
            get { return etatNb; }
            set { if (etatNb != value) { etatNb = value; GenerePropertyChanged("EtatNb"); } }
        }
        private string ard;
        public string Ard
        {
            get { return ard; }
            set { if (ard != value) { ard = value; GenerePropertyChanged("Ard"); } }
        }

        //*********************************************************************************************************************************************/
        //** Méthodes public **************************************************************************************************************************/
        //*********************************************************************************************************************************************/


        public void MajIllustration()
        {
            //if (type.Equals("AID") || type.Equals("AIG"))
            //{
            //    if (valeur == 1) { valeur = 0; App.W10GS1.MajCommande("Aig " + Num + " DE", ard); CheminImage = pathRails + filePic2 + ".png"; }
            //    else { valeur = 1; App.W10GS1.MajCommande("Aig " + Num + " DI", ard); CheminImage = pathRails + filePic1 + ".png"; }
            //}
            //if (type.Equals("VDR") || type.Equals("VCO"))
            //{

            //}
            //if (type.Equals("FEU"))
            //{

            //}
        }

        //*********************************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        //*********************************************************************************************************************************************/
        private string cheminImage;
        /// <summary>
        /// Chemin de l'image
        /// </summary>
        public string CheminImage
        {
            get { return cheminImage; }
            set
            {
                if (cheminImage != value && !string.IsNullOrWhiteSpace(value))
                {
                    cheminImage = value;
                    GenerePropertyChanged("CheminImage");

                    var yourImage = new BitmapImage(new Uri(cheminImage, UriKind.Relative));
                    yourImage.Freeze(); // -> to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
                    ImageCr = yourImage;
                }
            }
        }


        //*********************************************************************************************************************************************/
        private ImageSource imageCr;
        /// <summary>
        /// Image de la loco
        /// </summary>
        public ImageSource ImageCr
        {
            get { return imageCr; }
            set { if (imageCr != value) { imageCr = value; GenerePropertyChanged("ImageCr"); } }
        }


        /// Non de fichier des trois illustrations du composant
        private string filePic1;
        public string FilePic1
        {
            get { return filePic1; }
            set { if (filePic1 != value) { filePic1 = value; GenerePropertyChanged("FilePic1"); } }
        }
        private string filePic2;
        public string FilePic2
        {
            get { return filePic2; }
            set { if (filePic2 != value) { filePic2 = value; GenerePropertyChanged("FilePic2"); } }
        }
        private string filePic3;
        public string FilePic3
        {
            get { return filePic3; }
            set { if (filePic3 != value) { filePic3 = value; GenerePropertyChanged("FilePic3"); } }
        }

        //*********************************************************************************************************************************************/
        private Double posX;
        public Double PosX
        {
            get { return posX; }
            set { if (posX != value) { posX = value; GenerePropertyChanged("PosX"); } }
        }

        private Double posY;
        public Double PosY
        {
            get { return posY; }
            set { if (posY != value) { posY = value; GenerePropertyChanged("PosY"); } }
        }

        private Decimal angle;
        public Decimal Angle
        {
            get { return angle; }
            set { if (angle != value) { angle = value; GenerePropertyChanged("Angle"); } }
        }

        private Int32 longueur = 50;
        public Int32 Longueur
        {
            get { return longueur; }
            set { if (longueur != value) { longueur = value; GenerePropertyChanged("Longueur"); } }
        }

        private Int32 largeur = 30;
        public Int32 Largeur
        {
            get { return largeur; }
            set { if (largeur != value) { largeur = value; GenerePropertyChanged("Largeur"); } }
        }

        //*********************************************************************************************************************************************/
        private string tileMode1;
        public string TileMode1
        {
            get { return tileMode1; }
            set { if (tileMode1 != value) { tileMode1 = value; GenerePropertyChanged("TileMode1"); } }
        }

        private string stretch1;
        public string Stretch1
        {
            get { return stretch1; }
            set { if (stretch1 != value) { stretch1 = value; GenerePropertyChanged("Stretch1"); } }
        }

        private string borderBrush;
        public string BorderBrush
        {
            get { return borderBrush; }
            set { if (borderBrush != value) { borderBrush = value; GenerePropertyChanged("BorderBrush"); } }
        }

        private Int16 borderThickness1;
        public Int16 BorderThickness1
        {
            get { return borderThickness1; }
            set { if (borderThickness1 != value) { borderThickness1 = value; GenerePropertyChanged("BorderThickness1"); } }
        }

        //*********************************************************************************************************************************************/
        private Int32 ancreNb;
        public Int32 AncreNb
        {
            get { return ancreNb; }
            set { if (ancreNb != value) { ancreNb = value; GenerePropertyChanged("AncreNb"); } }
        }
        private Int32 ancre1;
        public Int32 Ancre1
        {
            get { return ancre1; }
            set { if (ancre1 != value) { ancre1 = value; GenerePropertyChanged("Ancre1"); } }
        }
        private Int32 ancre2;
        public Int32 Ancre2
        {
            get { return ancre2; }
            set { if (ancre2 != value) { ancre2 = value; GenerePropertyChanged("Ancre2"); } }
        }
        private Int32 ancre3;
        public Int32 Ancre3
        {
            get { return ancre3; }
            set { if (ancre3 != value) { ancre3 = value; GenerePropertyChanged("Ancre3"); } }
        }
        private Int32 ancre4;
        public Int32 Ancre4
        {
            get { return ancre4; }
            set { if (ancre4 != value) { ancre4 = value; GenerePropertyChanged("Ancre4"); } }
        }



        /// <summary>
        /// Collection  du composant reseau
        /// </summary>
        /// <remarks>le type KeyValuePair est le type de base d'un dictionnaire, il associe une clé à une valeur, là je m'en sert pour dire si le numéro est le domicile, le bureau etc...</remarks>
        //public ObservableCollection<KeyValuePair<string, string>> Aiguillages { get; set; }



        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        private void GenerePropertyChanged(string Propriete)
        {
            if (this.PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(Propriete));
        }

        #endregion 
    }
}


Pour carnet, j'ai mis un chemin de fichier relatif, commenté une méthode, ajouté la méthode de recherche de numéro
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test_WPF
{
    class Carnet
    {
        FichierXML xml;
        public ObservableCollection<Cr> Crs { get; set; }

        //*********************************************************************************************************************************************/
        //**** Constructeurss **********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public Carnet()
        {
            xml = new FichierXML("testCR.xml");
            Crs = new ObservableCollection<Cr>(xml.LireFichierCRS());
        }

        //*********************************************************************************************************************************************/
        //**** fonctions publiques ********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public void Lire()
        {
            Crs = new ObservableCollection<Cr>(xml.LireFichierCRS());
        }

        public void Enregistrer()
        {
            xml.EcrireFichierCRS(Crs);
        }

        //*********************************************************************************************************************************************/
        //**** fonctions publiques ********************************************************************************************************************/
        //*********************************************************************************************************************************************/
        public void Retirer(string _numCr)
        {
            foreach (Cr cr in Crs)
            {
                if (cr.Num.Equals(_numCr))
                { Crs.Remove(cr); break; }
            }
            Enregistrer();
        }

        public void Dupliquer(string _numCr)
        {
            Cr item = Crs.SingleOrDefault(x => x.Num == _numCr);//ici tu crées "juste" item, mais son contenu pointe vers le contenu de Crs.SingleOrDefault(x => x.Num == _numCr), item est une nouvelle référence au même contenu

            //voila ce qu'il faut faire
            Cr newCr = new Cr
            {
                Num = RechercheNewNumOrdre(),
                PosX = item.PosX + 50,//j'affecte bien une nouvelle valeur, pour 2 raisons, d'abord PosX est un double, un type valeur, la référence n'est pas copiée, c'est la valeur qui est copiée, et ensuite il y a une opération
                PosY = item.PosY + 50//idem
            };
            Crs.Add(newCr);

            Enregistrer();

        }

        private string RechercheNewNumOrdre()
        {
            int numeroMax = Crs.Select(x => Convert.ToInt32(x.Num)).Max();
            return (numeroMax + 1).ToString();
        }
    }
}


Le fichier xml
<?xml version="1.0" encoding="utf-8"?>
<Crs>
  <CR>
    <Num>1</Num>
    <PosX>25</PosX>
    <PosY>25</PosY>
  </CR>
  <CR>
    <Num>2</Num>
    <PosX>75</PosX>
    <PosY>75</PosY>
  </CR>
  <CR>
    <Num>3</Num>
    <PosX>75</PosX>
    <PosY>25</PosY>
  </CR>
  <CR>
    <Num>4</Num>
    <PosX>125</PosX>
    <PosY>125</PosY>
  </CR>
</Crs>


Et la classe de lecture
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Test_WPF
{

    class FichierXML
    {
        string filename;
        public FichierXML(string FileName)
        {
            filename = FileName;
        }

        public IEnumerable<Cr> LireFichierCRS()
        {
            XDocument xdoc = XDocument.Load(filename);

            return (from c in xdoc.Descendants("CR")
                    select new Cr
                    {
                        Num = c.Element("Num").Value,
                        PosX = Convert.ToDouble(c.Element("PosX").Value),
                        PosY = Convert.ToDouble(c.Element("PosY").Value),
                    }

                    );
        }


        public void EcrireFichierCRS(IEnumerable<Cr> Crs)
        {
            XDocument xdoc = new XDocument
                (
                new XElement("Crs",
                from c in Crs
                select new XElement("CR",
                    new XElement("Num", c.Num),
                    new XElement("PosX", c.PosX),
                    new XElement("PosY", c.PosY))));

            xdoc.Save(filename);

        }
    }
}


j'ai pas touché au xaml.

Et voici comment j'ai codé les 3 évènements, j'ai choisi de faire la duplication sur le click droit
        private void MainCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {

        }

        private void BtnCr_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {

        }

        private void BtnCr_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            carnet.Dupliquer(((Cr)((Button)sender).DataContext).Num);
        }



Si mes suppositions sur le fonctionnement sont juste, il y a moyen d'optimiser.
Commenter la réponse de Whismeril