Nurdism
Nurdism

Reputation: 608

easier way to create grid than creating 900 controls c#.net

I'm trying to write a program that creates a 30x30 grid of 20px x 20px boxes. When you click on a box in the grid it changes the color of the box and stores the RGB value of it and the x,y coordinates (hidden in the box). So basically I'm making a simple paint program. The purpose of it is to assist me in programming LED RGB animations for a 30x30 pixel grid. So once I draw something I export the X,Y,R,G,B of each pixel in the image.

So my question is, is there an easy way to do this. With out creating 900 buttons and putting them together? I've got something sorta working :

Panel BU = new Panel();

BU.AutoSize = false;
BU.Location = new System.Drawing.Point(xpos, ypos);
BU.BackColor = System.Drawing.Color.Transparent;
BU.Font = new System.Drawing.Font("Microsoft Sans Serif", 5, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
BU.Name = row_num + " x " + col_num;
BU.Size = new System.Drawing.Size(20, 20);
BU.MouseDown +=new MouseEventHandler(BU_MouseDown);
BU.MouseEnter +=new EventHandler(BU_MouseEnter);

this.Controls.Add(BU);

xpos = xpos + 20;

px_num++;
col_num++;

if (col_num == 30) 
{ 
   col_num = 0; 
   ypos = ypos + 20;
   row_num++; 
   xpos = 0;
};

But it takes WAAAAAY to long to load.

Upvotes: 1

Views: 1292

Answers (4)

LarsTech
LarsTech

Reputation: 81655

Yes.

Create one panel, and handle the grid painting and mouse events inside that.

Super simple example, optimized for nothing and flicker happy:

private Color[,] _Colors = new Color[30, 30];

private void panel1_Paint(object sender, PaintEventArgs e) {
  int left = 0;
  int top = 0;

  for (int y = 0; y < 30; y++) {
    left = 0;
    for (int x = 0; x < 30; x++) {
      Rectangle r = new Rectangle(left, top, 20, 20);

      using (SolidBrush sb = new SolidBrush(_Colors[x, y]))
        e.Graphics.FillRectangle(sb, r);
      ControlPaint.DrawBorder3D(e.Graphics, r, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom);

      left += 20;
    }
    top += 20;
  }
}

private void panel1_MouseDown(object sender, MouseEventArgs e) {
  if (e.Button == MouseButtons.Left) {
    int left = 0;
    int top = 0;

    for (int y = 0; y < 30; y++) {
      left = 0;
      for (int x = 0; x < 30; x++) {
        Rectangle r = new Rectangle(left, top, 20, 20);

        if (r.Contains(e.Location)) {
          _Colors[x, y] = Color.Red;
          panel1.Invalidate();
        }
        left += 20;
      }
      top += 20;
    }
  }
}

Upvotes: 4

Bradley Uffner
Bradley Uffner

Reputation: 16991

I typically use a Bitmap object for this and use one of the overrides in Graphics.DrawImage to draw it zoomed in. Then I draw grid lines over top of the zoomed image.

I'm digging up some source code for you now.

Edit Sorry, no source code handy at the moment. But basically what you want to do is create a new system.drawing.bitmap that is 30x30 pixels. In the paint even of teh control you want to draw it in, use the passed in Graphics Object (usually e.graphics) and call .DrawImage. Use one of the overloads that allows you to specify the output size so that you can scale it out by whatever zoom factor you want. You will also need to set the .PixelOffsetMode on the graphics object to .none, or else things will be offset by 1/2 your zoom. Set .InterpolationMode to .NearestNeighbor so that the output isn't blurry. This should give you a perfectly aligned "pixelated" zoomed image. Then just loop and draw horizontal and vertical grid lines.

To handle the mouse clicks just add a handler to the controls mouse down event, and divide the input position by your zoom factor to get the real x and y coords. then update that pixel of the source image and call .invalidate on the control you are drawing to. That will cause it to repaint with the updated image.

Upvotes: 1

Tigran
Tigran

Reputation: 62276

Well, instead of creating 900 controls, no difference if it's made in WindowsForms, or WPF with HD accelaration, it will be slow. What can do, if you really need always have all controls visible on the screen, so potentially at some points also have 900 contemporary, is just draw rectangles. You can do an emulation of the button.

Draw Rectangle, where Left and Top lines are darker then Right and Bottom, will give 3D filling to user, make it inverse colors of lines, and it will give to user a filling of pushed button. For sure you need to handle all mouse interactions, like MouseMove, MouseDown, MouseUp on your panel (cause the panel will be the only control present at this point), and figure out on what rectangle event happens and draw that rectangle accordingly.

Upvotes: 1

Iain Ballard
Iain Ballard

Reputation: 4828

A custom user control that renders 20x20 boxes would do the trick. To get the location of a mouse click, take the x and y values, divide by 20.

Something very similar here: https://github.com/i-e-b/DBSS/blob/master/DBSS/BigGrid/SheetView.cs

Upvotes: 1

Related Questions