Problème picturebox qui clignote...

Résolu
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009 - 9 juil. 2009 à 08:52
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009 - 17 juil. 2009 à 15:25
Bonjour à tous,

Je vais essayer de vous expliquer mon problème. Il faut imaginer une sorte de Puzzle représenté par une grosse picturebox1 qui représente le fond (ex: un paysage) sur laquelle j'ai généré dynamiquement par le code des "pièces de puzzle" sous formes de calques qui sont en fait des SuperPicturebox (super car elles gèrent la transparence et l'opacité). Donc quand toutes les pieces sont visibles on ne voit plus le fond, logique non ?

l'objectif est que quand on clique sur une piece elle se retourne (en faisant passer la propriété "visible" de la piece a false ou true). Jusque le pas de problème quand je clique sur une piece elle se retourne bien mais aussi l'ensembles des autres pièces semblent disparaitre 1/10eme de secondes puis réapparaitre normalement.d'ou cette sensation de clignotement à chaque fois qu'une des pièces du puzzle change d'état (visible non visible).

Je précise que tous les calques (les "pièces") ont le même parent: picturebox1 (le fond).

Donc si vous avez une idée de l'origine du problème merci ...

Mon projet n'est pas exactement un puzzle mais pour illustrer ceci voici mon code de génération des superPicturebox et de la gestion du mouseClick:

public partial class Form1 : Form
    {
       
        string[] TabNomDeFichier = new string[20];
        PictureBoxSuper[] CalqueEnCours = new PictureBoxSuper[20];
        PictureBoxSuper ImageVide = new PictureBoxSuper();
       

         public class PictureBoxSuper : Panel 
                { 
              ......
         
            }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
           
           
            TabNomDeFichier[1] = "G:\\temp\\calque_01.png";
            TabNomDeFichier[2] = "G:\\temp\\calque_02.png";
            TabNomDeFichier[3] = "G:\\temp\\calque_03.png";
            TabNomDeFichier[4] = "G:\\temp\\calque_04.png";
            TabNomDeFichier[5] = "G:\\temp\\calque_05.png";
            TabNomDeFichier[6] = "G:\\temp\\calque_06.png";
            TabNomDeFichier[7] = "G:\\temp\\calque_07.png";

             for (int intCompteur = 1; intCompteur <= 7; intCompteur++)
            {
                CalqueEnCours[intCompteur] = new PictureBoxSuper();
                CalqueEnCours[intCompteur].Name = "Calque" + intCompteur;
                CalqueEnCours[intCompteur].Image = new Bitmap(TabNomDeFichier[intCompteur]);
                CalqueEnCours[intCompteur].Opacity = 1f;
                CalqueEnCours[intCompteur].Location = new System.Drawing.Point(0, 0);
                CalqueEnCours[intCompteur].Size = new System.Drawing.Size(634, 1366);
                CalqueEnCours[intCompteur].MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);
                pictureBox1.Controls.Add(CalqueEnCours[intCompteur]);
               
                 CalqueEnCours[intCompteur].Visible = false;
               
               
            }

        }

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {

                      
            Color clCouleurTransparente = new Color();
            Bitmap bmpReferenceTransp = CalqueEnCours[1].Image as Bitmap;
            clCouleurTransparente = bmpReferenceTransp.GetPixel(0, 0);  //définie la couleur transparente pour determiner si la sourie se trouve sur un des calques

            // Tests de la position du pointeur sourie par rapport aux des Calques. si click dans zone dessin => affiche calque
            for (int intCompteur= 1; intCompteur <= 7; intCompteur++)
            {
                Bitmap bmp = CalqueEnCours[intCompteur].Image as Bitmap;
                if (clCouleurTransparente.R != bmp.GetPixel(e.X, e.Y).R) // test de la couche alpha du calque pour savoir si la sourie pointe sur une region déssiné.
                {
                    if (CalqueEnCours[intCompteur].Visible)
                        CalqueEnCours[intCompteur].Visible=false;//CalqueEnCours[intCompteur].Visible = false;
                    else CalqueEnCours[intCompteur].Visible= true;//CalqueEnCours[intCompteur].Visible = true;
                   
                }
               
            }
 
      }

10 réponses

leprov Messages postés 1160 Date d'inscription vendredi 23 juillet 2004 Statut Membre Dernière intervention 21 octobre 2010 17
9 juil. 2009 à 11:55
Ta picture box hérite de panel qui n'est pas double bufferisée. Trois solutions :

1 - hérite de picture box et non de panel
2 - Active le double buffering sur ta picturebox super dans le constructeur (DoubleBuffering = true, je crois, un truc comme ca)
3 - gère toi un double buffering a la main.

