Paolo
Paolo

Reputation: 11

C# draw in panel and shift left the drawn objects

I'm drawing rectangles in a panel starting from the left side.

When I reach the right side of the panel I'd like to shift left the rectangles previously drawn to have the space to draw another one, and so on.

Which is the simplest way to do it?

I'm drawing using System.Drawings.Graphics.

I'm using Winforms. The code is:

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.Runtime.InteropServices;

namespace FFViewer
{
    public partial class Form1 : Form
    {
        [DllImport("shlwapi.dll")]
        public static extern int ColorHLSToRGB(int H, int L, int S);

        int x=0;
        int y=300;
        int h = 0;


        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            panel_Paint();

            x = x + 40;
            h = h + 40;

            if (h >= 240)
                h = 0;
        }

        private void panel_Paint()
        {
            int val;
            Color color;

            val = ColorHLSToRGB(h, 128, 240);

            Graphics g = panel1.CreateGraphics();

            color = ColorTranslator.FromWin32(val);

            SolidBrush sb = new SolidBrush( color );

            g.FillRectangle(sb, x, y, 40, 100);

        }
    }

}

So, when I draw the latest rectangle on the right side, I'd like to shift left all the rectangles to leave the space to draw another one on the right side.

P.S. I don't have enough reputation to post images :(

Upvotes: 0

Views: 1173

Answers (2)

Idle_Mind
Idle_Mind

Reputation: 39132

Here's the "old school" way of doing it. This is basically what was done when a continuous graph of a real-time value needed to be displayed AND you didn't want to store the any of the values anywhere. This has severe limitations as it copies from the screen and the drawing will be erased the when window repaints itself. This first example is simply here to demonstrate the process, and is an extension of the way you created the initial blocks:

public partial class Form1 : Form
{

    public Form1()
    {
        InitializeComponent();
    }

    [DllImport("shlwapi.dll")]
    public static extern int ColorHLSToRGB(int H, int L, int S);

    int x = 0;
    int width = 40;
    int y = 300;
    int height = 100;
    int h = 0;

    private void button1_Click(object sender, EventArgs e)
    {
        if (x + width > panel1.ClientSize.Width) // if drawing the next block would exceed the panel width...
        {
            // capture what's currently on the screen
            Bitmap bmp = new Bitmap(x, height);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(panel1.PointToScreen(new Point(0, y)), new Point(0, 0), bmp.Size);
            }
            // draw it shifted to the left
            using (Graphics g = panel1.CreateGraphics())
            {
                g.DrawImage(bmp, new Point(-width, y));
            }
            // move x back so the new rectangle will draw where the last one was previously
            x = x - width;                
        }

        // draw the new block and increment values
        panel_Paint();
        x = x + width;
        h = h + width;
        if (h >= 240)
        { 
            h = 0;
        }
    }

    private void panel_Paint()
    {
        int val;
        Color color;
        val = ColorHLSToRGB(h, 128, 240);
        color = ColorTranslator.FromWin32(val);

        using (Graphics g = panel1.CreateGraphics())
        {
            using (SolidBrush sb = new SolidBrush(color))
            {
                g.FillRectangle(sb, x, y, width, height);
            }
        }
    }

}

This could be fixed by creating a Bitmap of the correct size and drawing to that instead. Then you shift everything and draw the new block on the right side. Finally, you'd draw that Bitmap in the Paint() event. So this is doing the same thing as above except we aren't copying from the screen, and the panel will properly redraw itself when requested:

public partial class Form1 : Form
{

    [DllImport("shlwapi.dll")]
    public static extern int ColorHLSToRGB(int H, int L, int S);

    int x = 0;
    int width = 40;
    int y = 300;
    int height = 100;
    int h = 0;

    Bitmap bmp;

    public Form1()
    {
        InitializeComponent();
        this.Load += Form1_Load;
        panel1.Paint += Panel1_Paint;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        int numBlocks = (int)(panel1.Width / width);
        bmp = new Bitmap(numBlocks * width, height);
        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.Clear(panel1.BackColor);
        }
    }

    private void Panel1_Paint(object sender, PaintEventArgs e)
    {
        if (bmp != null)
        {
            e.Graphics.DrawImage(bmp, new Point(0, y));
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        using (Graphics g = Graphics.FromImage(bmp))
        {
            if (x + width > bmp.Width) // if drawing the next block would exceed the bmp width...
            {                
                g.DrawImage(bmp, new Point(-width, 0)); // draw ourself shifted to the left   
                x = x - width;
            }

            // draw the new block 
            int val;
            Color color;
            val = ColorHLSToRGB(h, 128, 240);
            color = ColorTranslator.FromWin32(val);
            using (SolidBrush sb = new SolidBrush(color))
            {
                g.FillRectangle(sb, x, 0, width, height);
            }
        }

        x = x + width;
        h = h + width;
        if (h >= 240)
        { 
            h = 0;
        }

        panel1.Invalidate(); // force panel1 to redraw itself
    }

}

Upvotes: 1

C.Evenhuis
C.Evenhuis

Reputation: 26446

You should not use panel1.CreateGraphics(), but instead handle the Paint event of the panel, otherwise the rectangles might disappear, for instance after a popup appears in front of your form:

panel1.Paint += new PaintEventHandler(panel1_paint);

You'll need to paint all (visible) rectangles in the paint handler; you could keep a List<Rectangle> in your form to store the rectangles you have added:

private List<Rectangle> rectangles = new List<Rectangle>();
...
private void button1_Click(object sender, EventArgs e)
{
    rectangles.Add(new Rectangle(x, y, width, height));
    panel1.Invalidate(); // cause the paint event to be called
    // todo increment x/y
}

Then, in the panel1_Paint handler you can simply draw the rectangles, after having called Graphics.TranslateTransform() to shift the whole drawing area:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.TranslateTransform(-x, 0);
    foreach (Rectangle rectangle in rectangles)
    {
        // paint em
    }

    e.Graphics.ResetTransform();
}

Upvotes: 0

Related Questions