Invisible controls drawing over graphic object - c#

I have a method where I disable or enable some custom controls and then use a graphics object to draw lines and rectangles.
The gist of the method:
void MyMethod()
{
//...
mycontrol.enabled = false;
mycontrol.visible = false;
mycontrol.Invalidate();
mycontrol.Update();
GraphicsObject.DrawLines();
//...
}
Right after this method returns, the screen looks great. I have rectangles and lines where controls used to be.
However, after the click event handler returns (which called the above method). The controls which should be invisible draw over the lines and rectangles (leaving those area's blank - the same color as the background form).
Is there any way to fix this?
Thanks

As I mentioned in my comment if you are drawing on an object if you do not use the OnPaint Method or the Paint Event your custom drawing will not be automatically redrawn. Depending on what you are drawing on you can do something like( I am assuming you are drawing on a Form).
void MyMethod()
{
//...
mycontrol.enabled = false;
mycontrol.visible = false;
mycontrol.Invalidate();
mycontrol.Update();
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
//Conditional Logic to determine what you are drawing
// myPoints is a Point array that you fill elsewhere in your program
e.Graphics.DrawLines(new Pen(Brushes.Red), myPoints);
}

Related

How to call Paint event on MouseHover?

I have a panel's Paint event which draws a rectangle around the panel to highlight it.
Paint event-
private void deal_details_panel_Paint(object sender, PaintEventArgs e)
{
int thickness = 2;//it's up to you
int halfThickness = thickness / 2;
using (Pen p = new Pen(Color.GreenYellow, thickness))
{
e.Graphics.DrawRectangle(p, new Rectangle(halfThickness,
halfThickness,
deal_details_panel.ClientSize.Width - thickness,
deal_details_panel.ClientSize.Height - thickness));
}
}
Initially i dont want this rectangle to be shown, I have a button on which if i hover, only then i want to show this rectangle and on MouseLeave event i want to make it invisible again.
Button_Hover-
private void add_MouseHover(object sender, EventArgs e)
{
deal_details_panel.Invalidate();
}
I saw some answers which were to use Invalidate(), but, Invalidate() is not working for me.
Use Refresh() instead of Invalidate().
The Invalidate() method does not force repaint, it simply marks the control as "need repainting", leaving it up to the OS to repaint when needed.
The Refresh() method will force repainting immediately.
For more information, read Whats the difference between Control.Invalidate, Control.Update and Control.Refresh? blog post, where it clearly states:
Control.Invalidate( ) / Control.Invalidate(bool) / Control.Invalidate(Rectangle) / Control.Invalidate(Rectangle, bool) / Control.Invalidate(Region) / Control.Invalidate(Region, bool)
....
The important thing to note here is that these functions only “invalidate” or “dirty” the client area by adding it to the current update region of the window of the control. This invalidated region, along with all other areas in the update region, is marked for painting when the next WM_PAINT message is received. As a result you may not see your control refreshing (and showing the invalidation) immediately (or synchronously).
Update
In order for the panel to show the YellowGreen border only when the mouse is hovering over the button, you need to change your code a little bit.
First, add a bool field to your form, to indicate whether to draw the border or not, initialized to false:
private bool _drawBorder = false;
Then, change the paint method to take that value into consideration:
private void deal_details_panel_Paint(object sender, PaintEventArgs e)
{
if(_drawBorder)
{
int thickness = 2;//it's up to you
int halfThickness = thickness / 2;
using (Pen p = new Pen(Color.GreenYellow, thickness))
{
e.Graphics.DrawRectangle(p, new Rectangle(halfThickness,
halfThickness,
deal_details_panel.ClientSize.Width - thickness,
deal_details_panel.ClientSize.Height - thickness));
}
}
}
Finally, don't use the mouse_Hover event on the button, use MouseEnter and MouseLeave instead.
This is for two reasons - 1: Mouse_Hover have a built in delay and 2: using Mouse_Hover you don't get an indication when the mouse stops hovering over the button.
In the MouseEnter and MouseLeave event handlers, all you need to do is update the value of _drawBorder and call deal_details_panel.Refresh():
private void add_MouseEnter(object sender, EventArgs e)
{
_drawBorder = true;
deal_details_panel.Refresh();
}
private void add_MouseLeave(object sender, EventArgs e)
{
_drawBorder = false;
deal_details_panel.Refresh();
}
Zohar pointed correct solution but I would add a suggestion to change:
using (Pen p = new Pen(Color.GreenYellow, thickness))
to:
using (Pen p = new Pen(Color.Transparent, thickness))
This will set correct state from the start so no need to repaint.

redrawing code in C#

i am creating a drawing programe which Suppose to take the parameters from the user
(Radius,height,width,....etc)
i have created a class with one paramter (radius)
public int faceoutline(int r)
{
Graphic = pictureBox1.CreateGraphics();
Graphic.DrawEllipse(myPen, 0, 0, r, r);
return r;
}
then i call it :
private void button1_Click(object sender, EventArgs e)
{
faceoutline(int.Parse(textBox1.Text));
pictureBox1.Invalidate();
}
....and nothing happens
i copied the button code to a timer but it keep balnking
WHAT I AM DOING WRONG ?!
You should place the drawing code in the Paint event handler of the PictureBox, and use the Graphics provided in the event args. This way, your custom drawing code will be executed every time the control is redrawn.
If you don't do it this way, anything you draw will disappear the next time the control is redrawn. In your code, you call Invalidate right after you draw your ellipse, so the control is redrawn without the ellipse...

C# Draw Line OnPaint() vs CreateGraphics()

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.
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.
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 :)
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);
}
}
}

