I am trying to call panel1 paint method to repaint the panel with a orange line (it is initiated with a blue line).
I have tried invalidate(), update() and refresh(), but nothing seems to call the paint event of panel1...
The paint event handler have been added to the panel1:
this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint);
Can someone please assist?
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 testForm = new Form1();
Application.Run(testForm);
testForm.drawNewLine();
}
}
public partial class Form1 : Form
{
bool blueLine = true;
bool orangeLine = false;
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
if (blueLine == true)
{
Pen bluePen = new Pen(Color.Blue, 3);
g.DrawLine(bluePen, 30, 50, 30, 250);
}
else if (orangeLine == true)
{
Pen orangePen = new Pen(Color.Orange, 3);
g.DrawLine(orangePen, 30, 50, 30, 250);
}
g.Dispose();
}
public void drawNewLine()
{
blueLine = false;
orangeLine = true;
//panel1.Invalidate();
//panel1.Update();
panel1.Refresh();
}
}
Application.Run(testForm); blocks until form is closed, so when drawNewLine() is called - form does not exist anymore (create a button which calls it on click and check yourself, the code is working). Invalidate() should work just fine.
Also, you should not dispose Graphics object which is passed to your code in paint event. You were not responsible for creating it, so let the code which created it to destroy it.
Also, dispose Pen objects since you are creating them.
Related
I have a question about a program I am writing for practice, which in its current state allows the user to move a label around the Form using the arrow keys.
I want to start adding some graphical rectangles to my program, and am currently practicing by trying to draw a simple rectangle once a timer hits 100.
Here's the weird part, the rectangle only draws once the label has passed over part of it, and will only draw the part the label passes over. Picture of this happening: label passing over rectangle. Ideally I would like to understand why this is happening, but will also be very happy if anyone can offer a solution!
I will post my whole code as I'm not even sure which part the problem could be coming from. Very sorry if its untidy, hopefully someone will have an idea just from the image:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
int direction;
int X = 200;
int Y = 200;
Rectangle myRock;
public System.Windows.Forms.Timer aTimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.Click += new System.EventHandler(this.Form1_Click);
}
void worker()
{
for (int i = 0; i < 100000; i++) {
if (label2.InvokeRequired)
{
label2.Invoke((MethodInvoker)delegate
{
label2.Text = i.ToString(); // this is a timer acting as a score keeper
});
Thread.Sleep(100);
}
}
}
public void DrawRectangleRectangle(PaintEventArgs e, Rectangle rect)
{
// Create pen.
Pen blackPen = new Pen(Color.White, 3);
SolidBrush whiteBrush = new SolidBrush(Color.White);
// Create rectangle.
// Draw rectangle to screen.
e.Graphics.DrawRectangle(blackPen, rect);
e.Graphics.FillRectangle(whiteBrush, rect);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(worker);
direction = 2;
newThread.Start();
}
private void SetDirection(KeyEventArgs e)
{
if (e.KeyCode == Keys.Down) direction = 4;
else if (e.KeyCode == Keys.Up) direction = 2;
else if (e.KeyCode == Keys.Right) direction = 3;
else if (e.KeyCode == Keys.Left) direction = 1;
}
private void ApplyMovement()
{
Size size = new Size(100,100);
Point position = new Point(100, 100);
while (direction != 0)
{
Application.DoEvents();
if (direction == 1) X--;
else if (direction == 2) Y--;
else if (direction == 3) X++;
else if (direction == 4) Y++;
if (label2.Text == "100") myRock = new Rectangle(position, size);
Thread.Sleep(10);
label1.Location = new Point(X, Y);
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
SetDirection(e);
ApplyMovement();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
int label2Variable = Convert.ToInt32(label2.Text);
if (label2Variable > 100)
{
DrawRectangleRectangle(e, myRock);
}
}
private void label1_Click(object sender, EventArgs e)
{
}
}
}
Thanks in advance!
What is happening is that the OnPaint() function is not being called, so you aren't repainting all of the form. When Windows believes that something on your form has changed, it will issue a Repaint command to your application, asking it to redraw all or a section of the form's area, which appears in your code as the Form.OnPaint() method being fired.
What you need to do here is tell Windows that your form has changed. The easiest way to do this is to call:
this.Invalidate();
once something has changed, so possibly at the end of the loop in ApplyMovement().
(You can also call this.Refresh(), which will Invalidate the whole form, and then cause a synchronous repaint of it. Thanks #Bradley)
That way, you're telling Windows that the form is "invalid" and needs to be completely repainted, whereupon Windows will (indirectly) call the OnPaint() method.
Edit
Having rehydrated your app, I have applied the following change to the ApplyMovement() method:
if (label2.Text == "100") myRock = new Rectangle(position, size);
... changed to ...
if (label2.Text == "100")
{
myRock = new Rectangle(position, size);
this.Invalidate();
}
Now, when the value of the incrementing label field hits "100", the myRock rectangle appears immediately in my tests.
It's important to understand that your code never calls OnPaint() directly. You use the Invalidate() method to tell Windows itself that a part (or all) of your form has changed and needs to be updated on-screen. Windows will then "call" the OnPaint() method.
If you're using the OnPaint() method like this to draw information from your program to the screen, such as the myRock rectangle, or some custom painted text, then any time that information changes, you need to call Invalidate() to trigger an OnPaint().
It's the way all the common controls, such as textboxes and buttons, update themselves to the screen. They call Invalidate() internally any time you change one of their properties.
A quick way to think about it is that anytime you want OnPaint() to be called, you call Invalidate().
Hope this helps
I've got a simple Windows.Forms Form.
I want to fill the pictureBox1 with color Color.Aqua and draw a rectangle.
However nothing is drown untill I move the Form.
Why is this?
How can I force everything to be drawn without moving the Form?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly Graphics _graphics;
private List<PointF> _points;
private Bitmap _bitmap;
public Form1()
{
InitializeComponent();
_bitmap = new Bitmap(1000, 600);
_graphics = Graphics.FromImage(_bitmap);
pictureBox1.Image = _bitmap;
var timer = new Timer
{
Interval = 1
};
timer.Tick += OnTick;
timer.Start();
Invalidate();
}
private void OnTick(object sender, EventArgs e)
{
_graphics.Clear(Color.Aqua);
_graphics.DrawRectangle(Pens.Black, 10, 10, 10, 10);
Invalidate();
}
}
}
You must subscribe to Paint event of your picturebox and put your drawing code there,something like this:
public Form1()
{
InitializeComponent();
pictureBox1.Paint += PictureBox1_Paint;
}
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Aqua);
e.Graphics.DrawRectangle(Pens.Black, 10, 10, 10, 10);
}
This event is raised everytime the control needs to be redrawn, so you don't need that Timer trick or calling Invalidate
I see four problems:
The Invalidate() method is called for the form. This should invalidate the PictureBox, but you can do better by just invalidating the PictureBox directly.
You are drawing the bitmap, but not updating the Image property of the PictureBox.
One millisecond intervals will kill you. 50 to 100 is much more reasonable, and anything less than 17 is probably faster than the refresh rate of your monitor.
The whole the thing with the separate graphics is extra and not needed. The pictureBox has it's own graphics context, and you do better using that.
Put it all together, and you get this:
public partial class Form1 : Form
{
private Timer _timer;
private List<PointF> _points;
public Form1()
{
InitializeComponent();
_timer = new Timer(100);
timer.Tick += OnTick;
timer.Start();
}
private void OnTick(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Aqua);
e.Graphics.DrawRectangle(Pens.Black, 10, 10, 10, 10);
}
}
You don't need the Invalidate() call at all, since you're drawing to a buffer (Bitmap). Just set the bitmap to pictureBox1.Image property:
private void OnTick(object sender, EventArgs e)
{
_graphics.Clear(Color.Aqua);
_graphics.DrawRectangle(Pens.Black, 10, 10, 10, 10);
pictureBox1.Image = _bitmap;
}
I'm very new(read 3 weeks exp) to C#(programming in general),started with html/css and javascript and now on my way with C#.
I'm trying to make my own simple 'Paint' application in windows form. But i've encountered an issue and just cant wrap my head around it, doesnt matter how much i read or follow other mans code, i'm stuck. The following code works fine but when resizing the application window the drawing dissappears.
As a solution ive read that declaring the Graphics method within the panel1_Paint event this should be resolved And here is my issue. See last code sample, ive come up with this(yes like i said, im new to this)and its not drawing anything.
ive simply tried to recreate the first example under the panel1_Paint event but i guess something went wrong during the mouseMove event and i cant figure out what it is.
Could someone explain to me what i am missing here, that would be very appreciated. thanks in advance.
[Old code]
namespace Painter
{
public partial class Form1 : Form
{
Graphics graphics;
Pen pen = new Pen(Color.Black, 1);
Point startingPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
bool mousePaint = false;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
startingPoint = e.Location;
if (e.Button == MouseButtons.Left)
{
mousePaint = true;
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if(mousePaint == true)
{
endPoint = e.Location;
graphics = panel1.CreateGraphics();
graphics.DrawLine(pen, startingPoint, endPoint);
}
startingPoint = endPoint;
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
mousePaint = false;
}
}
}
[New Code]
namespace Painter
{
public partial class Form1 : Form
{
Pen pen = new Pen(Color.Black, 1);
Point startingPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
bool mousePaint = false;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = panel1.CreateGraphics();
if (mousePaint == true)
{
graphics.DrawLine(pen, startingPoint, endPoint);
}
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
startingPoint = e.Location;
if (e.Button == MouseButtons.Left)
{
mousePaint = true;
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if(mousePaint == true)
{
endPoint = e.Location;
}
startingPoint = endPoint;
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
mousePaint = false;
}
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = panel1.CreateGraphics();
This is nonsense! Always and only use the e.Graphics object from the Paint param!!
Also: To trigger the Paint event do a panel1.Invalidate(); whenever your drawing data have changed!
Also: Make sure you understand just what your mousePaint flag is supposed to control: the mouse painting (i.e. adding new shapes to draw) or the regular painting (i. all shape previously drawn)!? Note that all drawing, current and previous needs to be done from the Paint event, whenever necessary i.e. over and over again!
To be able to do so: Collect all the shpes' data in a List<T>..
To Doublebuffer a Panel you need to subclass it. Your code turns on DoubleBuffering for the Form, which fine but won't help the Panel..
Instead simply use a PictureBox, which is control meant for drawing on!
A DoubleBuffered Panel subclass is as simple as this:
class DrawPanel : Panel
{
public DrawPanel()
{
DoubleBuffered = true;
}
}
Update: Instead you can also use a Label (with Autosize=false); it also has the DoubleBuffered property turned on out of the box and supports drawing better than Panels do.
The following code works fine but when resizing the application window the drawing dissappears.
This happens because resizing the application window invalidates portion of your panel which causes the portion to be redrawn.
Reason why your second approach is not working (the one labelled as [NEW CODE]) is because the Paint event is called only when relevant component is redrawn. You could partially solve this by forcing redraw of the panel in your MouseDown/MouseMove event handlers but you would still lose your previously painted stuff.
Possible solution is to create instance of Bitmap and paint there. Then just set this Bitmap as BackgroundImage of the panel. You can find more information on that here. Of course you would need to think about stuff like resizing and what should happen to the bitmap if application window gets shrunk or enlarged.
Here is some code that I quickly put together to get you started:
namespace WinForms_PaintTest
{
public partial class Form1 : Form
{
private Pen pen;
private Bitmap bitmap;
public Form1()
{
InitializeComponent();
this.pen = new Pen(Color.Black, 1);
this.bitmap = new Bitmap(this.panel1.Width, this.panel1.Height);
this.panel1.BackgroundImage = this.bitmap;
}
private void panel1_MouseMove(Object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
using (Graphics g = Graphics.FromImage(this.bitmap))
{
g.DrawRectangle(this.pen, e.Location.X, e.Location.Y, 1, 1);
}
this.panel1.Refresh();
}
}
private void Form1_FormClosed(Object sender, FormClosedEventArgs e)
{
this.pen.Dispose();
this.bitmap.Dispose();
}
}
}
Also regarding this:
this.DoubleBuffered = true;
I believe your intention was to prevent the flickering when relevant control is being redrawn? If that is case you need to set this property against the panel and not against the form itself. It is little bit tricky though because DoubleBuffered property of the panel is protected so you will need to either inherit from the panel or resort to reflection. You can find more information here .
In my application I have a mainform. When the open button is clicked I want to show a second (borderless) form whith the text loading. I've got this working so far.
But what I want is that the loading form is centered relative to the mainform. How do I do this?
SOLUTION:
private void tsbOpen_Click(object sender, EventArgs e)
{
if (_fileDialog.ShowOpenDialog() == DialogResult.OK)
{
_progress = new frmProgress(); // _progress is a member var
backgroundWorker1.RunWorkerAsync("open");
_progress.ShowDialog(this);
}
}
You can set StartPosition to CenterParent and pass the mainform as an Owner.
I created a subform named ProcessingRequest and I put some text and an animated gif on it.
I have a Property in my main form that calculates the location my sub form should be in.
private Point ProcessingLocation { get { return new Point(this.Location.X + this.Width / 2 - new ProcessingRequest().Width / 2, this.Location.Y + this.Height / 2 - new ProcessingRequest().Height / 2); } }
I have a class that makes a new thread to show the sub form.
public class ShowProgress
{
static private System.Drawing.Point point;
static private ProcessingRequest p;
static public void ShowProgressForm(System.Drawing.Point myPoint)
{
point = myPoint;
Thread t = new Thread(new ThreadStart(ShowProgress.ShowForm));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
static private void ShowForm()
{
p = new ProcessingRequest();
p.StartPosition = FormStartPosition.Manual;
p.Location = point;
p.TopMost = true;
Application.Run(p);
}
static public void CloseForm()
{
p.Invoke(new CloseDelegate(ShowProgress.CloseFormInternal));
}
static private void CloseFormInternal()
{
p.Close();
}
}
public delegate void CloseDelegate();
Then in my main form I simply put
ShowProgress.ShowProgressForm(ProcessingLocation);
//heavy processing code goes here or whatever
ShowProgress.CloseForm();
:)
Martijn try this
at the start of the method put some code like this
public sub Bah()
{
if (me.InvokeRequired)
{
me.Invoke(new action(Bah));
return
}
myform.showdialog...
}
dont know if this code compiles to 100% but you get the idea
Get the position of the main form coordinates and its size and take the size of child form and put some simple mathematics on it.
I have a Winforms app in C# that calls calls a method asynchronously and uses a callback.
I would like to display an animated gif to let the end user know that work is being done.
I would like to have the animated gif hover over the center of the form.
How can I do this?
Update:
Thanks. I guess the step I was missing was to use a Picture Box to hold the gif.
The following seems to be doing the trick of showing the gif and like jmatthews3865 said below I can just set the visible property of the PictureBox to false to hide it.
private ShowAnimatedGif()
{
PictureBox pb = new PictureBox();
this.Controls.Add(pb);
pb.Left = (this.Width / 2) - (pb.Width / 2);
pb.Top = (this.Height / 2) - (pb.Height / 2);
pb.Image = Resources.AnimatedGifHere;
pb.Visible = true;
}
in your form, simply include the image with it's visible property set to false.
from the event which calls the long running async process (button1_click etc.), set the images visibility property to true. event fires, image appears, async process runs and your ui thread should still be responsive.
in your callback event set the images visible property to false to indicate that the process is complete.
Need some code to give an exact answer, but this is fairly trivial, insert the gif before you make the asynchronous call, then remove it in the callback.
This is the answer. I'm using LoadingCircle which is an animated gif component.
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
}
private void button2_Click(object sender, EventArgs e)
{
Thread tr = new Thread(FlushToServer);
tr.Start();
}
private void ShowAnimation(bool show)
{
if (show)
{
loadingCircle1.Visible = true;
loadingCircle2.Active = true;
}
else
{
loadingCircle1.Visible = false;
loadingCircle1.Active = false;
}
}
private void FlushToServer()
{
this.Invoke(this.pa,true);
//your long running process
System.Threading.Thread.Sleep(5000);
this.Invoke(this.pa,false);
}
}
i modify the above code a bit and it will not throw error "c# invoke or begininvoke cannot be called on a control until the window handle has been created."
namespace AnimateUI
{
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
pictureBox1.Visible = false;
}
private void ShowAnimation(bool show)
{
if (show)
{
pictureBox1.Visible = true;
}
else
{
pictureBox1.Visible = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread tr = new Thread(StartTask);
tr.Start();
}
private void StartTask()
{
if (!this.IsHandleCreated)
this.CreateControl();
this.Invoke(this.pa, true);
System.Threading.Thread.Sleep(15000);
this.Invoke(this.pa, false);
}
}
}