I have write a c# class, which contains graphics inside.
this is how I build the class and draw the rectangle. It works perfectly:
public Graphics shape;
public Rectangle rc;
// constructor
public CLASS_NAME(Graphics formGraphics)
{
this.shape = formGraphics;
}
public void draw(int x, int y, int w, int h)
{
SolidBrush myBrush = new SolidBrush(Color.Red);
this.rc = new Rectangle(x, y, w, h);
this.shape.FillRectangle(myBrush, rc);
myBrush.Dispose();
}
then I wanted to add a new method to the object in order to change the color, but when I call this nothing happens:
public void change_color()
{
SolidBrush myBrush = new SolidBrush(Color.Yellow);
this.shapeFillRectangle(myBrush, rc);
myBrush.Dispose();
}
I also tried: rc.Fill =
but VS doesn't recognize rc.Fill
as a valid method.
- THE ERROR I GET IS: it says that in the
change()
method, this line: this.shapeFillRectangle(myBrush, rc);
has a parameter that isn't valid.
OK, let's start with a 'drawRectangle' class. It has enough data to create a simple Pen
, holds a Rectangle
and also a reference to the Control
it will draw on.
I have added a ToString
override, so we can display it with all its properties in, say a ListBox
..
version 1
public class DrawRectangle
{
public Color color { get; set; }
public float width { get; set; }
public Rectangle rect { get; set; }
public Control surface { get; set; }
public DrawRectangle(Rectangle r, Color c, float w, Control ct)
{
color = c;
width = w;
rect = r;
surface = ct;
}
public override string ToString()
{
return rect.ToString() + " (" + color.ToString() +
" - " + width.ToString("0.00") + ") on " + surface.Name;
}
}
Next we need a list of those rectangles:
public List<DrawRectangle> rectangles = new List<DrawRectangle>();
Now let's add them in a loop in a button click:
private void buttonAddLoop_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
rectangles.Add(new DrawRectangle(new Rectangle(i * 30, i * 30, 22, 22),
Color.Black, 3.5f, drawPanel1));
drawPanel1.Invalidate();
}
Note how I invalidate the control I want them painted on by Invalidating
it! (You can use the Form
as well, as Form
inherits from Control
..)
For this to work we need to code the Paint
event of each control that needs to paint some of the rectangles; I use only a Panel drawPanel1
:
private void drawPanel1_Paint(object sender, PaintEventArgs e)
{
foreach (DrawRectangle dr in rectangles)
{
if (dr.surface == sender)
{
using (Pen pen = new Pen(dr.color, dr.width))
e.Graphics.DrawRectangle(pen, dr.rect);
}
}
}
Now we can change any of our DrawRectangles
, maybe in another button click:
private void buttonChangeButton_Click(object sender, EventArgs e)
{
rectangles[3].color = Color.Red;
rectangles[6].width = 7f;
drawPanel1.Invalidate();
}
Update:
The above class was a simple start to show how to encapsulate the things a 'Rectangle' class would need; it was not meant to be perfect!
Here is one flaw it has: It doesn't really care enough about the best way to spread responsibilities. It put the burdon of drawing the rectangles on the controls and if you have more complex drawing code and more controls each of them would have to learn the more complex code. This is not good. Instead the responsibility should stay with the Rectangle class. The control should only tell them to draw themselves..
Here is an updated class that will do just that. As a more complex drawing it will be able to draw filled rectangles as well..:
version 2
public class DrawRectangle
{
public Color color { get; set; }
public float width { get; set; }
public Color fillColor { get; set; }
public Rectangle rect { get; set; }
public Control surface { get; set; }
public DrawRectangle(Rectangle r, Color c, float w, Color fill, Control ct )
{
color = c;
width = w;
fillColor = fill;
rect = r;
surface = ct;
}
public DrawRectangle(Rectangle r, Color c, float w, Control ct)
: this. DrawRectangle(r, c, w, Color.Empty, ct) {}
public override string ToString()
{
return rect.ToString() + " (" + color.ToString() +
" - " + width.ToString("0.00") + ")";
}
public void Draw(Graphics g)
{
if (fillColor != Color.Empty)
using (SolidBrush brush = new SolidBrush(fillColor))
g.FillRectangle(brush, rect);
if (color != Color.Empty)
using (Pen pen = new Pen(color, width)) g.DrawRectangle(pen, rect);
}
}
It uses a second color to determine the filling. (I didn't add the fill color the the ToString
method.) It compares the color with the special color values Color.Empty
to determine what should and shouldn't be drawn.
The loop to create the new rectangles may now include the fill color. If it doesn't, the old constructor will be called, which now sets the fill color to Color.Empty
.
Here is how simple the Paint
event gets:
private void drawPanel1_Paint(object sender, PaintEventArgs e)
{
foreach (DrawRectangle dr in rectangles)
if (dr.surface == sender) dr.Draw(e.Graphics);
}
To fill some rectangle we can now write:
rectangles[2].fillColor = Color.Fuchsia;
Aside:
A note an color comparison: It is not obvious, but while the color Color.Empty
really is just 'transparent black' (0,0,0,0), color comparison is special: NamedColors
as well as KnownColors
, including Color.Empty
always compare false to normal colors. To make a true color comparison one would have to cast to a 'normal' Color
:
bool ok=Color.FromArgb(255,255,255,255) == Color.White; // false
bool ok=Color.FromArgb(255,255,255 255) == Color.FromArgb(Color.White.ToArgb()); // true
Therefore the comparison in the Draw
code is safe.