locked
DoubleBuffered panel creates trailing effect when moving pictureBoxes across it RRS feed

  • Pergunta

  • I am using a Double buffered panel using the code:

    public DoubleBufferPanel()
            {
    
                // Set the value of the double-buffering style bits to true.
                this.DoubleBuffered = true;
    
    
    
                this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
                 ControlStyles.AllPaintingInWmPaint, true);
    
                this.UpdateStyles();
    
            }

    I have 2 hidden pictureBoxes which have there background Images painted on the panel, and I have 1 Visible pictureBox which is moveable with the mouse.

    The code I use to move the pictureBox is:

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {            
        x = e.X;
        y = e.Y;
    }
    
    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            pictureBox1.Left += (e.X - x);
            pictureBox1.Top += (e.Y - y);
        }
    }

    But this creates a trailing effect of the pictureBox1's backgroundImage like so

    The odd thing is that if I were to get rid of the Paint event and set the double buffered panel's Backgroundimage to a picture, the trailing effect would stop.

    I've been trying to fix it all week and to no avail, I've tried Invalidate() in the mouse Down event but nothing... Anyone know how to Paint to a panel without causing this trailing effect? Thanks

    domingo, 26 de agosto de 2012 17:48

Todas as Respostas

  • Double buffering seems to help this code:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.IO;
    namespace WindowsFormsApplication1
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
          MyPanel pnl = new MyPanel(this);
        }
      }
    }
      internal class MyPanel : Panel
      {
        private string[] Pics = Directory.GetFiles("C:\\Users\\Public\\Pictures\\Sample Pictures");
        private Image[] Imgs = new Image[2];
        private MyPicturebox PB = new MyPicturebox();
        public MyPanel(Form Parent)
        {
          Imgs[0] = Image.FromFile(Pics[0]);
          Imgs[1] = Image.FromFile(Pics[1]);
          PB.SizeMode = PictureBoxSizeMode.AutoSize;
          PB.Image = Image.FromFile(Pics[2]);
          PB.Parent = this;
          this.ClientSize = new Size(4000, 4000);
          this.Parent = Parent;
          Parent.AutoScroll = true;
          this.DoubleBuffered = true;
        }
        protected override void OnPaint(PaintEventArgs e)
        {
          base.OnPaint(e);
          e.Graphics.DrawImage(Imgs[0], 0, 0);
          e.Graphics.DrawImage(Imgs[1], 0, Imgs[0].Height);
        }
      }
      internal class MyPicturebox : PictureBox
      {
        protected override void OnMouseDown(MouseEventArgs e)
        {
          base.OnMouseDown(e);
          if (e.Button == MouseButtons.Left)
          {
            this.Capture = false;
            Message Msg = Message.Create(this.Handle, 0XA1, new IntPtr(2), IntPtr.Zero);
            this.WndProc(ref Msg);
            this.Refresh();
          }
        }
      }

    domingo, 26 de agosto de 2012 21:35
  • thanks for the response John.

    Taking a look at the code you supplied with, I'm not sure it would help the application I am working on.

    I don't believe I can preload the images inside the doubleBuffered panel class, because each pictureBoxes background image uses Clipboard.GetImage() when ctrl + V are pressed(similar to MS Paint).

    If I were to use an internal DoubleBuffered panel class like the one you posted, rather than an external class 'DoubleBufferedPanel.cs', would it make any difference in reducing the trailing-effect of the pictureBoxes?

    domingo, 26 de agosto de 2012 23:00
  • thanks for the response John.

    Taking a look at the code you supplied with, I'm not sure it would help the application I am working on.

    I don't believe I can preload the images inside the doubleBuffered panel class, because each pictureBoxes background image uses Clipboard.GetImage() when ctrl + V are pressed(similar to MS Paint).

    If I were to use an internal DoubleBuffered panel class like the one you posted, rather than an external class 'DoubleBufferedPanel.cs', would it make any difference in reducing the trailing-effect of the pictureBoxes?


    Your panel can be any panel you choose.  The only unusual part of the code I posted is probably the picturebox.  Unless your drawing the images every time the picturebox moves.
    domingo, 26 de agosto de 2012 23:08
  • Your panel can be any panel you choose.  The only unusual part of the code I posted is probably the picturebox.  Unless your drawing the images every time the picturebox moves.

    John I tried using a DoubleBuffered panel with the same code I posted in my original post, but that couldn't reduce the flicker. What is different from the panel I posted, and the one which you posted?

    One thing I did find odd when I was trying to fix my application's panel1_paint event was that when using:

    e.Graphics.DrawImage(pictureBox1.BackgroundImage, new Rectangle(pictureBox1.Location, pictureBox1.Size));

    using new Rectangle(), is what may cause panel's paint event problem which I'm having. When I remove new Rectangle and use just this:

    e.Graphics.DrawImage(pictureBox1.BackgroundImage, pictureBox1.Location);
    everything gets Painted fast, and trailing is nonexistent.

    • Editado Nickslik domingo, 26 de agosto de 2012 23:31
    domingo, 26 de agosto de 2012 23:30
  • Why are you drawing picturebox images in a panel's paint event? The picturebox will draw it's own images.
    segunda-feira, 27 de agosto de 2012 00:01
  • Why are you drawing picturebox images in a panel's paint event? The picturebox will draw it's own images.

    Well the pictureBoxes I am not currently moving get drawn to panel1, & the pictureBoxes get Hidden. The pictureBox I am moving becomes Visible when I am moving it, but in the MouseUp event, it becomes Hidden again & drawn to panel1.

    It may sound weird but I need to do this for the application. I just do not know why this trailing is occuring. Is there a way to perform "e.Graphics.DrawImage()" without using "new Rectangle"?


    • Editado Nickslik segunda-feira, 27 de agosto de 2012 00:47
    segunda-feira, 27 de agosto de 2012 00:43
  • You need to rethink your application.  Pictureboxes display images.  Hiding a picturebox and then drawing it's image on it's parent isn't logical.  A good web site for info about Windows Forms graphics is www.bobpowell.net.  For help with your trailing image problem, post code that reproduces the problem.
    • Editado JohnWein segunda-feira, 27 de agosto de 2012 00:53
    segunda-feira, 27 de agosto de 2012 00:49
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Collections;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            int x;
            int y;
                            
            public Form1()
            {
                InitializeComponent();
                pictureBox1.Show();
                pictureBox2.Hide();
                pictureBox3.Hide();
            }
    
            private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {            
                x = e.X;
                y = e.Y;
                panel1.Invalidate();
            }
    
            private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    pictureBox1.Left += (e.X - x);
                    pictureBox1.Top += (e.Y - y);
                }
            }
    
            private void panel1_Paint(object sender, PaintEventArgs e)
            {           e.Graphics.DrawImage(pictureBox2.BackgroundImage, new Rectangle(pictureBox2.Location, pictureBox2.Size));
                         e.Graphics.DrawImage(pictureBox3.BackgroundImage, new Rectangle(pictureBox3.Location, pictureBox3.Size));
            }
    
    
        }
    }

    I made the code easier to understand.

    And heres the DoubleBuffered panel class

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public class DoubleBufferPanel : Panel
        {
    
    
            public DoubleBufferPanel()
            {
    
                // Set the value of the double-buffering style bits to true.
                this.DoubleBuffered = true;
    
    
    
                this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
                 ControlStyles.AllPaintingInWmPaint, true);
    
                this.UpdateStyles();
    
            }
    
    
        }
    
    }

    All you have to do now is create 3 pictureBoxes, and a doubleBuffered panel.

    The form is maximized, Panel1.size = (2000, 1200); and each pictureBox size = (700, 700) and set each pictureBoxes background Image to a random large detailed image. The trailing effect occurs when I move pictureBox1 and I can reproduce this every time.







    • Editado Nickslik segunda-feira, 27 de agosto de 2012 02:06
    segunda-feira, 27 de agosto de 2012 01:04
  • 
    
                // Set the value of the double-buffering style bits to true.
                this.DoubleBuffered = true;
    
    
    
                this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
                 ControlStyles.AllPaintingInWmPaint, true);
    

    Hi,

    setting this.DoubleBuffered to true is enough (it automatically sets the styles OptimizedDoubleBuffer and AllPaintingInWMPaint to true)

    http://www.codeproject.com/Articles/12870/Don-t-Flicker-Double-Buffer

    Regards,

      Thorsten


    segunda-feira, 27 de agosto de 2012 02:59
  • The code I posted should perform the same as the code you posted, but without the trailing images.
    segunda-feira, 27 de agosto de 2012 03:39
  • The code I posted should perform the same as the code you posted, but without the trailing images.

    John I tried using your code (without preloading the image) and still did not prevent trailing.
    segunda-feira, 27 de agosto de 2012 16:31
  • "without preloading the image"

    What does that mean?  Do you want to continuously draw 3 images on the panel while one of the images is moving?  Why do you want to do that?  Aren't 2 of the images always at the same location?

    Please explain what you are trying to do.
    • Editado JohnWein segunda-feira, 27 de agosto de 2012 17:14
    segunda-feira, 27 de agosto de 2012 17:14
  • In my bigger application, I made it so that when you press ctrl+V, whichever pictureBox pressed their BackgroundImage = Clipboard.GetImage().

    I cannot load the images inside the external Bufferedpanel Class because of how the images change, correct?

    Is this one example of the cons of C#, the panel paints slowly? Because as you saw from the code I've listed above, it's a very small application, so I don't understand why I'm getting a terrible trail while moving pictureBoxes.

    • Editado Nickslik segunda-feira, 27 de agosto de 2012 18:29
    segunda-feira, 27 de agosto de 2012 18:19
  • I don't know what you mean by "pictureBox pressed their BackgroughImage".  In the code you've posted so far there is only one PictureBox.  ( a hidden control doesn't count, it's just unnecessarily consuming resources).

    "don't understand why I'm getting a terrible trail while moving pictureBoxes."

    When you run the code I posted as I posted it, you should not get a trail.

    segunda-feira, 27 de agosto de 2012 19:08
  • John in the code I posted I removed the copy+paste method I created which sends the Clipboard image to either pictureBox2, or pictureBox3.

    When I tried running the code you posted it would not run. The code you presented me with is different from my application tho.

    private void panel1_Paint(object sender, PaintEventArgs e)
            {
                e.Graphics.DrawImage(pictureBox3.BackgroundImage, pictureBox3.Location);
                e.Graphics.DrawImage(pictureBox2.BackgroundImage, pictureBox2.Location);
            }

    I found out that when my app perform's the code above, & DOES NOT size the pictureBox using new Rectangle(pic1.Location, pic1.Size); then no trailing or flickering occurs.

    Is there a way to not use Rectangle structure but draw a resized image somehow? I have a feeling using the Rectangle structure causes the Paint event to keep repainting excessively.


    • Editado Nickslik segunda-feira, 27 de agosto de 2012 20:17
    segunda-feira, 27 de agosto de 2012 20:16
  • What error do you get when you run the code I posted?
    segunda-feira, 27 de agosto de 2012 20:29
  • What error do you get when you run the code I posted?

    Out of memory exception was unhandled at line:

    PB.Image = Image.FromFile(Pics[2]);

    But like I said in my last post, is there a way to not use new Rectangle() structure? Because that seems to cause my problem.


    • Editado Nickslik segunda-feira, 27 de agosto de 2012 22:18
    segunda-feira, 27 de agosto de 2012 22:17
  • What error do you get when you run the code I posted?

    Out of memory exception was unhandled at line:

    PB.Image = Image.FromFile(Pics[2]);

    But like I said in my last post, is there a way to not use new Rectangle() structure? Because that seems to cause my problem.


    Replace the GetFiles statement with the following:

    private string[] Pics = { "FilePathPic1", "FilePathPic2", "FilePathPic3" }; 

    Where the Pics are file paths to image files of your choice.


    • Editado JohnWein segunda-feira, 27 de agosto de 2012 22:38
    segunda-feira, 27 de agosto de 2012 22:23
  • Yes John your code does work now, but like I said it does not use Resize the images to fit inside a pictureBox using new Rect().

    Is there a way to resize the images without using that? Because I'm almost sure thats what is causing the slow repaint of the panel.
    segunda-feira, 27 de agosto de 2012 23:17
  • Yes John your code does work now, but like I said it does not use Resize the images to fit inside a pictureBox using new Rect().

    Is there a way to resize the images without using that? Because I'm almost sure thats what is causing the slow repaint of the panel.

    Why are you using images that aren't the correct size? How often do you change the size of the images? How do you know what size the images should be at any particular time?
    segunda-feira, 27 de agosto de 2012 23:49
  • Well similar to MS Paint, I get the images from clipboard(). Then I can resize the images whichever size I like by using something similar to this:

    http://www.codeproject.com/Articles/13184/Runtime-resizable-controls

    Is there a way to perhaps copy a pictureBox's resized image and store it in memory? That way I don't have to resize the image (using new Rect) upon every Redraw in the Paint event.



    • Editado Nickslik terça-feira, 28 de agosto de 2012 04:12
    terça-feira, 28 de agosto de 2012 04:07
  • This app does everything you have indicated that you want as I understand it:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.IO;
    namespace WindowsFormsApplication1
    {
      partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
          MyPanel pnl = new MyPanel();
          pnl.Parent = this;
          this.AutoScroll = true;
        }
      }
      class MyPanel : Panel
      {
        private MyPicturebox[] Pics = new MyPicturebox[3];
        public MyPanel()
        {
          string PicsPath = "C:\\Users\\Public\\Pictures\\Sample Pictures";
          string[] PicFiles = { "Tulips.jpg", "Koala.jpg", "Desert.jpg" };
          for (int I = 0; I < Pics.Length; I++)
          {
            Clipboard.SetImage(Image.FromFile(Path.Combine(PicsPath, PicFiles[I])));
            Pics[I] = new MyPicturebox();
            Pics[I].ClientSize = new Size(256, 192);
            Pics[I].Image = Clipboard.GetImage();
            Pics[I].Top = I * Pics[0].Height;
            Pics[I].Parent = this;
          }
          this.ClientSize = new Size(4000, 4000);
        }
      }
      class MyPicturebox : PictureBox
      {
        public MyPicturebox()
        {
          this.SizeMode = PictureBoxSizeMode.StretchImage;
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
          base.OnMouseDown(e);
          if (!(e.Button == MouseButtons.Left))
          {
            return;
          }
          if (GetSizingRect().Contains(e.Location))
          {
            return;
          }
          this.Capture = false;
          Message Msg = Message.Create(this.Handle, 0XA1, new IntPtr(2), IntPtr.Zero);
          this.WndProc(ref Msg);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
          base.OnMouseMove(e);
          if (GetSizingRect().Contains(e.Location))
          {
            Cursor = Cursors.SizeNWSE;
          }
          else
          {
            Cursor = Cursors.Default;
          }
          if (e.Button == MouseButtons.Left)
          {
            this.ClientSize = new Size(e.Location);
          }
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
          base.OnPaint(pe);
          ControlPaint.DrawSizeGrip(pe.Graphics, Color.Transparent, GetSizingRect());
        }
        public Rectangle GetSizingRect()
        {
          return new Rectangle(this.Width - 20, this.Height - 20, 20, 20);
        }
      }
    }
    Please explain what this doesn't do that you want.
    terça-feira, 28 de agosto de 2012 09:07
  • Hi Nick,

    How is it going with John's suggestion?

    Is this what you want?

    Any update to this issue?

    Best Regards,


    Bob Wu [MSFT]
    MSDN Community Support | Feedback to us

    quinta-feira, 30 de agosto de 2012 09:58
  • Sorry about the wait. I just got internet back from my ISP as I was having some connection difficulties. I will try out the code you provided with me tonight and write my response in the morning. Thanks for the help so far

    John after using the code you've written, I noticed that there is problems with the repainting of the panel. Like  in the code I provided, it causes trailing of images, and while resizing the images, theres flicker

    I need to make this doublebuffered panel paint quicker, but I have no idea why it's painting so slow, for such a small application.


    • Editado Nickslik sexta-feira, 31 de agosto de 2012 17:06
    sexta-feira, 31 de agosto de 2012 01:58
  • using stopwatch reduce trailing effect.

    #region [ - Handling form movement - ]
         
         private Point _mousDownLocation;
         private bool _isMouseDown;
         private Stopwatch stopwatch;
         private void SemiForm_MouseDown(object senderMouseEventArgs e)
         {
             if (e.Button == MouseButtons.Left)
             {
                 _isMouseDown = true;
                 _mousDownLocation = e.Location;
                 stopwatch.Start();
             }
         }
         private void SemiForm_MouseUp(object senderMouseEventArgs e)
         {
             if (e.Button == MouseButtons.Left)
             {
                 _isMouseDown = false;
                 stopwatch.Stop();
             }
         }
         private void SemiForm_MouseMove(object senderMouseEventArgs e)
         {
             if (_isMouseDown && Movable && stopwatch.ElapsedMilliseconds > 10)
             {
                 this.Left = e.+ this.Left - _mousDownLocation.X;
                 this.Top = e.+ this.Top - _mousDownLocation.Y;
                 stopwatch.Restart();
             }
         }
     
         #endregion [ - Handling form movement - ]

    • Sugerido como Resposta Amin29a domingo, 23 de agosto de 2020 16:02
    domingo, 23 de agosto de 2020 16:02