C# - Code in form constructor not executed

Relatively new to C#; hopefully I'm just overlooking something simple.
I have a form named 'Exercise1' which contains a picture box called 'drawingArea' and a few buttons. The code for the constructor of Exercise1 is as follows:
public Exercise1()
{
InitializeComponent();
paper = drawingArea.CreateGraphics();
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2,
drawingArea.Height / 2, 30);
paper.Clear(Color.White);
balloon.Display(paper);
}
...
'paper' and 'balloon' are created as globals above the constructor for use in the other methods on the form. Both 'paper' and 'balloon' work as initialized in the constructor in the other methods defined on the form.
For whatever reason, the commands
paper.Clear(Color.White);
and
balloon.Display(paper);
Which should clear the picture box and show a red ellipse, don't execute (at least visibly). What gives?
UPDATE:
Think I'm going to like this website... You guys are quick!
#Nitesh: The constructor for Exercise1 is called from another form. Code is as follows:
private void button1_Click(object sender, EventArgs e)
{
int exSelector = (int)numericUpDown1.Value;
switch (exSelector)
{
case 1:
Exercise1 form1 = new Exercise1();
form1.Show();
break;
...
#Sean Dunford: Yes and yes it is.
#RBarryYoung: Was playing around with that a bit, but had no luck. What command triggers a Form_Load event for Exercise1?
UPDATE: This altered code works as expected:
public Exercise1()
{
InitializeComponent();
paper = drawingArea.CreateGraphics();
drawingArea.BackColor = Color.White;
drawingArea.Paint += new PaintEventHandler(this.drawingArea_Paint);
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2, drawingArea.Height / 2, 30);
}
private void drawingArea_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
balloon.Display(e.Graphics);
}
...
Thanks for all the help!
You cannot do drawing in the constructor. To do proper drawing, you need to have the form shown on the screen. You can try using the Shown event to do your rendering (this may get lost when the form is redrawn, though).
Usually the best way is to set whatever flags you need in the constructor and then use the Paint event of the form to do all painting. Later on, when you need to repaint something, set up whatever state needs to be rendered, invalidate your form (this results in a Paint event) and then you can repaint the new state.
If you try to do customized drawing (outside your Paint event) you'll run the risk of things randomly going blank or your drawing may disapper when you resize/minimize your form.
You use Graphics in a constructor, that means that you draw on the paper only once, any redraw for whatever reason that happens after constructor will draw the drawingArea in its original way. Try to add PaintEventHandler to drawingArea and then call inside balloon.Display(e.Graphics);
public Exercise1()
{
InitializeComponent();
balloon = new Balloon("redBalloon", Color.Red, drawingArea.Width / 2,
drawingArea.Height / 2, 30);
drawingArea.Paint += new PaintEventHandler(drawingArea_Paint);
}
void drawingArea_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
baloon.Display(e.Graphics);
}
You should be overriding the forms OnPaint event handler. In doing so, you are able to get the graphics context which will redraw your paper and balloon areas.

Draw an image out of a control

I have a control. It's a text box. I want to draw an image at the left of the control. The image should be painted outside the control. I could paint it one but inside.
Here is the code:
private static Image requiredIcon = Resources.Icon_required;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (base.Enabled && string.IsNullOrEmpty(base.Text))
{
e.Graphics.DrawImage(requiredIcon, 0, 0);
}
}
Instead of drawing the image, you could create an appropriate control, e.g. PictureBox, with it's Image property set to the appropriate image resource.
It may be easier to do this at design time rather than run time. If the image should not be shown intitially, set it's Visible property to false at design time and set it to true at run time when the image should be shown.
Create a user control composed of a TextBox and a PictureBox. Then in the OnPaint event of your user control you will be able to draw outside the textbox (in the PictureBox control).
Here's a picture of what the UserControl could look like. It contains a PictureBox and a TextBox.
If you just want to draw an icon you just have to assigned it to the PictureBox. So the OnPaint may not be necessary in your case.
Instead of overloading the TextBox.OnPaint method, you have to overload the Form's (or whatever is the parent component of the TextBox) OnPaint method, and paint the Image at the left hand side of the TextBox. You can't have the TextBox draw to an area outside its own bounds.
I can get a parent and use it for painting.
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
base.Parent.Paint += new PaintEventHandler(Parent_Paint);
}
private void Parent_Paint(object sender, PaintEventArgs e)
{
if (base.Enabled && string.IsNullOrEmpty(base.Text))
{
e.Graphics.DrawImage(requiredIcon, 0, 0);
}
}

Categories

Resources