A toi de voir ce que tu préfère, le plus simple étant de loin la solution 1
3
leprov Messages postés 1160 Date d'inscription vendredi 23 juillet 2004 Statut Membre Dernière intervention 21 octobre 2010 17
10 juil. 2009 à 11:25
 ce que tu propose, cest juste gérer ton double buffering a la main avec une seule picturebox. alors oui une seule picture box ca serait mieux. gérer le double buffer a la main...Bah si la picturebox le fait pour toi, ca va juste etre un traitement inutile, autant dessiner directement dans le graphics de la picturebox
3
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
17 juil. 2009 à 15:25
voilà c'est fait avec une seule picturebox sur laquel je dessine avec un objet graphique... J'en ai profité pour tenter de créer une class "Calque" toute simple pour stocker le chemin de fichier, et son état (actif ou inactif).

voilà le code et ça marche... merci encore pour vos conseils

public partial class Form1 : Form
{
// Définition des constantes
const string stgCheminFichiers = "G:\\temp\\Homme\";

// Définition des variables
Calque[] CalqueImage = new Calque[20];
Graphics obj_Graphique;

private void DessineImageActive() // Dessine sur l'objet graphique de la picturebox1 les calques actifs.
{
for (int intCompteur = 0; intCompteur <= 7; intCompteur++)
{
if (CalqueImage[intCompteur].Actif == 1)
{
obj_Graphique.DrawImage(CalqueImage[intCompteur].Image, 0, 0, 634, 1366);
}
}
}

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
for (int intCompteur = 0; intCompteur <= 7; intCompteur++) CalqueImage[intCompteur] = new Calque();
CalqueImage[0].FichierChemin = stgCheminFichiers + "fond.png";
CalqueImage[1].FichierChemin = stgCheminFichiers + "piece1.png";
CalqueImage[2].FichierChemin = stgCheminFichiers + "piece2.png";
CalqueImage[3].FichierChemin = stgCheminFichiers + "piece3.png";
CalqueImage[4].FichierChemin = stgCheminFichiers + "piece4.png";
CalqueImage[5].FichierChemin = stgCheminFichiers + "piece5.png";
CalqueImage[6].FichierChemin = stgCheminFichiers + "piece6.png";
CalqueImage[7].FichierChemin = stgCheminFichiers + "piece7.png";

// desactivation de tous les calques sauf le premier[0] qui est le fond
CalqueImage[0].Actif = 1;

//création de l'objet graphique permettant de dessiner sur la picturebox1
obj_Graphique = Graphics.FromImage(pictureBox1.Image);

}

private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
Color clCouleurTransparente = new Color();

clCouleurTransparente = CalqueImage[1].Bitmap.GetPixel(0, 0); //définie la couleur transparente pour determiner si la sourie se trouve sur un des calques

// Tests de la position du pointeur sourie par rapport aux des Calques. si click dans zone dessin => affiche calque
for (int intCompteur = 1; intCompteur <= 7; intCompteur++)
{
if (clCouleurTransparente.A != CalqueImage[intCompteur].Bitmap.GetPixel(e.X, e.Y).A) // test de la couche alpha du calque pour savoir si la sourie pointe sur une region déssiné.
{
if (CalqueImage[intCompteur].Actif == 0)
CalqueImage[intCompteur].Actif = 1;
else
CalqueImage[intCompteur].Actif = 0;
}
}

// Redessine l'image de la picturebox1 en fonction des calques actifs
DessineImageActive();
pictureBox1.Invalidate(); // force la mise a jour de l'affichage de la picturebox1
}

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
obj_Graphique.Dispose();
}
}
3
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
9 juil. 2009 à 18:06
Merci beaucoup pour ta réponse LeProv.

J'ai donc essayer la première méthode, en héritant ma SuperPictureBox de PictureBox au lieur de Panel, mais la mon control ne semblait plus fonctionner du tout et comme je suis débutant en programmation et encore plus en C# je t'avouerais que j'ai pas beaucoup chercher plus loin dans cette solution. J'ai quand même essayé avec un autre type de SuperPictureBox hérité de Control cette fois ci (trouvé sur le forum) ayant à peu près les mêmes propriété mais sans réglage de l'opacité possible... et le résultat est identique: scintillement / clignotement des calques visibles (mais pas la Picturebox de fond) à chaque clic sur l'un ou l'autre. => echec solution 1 ?

Ensuite j'ai essayé de trouver le moyen d'activer le double buffering sur ma classe SuperPictureBox dérivé de panel, avec ce bout de code trouvé sur un autre forum:

