Change color of graphic rectangle dynamically

2019-09-12 20:36发布

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.

1条回答
来,给爷笑一个
2楼-- · 2019-09-12 21:20

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

enter image description here

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;

enter image description here

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.

查看更多
登录 后发表回答