C# Draw Line OnPaint() vs CreateGraphics()

2019-01-29 02:27发布

问题:

Question: How do you properly draw on a winform from a method other than the OnPaint() method?

Additional Information: The code I have now draws some background lines for a TicTacToe game in the OnPaint() method. Then I use the Mouse_Click event and am running this code which apparently is not proper:

private void TicTacToe_MouseClick(object sender, MouseEventArgs e)
   Graphics g = this.CreateGraphics();
   g.DrawEllipse(this.penRed, this.Rectangle);

For reasons I do not understand, it does draw the circle, but when minimizing or moving the form off screen it erases the circles but not the lines from the OnPaint() method.

回答1:

What you are doing is drawing on the form "asynchronously" (from the OnPaint method). You see, the OnPaint method is what Windows Forms relies on to draw your entire form. When something happens to your From, it is invalidated and OnPaint is called again. If something isn't drawn in that method, then it will not be there after that happens.

If you want a button to trigger something to appear permanently then what you need to do is Add that object to a collection somewhere, or set a variable related to it. Then call Refresh() which calls Invalidate() and Update() Then, during OnPaint, draw that object (ellipis).

If you want it to still be there after something happens to your form, such as minimize, you have to draw it during OnPaint.

Here's my suggestion:

public partial class Form1 : Form
{
    Rectangle r = Rectangle.Empty;
    Pen redPen = new Pen(Color.Red);

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        r = new Rectangle(50, 50, 100, 100);
        Refresh();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        if (r != Rectangle.Empty)
        {
            e.Graphics.DrawRectangle(redPen, r);
        }
    }
}


回答2:

You are doing a lot of "view" but no "model".

When you want to create a shape, when the mouse button goes down/up, create some DATA representing the shape.

Your data structures represent the persistent information (it is the data that allows you to save and load this information between sessions).

All your paint function needs to do is look at the DATA structures and paint it. This will therefore persist between sizing/hiding/showing.



回答3:

The problem is that Windows windows (that includes WinForms) have no graphical memory of their own unless their creator provides such memory and it is only a matter of time before that particular window wilk get overwritten or hidden and eventually need to be repainted. You're painting to the screen ( you might say ) and others can do the same. The only convention you can rely on is that the OnPaint will get called when needed. Basically it's alright to use your philosophy and draw whenever you need to (not on some misterious and unpredictable schedule). For that check out my solution.

You should use a "backbuffer bitmap" like:

private Bitmap bb;

protected override void OnResize(EventArgs e) {
  this.bb = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
  this.InitBackBuffer();
}
private void InitBackBuffer() {
  using (var g = Graphics.FromImage(this.bb)) {
    // do any of the "non dissapearing line" drawing here
  }
}

private void TicTacToe_MouseClick(object sender, MouseEventArgs e)
  using (Graphics g = Graphics.FromImage(this.bb))
    g.DrawEllipse(this.penRed, this.Rectangle);
  this.Invalidate();
}

protected override void OnPaint(PaintEventArgs e) {
  base.OnPaint(e);
  e.Graphics.DrawImageUnscaled(this.bb);
}

Try that. That should do it :)



标签: c# drawing line