4444
4444

Reputation: 3680

2D Array of RectangleShapes

I am developing a very rudimentary drawing program: A 2D grid comprised of multiple RectangleShapes, around 20x30 pixels each, which when clicked change color based on user RGB input, which works just fine:

Color SelectedColor = new Color();
private void Pixel_1_1_Click(object sender, EventArgs e)    // on Rectangle click
{
    Pixel_1_1.FillColor = SelectedColor;    // change to currently desired color.
}

Since the number of squares is rising dramatically, I'm looking for a way to arrange the "pixel" rectangles into a 2D array. (I really don't want to have to make a Pixel_Click method for every single Rectangle on the screen!) Hoping eventually to be able to call something like:

private void Pixel_[x]_[y]_Click(object sender, EventArgs e)
{
    Pixel_[x]_[y].FillColor = SelectedColor;
}

My friends suggest the use of an anonymous delegate, but I don't understand how to fully use one to solve my problem.

What would be the best way to generate a 2D array of rectangles in a C# Windows Form? And once generated, how can I access them with a single method for variant values of x and y?

Upvotes: 2

Views: 2538

Answers (3)

Ani
Ani

Reputation: 10896

I believe you're going about this the wrong way. What you want to do is to draw directly into a bitmap. Here is some code that uses a PictureBox to allow the user to draw into it.

    using System;
    using System.Drawing;
    using System.Windows.Forms;

    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            private Pen _pen;

            private bool _mouseDown;
            private int _startX;
            private int _startY;

            public Form1()
            {
                InitializeComponent();
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
                _pen = new Pen(Color.Black);
            }

            private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {
                _mouseDown = true;

                _startX = e.X;
                _startY = e.Y;
            }

            private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
            {
                _mouseDown = false;
            }

            private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
            {
                if (_mouseDown)
                {
                    using (var graphics = Graphics.FromImage(pictureBox1.Image))
                    {
                        graphics.DrawLine(_pen, _startX, _startY, e.X, e.Y);
                        _startX = e.X;
                        _startY = e.Y;
                    }
                    pictureBox1.Invalidate();
                }
            }

        }
    }

This is the normal method to write a painting app and is quite performant as well. You can also easily save, write new tools or manipulate images better in this way.

Upvotes: 0

LarsTech
LarsTech

Reputation: 81655

While you are probably correct in thinking of each rectangle as an object, it probably isn't correct to think of each rectangle as a windows control, especially since you have so many of them.

So try creating your own rectangle object:

public class MyRect {
  public Color FillColor { get; set; }
  public Rectangle Rectangle { get; set; }

  public MyRect(Rectangle r, Color c) {
    this.Rectangle = r;
    this.FillColor = c;
  }
}

Now you just need to keep a list of your objects and paint on a single Panel control (or PictureBox) all of your rectangles:

private List<MyRect> myRectangles = new List<MyRect>();

public Form1() {
  InitializeComponent();

  myRectangles.Add(new MyRect(new Rectangle(10, 10, 64, 16), Color.Blue));
  myRectangles.Add(new MyRect(new Rectangle(20, 48, 16, 64), Color.Red));
}

private void panel1_Paint(object sender, PaintEventArgs e) {
  foreach (MyRect mr in myRectangles) {
    using (SolidBrush sb = new SolidBrush(mr.FillColor)) {
      e.Graphics.FillRectangle(sb, mr.Rectangle);
    }
  }
}

To handle the "click" event of the rectangles, you just handle the MouseDown or MouseClick event of your container control and determine yourself which rectangle is being clicked on:

void panel1_MouseDown(object sender, MouseEventArgs e) {
  if (e.Button == MouseButtons.Left) {
    foreach (MyRect mr in myRectangles) {
      if (mr.Rectangle.Contains(e.Location)) {
        ChangeColor(mr, Color.Green);
      }
    }
    panel1.Invalidate();
  }
}

private void ChangeColor(MyRect mr, Color newColor) {
  mr.FillColor = newColor;
}

Upvotes: 1

James
James

Reputation: 9985

If you want to maintain the rectangles as components on screen then you can assign all of them the same click event, the click event will have a little dropdown to pick an existing event. To know which recantangle was clicked use the sender parameter ((Pixel)sender).FillColor = SelectedColor;

For ease I would recommend using something like a panel and drawing rectangles on it, That means you only have a single click event to deal with. So now your question becomes "How do I draw a grid of rectangles on a panel" and "How do I know which rectangle was clicked"

So for the first part you could use this not the very efficient way.

Create a class which stores the information about your pixels

class MyPixel
{
    public Color PixelColour;
    public Rectangle Bounds;
}

Keep a list of them in memory

List<MyPixels> MyGrid = new List<MyPixels>();

then in the onpaint event of the panel Draw the pixels on the panel

foreach(MyPixel Pixel in MyGrid)
{
    using(Brush B = new SolidBrush(Pixel.PixelColor))
    {
        e.Graphics.DrawRectangle(B, Pixel.Bounds);
    }
}

Now in the click event you'll need to know which pixel was clicked

foreach(MyPixel Pixel in MyGrid)
{
    if (Pixel.Bounds.Contains(e.Location))
    {
        PixelClicked(Pixel);
    }
}

Upvotes: 1

Related Questions