public SuperPictureBox()
    {

        // Set the value of the double-buffering style bits to true.

        this.DoubleBuffered =
true;

        this.SetStyle(
ControlStyles.AllPaintingInWmPaint |

        ControlStyles.UserPaint |

        ControlStyles.OptimizedDoubleBuffer,
true);

        UpdateStyles();

    }

 et là j'ai un autre soucis qui apparait: quand j'active un des calque par un clic, l'image apparait bien mais la zone devant resté transaparente devient toute noir et cache donc la picturebox1 et tous les autres calques activés. J'ai fait la même chose avec ma classe dérivé de Control et j'ai exactement le même resultat  ==> Avec cette solution la transparence passe à la trappe.... y a quelque chose à faire ???

Enfin, reste la solution du double buffering à la main... mais là je crois que j'ai encore des progres à faire dans la compréhension du langage C# et le fonctionnement des buffers avant de me lancer la dedans ==> cette solution me semble hors de porté actuellement.

En tous les cas merci pour tes conseils... c'etait très enrichissant et malgré tout, ton histoire de buffer multiple me semble toujours être la solution à mon problème. Mais bon si quelqu'un d'autre à une autre idée... n'hésitez pas

Est il possible d'activer le double buffering sur Ma SuperPictureBox sans pour autant perdre la transparence ? Pour infos voici le code de la class SuperPictureBox dérivé de panel que j'ai trouvé afin de permettre la gestion de la transparence de mes calques:

public class PictureBoxSuper : Panel 
                { 
 
            public Image Image; 
             
            /// <summary> 
             
            /// Définie la fenetre comme transparente
             
            /// </summary> 
             
            protected override CreateParams CreateParams 
             
            { 
             
            get 
             
            { 
             
            CreateParams cp = base.CreateParams; 
             
            cp.ExStyle |= 0x20; //WS_EX_TRANSPARENT  (fenetre transparente)
            return cp; 
             
            } 
             
            } 
             
            public PictureBoxSuper() 
             
            { 
             
            Opacity = 0.5f;

            // Set the value of the double-buffering style bits to true.  <== ajout perso pour gestion du double buffer mais ca fait planté la transparence !!

            this.DoubleBuffered = true;

            this.SetStyle(ControlStyles.AllPaintingInWmPaint |

            ControlStyles.UserPaint |

            ControlStyles.OptimizedDoubleBuffer, true);

            UpdateStyles();
                         
            } 
             
            /// <summary> 
             
            /// This is important: redraw all other controls under PanelEx first 
             
            /// </summary> 
             
            public void InvalidateEx() 
             
            { 
             
            if (Parent == null) 
             
            return; 
             
            Rectangle rc = new Rectangle( this.Location, this.Size ); 
             
            Parent.Invalidate( rc, true ); 
             
            } 
             
           
            protected float opacity; 
             
            protected ImageAttributes imgAttributes; 
             
            /// <summary> 
                          /// 1.0 no transparency, 0.0 fully transparent 
             
            /// </summary> 
             
            public float Opacity 
             
            { 
             
            get { return opacity; } 
             
            set 
             
            { 
             
            opacity = value; 
             
            if (imgAttributes != null) 
             
            imgAttributes.Dispose(); 
             
            // define alpha coefficient 
             
            float[][] colorMatrixElements =
            { 
            new float[] {1,  0,  0,  0, 0}, //r 
             
            new float[] {0,  1,  0,  0, 0},//g 
             
            new float[] {0,  0,  1,  0, 0},//b 
             
            new float[] {0,  0,  0,  value, 0}, 
             
            new float[] {0, 0, 0, 0, 1}
            }; 
             
            ColorMatrix colorMatrix = new ColorMatrix( colorMatrixElements ); 
             
            imgAttributes = new ImageAttributes(); 
             
            imgAttributes.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default,ColorAdjustType.Bitmap ); 
             
            } 
             
            } 
             
            /// <summary> 
             
            /// Dessine l'image translucide 
             
            /// </summary> 
             
            ///

 
             
            protected override void OnPaint( PaintEventArgs pe ) 
             
            { 
             
            Bitmap bmp = Image as Bitmap; 
             
            if (bmp == null) 
             
            return; 
             
            pe.Graphics.DrawImage(Image,new Rectangle( 0, 0, Size.Width, Size.Height),0,0,Image.Width,Image.Height,GraphicsUnit.Pixel,imgAttributes ); 
             
            } 
             
            protected override void OnPaintBackground( PaintEventArgs pevent ) 
             
            { 
             
            // left intentionally blank! 
             
            } 
             
            protected override void OnLocationChanged( EventArgs e ) 
             
            { 
             
            base.OnLocationChanged( e ); 
             
            InvalidateEx(); // redraw everything underneath this control 
             
            } 
             
            }

merci de votre aide  
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
9 juil. 2009 à 18:28
Salut,
Pas regardé en détail, mais apparement tu te mets en UserPaint ce qui veut dire que tu gères l'affichage toi même.
Essayes donc juste cette ligne:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

