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

  • Question

  • 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

    Sunday, August 26, 2012 5:48 PM

All replies

  • 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();
          }
        }
      }

    Sunday, August 26, 2012 9:35 PM
  • 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?

    Sunday, August 26, 2012 11:00 PM
  • 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.
    Sunday, August 26, 2012 11:08 PM
  • 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.

    • Edited by Nickslik Sunday, August 26, 2012 11:31 PM
    Sunday, August 26, 2012 11:30 PM
  • Why are you drawing picturebox images in a panel's paint event? The picturebox will draw it's own images.
    Monday, August 27, 2012 12:01 AM
  • 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"?


    • Edited by Nickslik Monday, August 27, 2012 12:47 AM
    Monday, August 27, 2012 12:43 AM
  • 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.
    • Edited by JohnWein Monday, August 27, 2012 12:53 AM
    Monday, August 27, 2012 12:49 AM
  • 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.







    • Edited by Nickslik Monday, August 27, 2012 2:06 AM
    Monday, August 27, 2012 1:04 AM
  • 
    
                // 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


    Monday, August 27, 2012 2:59 AM
  • The code I posted should perform the same as the code you posted, but without the trailing images.
    Monday, August 27, 2012 3:39 AM
  • 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.
    Monday, August 27, 2012 4:31 PM
  • "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.
    • Edited by JohnWein Monday, August 27, 2012 5:14 PM
    Monday, August 27, 2012 5:14 PM
  • 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.

    • Edited by Nickslik Monday, August 27, 2012 6:29 PM
    Monday, August 27, 2012 6:19 PM
  • 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.

    Monday, August 27, 2012 7:08 PM
  • 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.


    • Edited by Nickslik Monday, August 27, 2012 8:17 PM
    Monday, August 27, 2012 8:16 PM
  • What error do you get when you run the code I posted?
    Monday, August 27, 2012 8:29 PM
  • 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.


    • Edited by Nickslik Monday, August 27, 2012 10:18 PM
    Monday, August 27, 2012 10:17 PM
  • 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.


    • Edited by JohnWein Monday, August 27, 2012 10:38 PM
    Monday, August 27, 2012 10:23 PM
  • 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.
    Monday, August 27, 2012 11:17 PM
  • 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?
    Monday, August 27, 2012 11:49 PM
  • 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.



    • Edited by Nickslik Tuesday, August 28, 2012 4:12 AM
    Tuesday, August 28, 2012 4:07 AM
  • 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.
    Tuesday, August 28, 2012 9:07 AM
  • 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

    Thursday, August 30, 2012 9:58 AM
  • 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.


    • Edited by Nickslik Friday, August 31, 2012 5:06 PM
    Friday, August 31, 2012 1:58 AM
  • 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 - ]

    • Proposed as answer by Amin29a Sunday, August 23, 2020 4:02 PM
    Sunday, August 23, 2020 4:02 PM