<hr />
-Blog-
-Site Perso-
0
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
9 juil. 2009 à 18:37
Merci beaucoup....je viens de tester mais j'ai le même résultat   le calque apparait bien mais la zone devant être transparente apparait en noir.
0
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
9 juil. 2009 à 19:13
le fait de passer par un buffer intermédiaire fait perdre la transparence... car peut être par défaut le buffer intermédiaire est initialisé par des pixels noirs (0,0,0), donc forcement quand on colle dessus une image transparente ça nous donne au final une belle image toute noir... je fabule ou c'est un truc de ce genre qui se passe ?

ce qui est zarbi c'est que quand je clic sur une "pièce" ce n'est pas le "scintillement" de ce calque qui est génant mais plutôt celui de tous les autres qui sont activés à ce moment là.

D'ailleurs, en y repensant j'avais déjà remarqué au départ du projet un problème similaire avec l'image de fond car au lieu d'utiliser une picturebox simple j'utilisait également une de mes SuperPictureBox. Et donc par hasard et sans savoir pourquoi j'avais déjà résolu le problème en la remplacant par une PictureBox simple(donc doublebufférisé !!). Mais là, j'avais pas ce problème de transparence puisque c'était l'image de fond...

...donc maintenant reste à trouvé le génie qui pourrait m'aider à coder un double buffer qui conserve la transparence...  

ps: ou sinon faut que je trouve un code qui me permette de mettre à jour seulement la zone de l'image qui change, donc la zone non transparente du calque activé / désactivé.
0
leprov Messages postés 1160 Date d'inscription vendredi 23 juillet 2004 Statut Membre Dernière intervention 21 octobre 2010 17
10 juil. 2009 à 09:19
Ta transparence dégage très probablement car tu ouvre tes fichiers en tant que bitmap (format de fichier qui ne gère pas la transparence et la remplace par du noir). Dans ton Form, lorsque tu initialise ta propriété image de ton calque, essaie avec Image.FromFile plutot qu'avec new Bitmap, ca devrait régler ton soucis
0
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
10 juil. 2009 à 11:08
Tiens Merci  je vais essayer ta proposition...

Sinon je me disais que j'allai virer mes SuperPictureBox et remplacer le tout par des objets bitmap indépendant pour le fond et chaque calques. Ensuite après une boucle de test j'ajouterais les calques "actifs" grace à un objet Graphics et avec la méthode "Grapichs.DrawImage" sur une autre bitmap qui me servira de Buffer. Finalement il me restera plus qu'a transferer directement mon buffer sur la form. Ca parait faisable et plus simple non? En tous les cas Merci pour ma m'avoir aiguillé sur cette histoire de buffer... Je testerais tout ca la semaine prochaine parce que la j'aurrais pas le temps avant... Donc Merci beaucoup et.Wait & See.

 
0
vogel69 Messages postés 10 Date d'inscription jeudi 4 décembre 2003 Statut Membre Dernière intervention 17 juillet 2009
10 juil. 2009 à 12:45
bon, finalement j'ai eu le temps d'essayer ta proposition. J'ai donc remplacé le code:

                CalqueEnCours[intCompteur] = new PictureBoxSuper();
                CalqueEnCours[intCompteur].Name = "Calque" + intCompteur;
                CalqueEnCours[intCompteur].Image = new Bitmap(TabNomDeFichier[intCompteur]);
                CalqueEnCours[intCompteur].Opacity = 1f;
                CalqueEnCours[intCompteur].Location = new System.Drawing.Point(0, 0);
                CalqueEnCours[intCompteur].Size = new System.Drawing.Size(634, 1366);
                CalqueEnCours[intCompteur].MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);
                pictureBox1.Controls.Add(CalqueEnCours[intCompteur]);

par:

                CalqueEnCours[intCompteur] = new PictureBoxSuper();

                CalqueEnCours[intCompteur].Name = "Calque" + intCompteur;

                CalqueEnCours[intCompteur].Image = Image.FromFile(TabNomDeFichier[intCompteur]);

                CalqueEnCours[intCompteur].Opacity = 1f;

                CalqueEnCours[intCompteur].Location = new System.Drawing.Point(0, 0);

                CalqueEnCours[intCompteur].Size = new System.Drawing.Size(634, 1366);

                CalqueEnCours[intCompteur].MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);

                pictureBox1.Controls.Add(CalqueEnCours[intCompteur]);

mais bon j'ai toujours le même comportement (transparence devient pixel noir). Je vais donc tenté de tout envoyé directement dans une seule picturebox comme tu me l'as conseillé sans m'occuper de faire un buffer intermédiaire avant si j'ai bien compris ( puisque les picturebox utilisent par défaut un doublebuffer).
Bon merci encore... Je vous tiendrais au courant si ca marche quand j'aurrai essayé.
0
Rejoignez-nous