Invalidating again and again Windows Forms - c#

I am using this code to draw some circles but it keeps redrawing and redrawing and stops with error that textBox1.text has bad number format even when I try 5 or 6. What is wrong with this?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace terc
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
}
void Button1Click(object sender, EventArgs e)
{
this.Paint += new PaintEventHandler(KresliTerc);
}
protected void KresliTerc(object sender,PaintEventArgs e)
{
Graphics grfx = e.Graphics;
int pocet = int.Parse(textBox1.Text);
label1.Text = pocet.ToString();
for(int i=1; i <= pocet; i++)
{
grfx.FillEllipse(Brushes.Black,ClientSize.Width/2,ClientSize.Height/2,50*i,50*i);
Invalidate();
}
}
}
}

Why do you attach the event handler each time that the button is clicked? This means that you will have as many event handlers as button clicks, which is I doubt is what you want.
However, your repaint problem is probably the fact that you call Invalidate inside the Paint event handler. This will force a new repaint of the form. So for each time that you paint, you will trigger a new paint, which will trigger a new paint and so on.

You should not invalidate in a Paint call.
Handle the painting based on some integer or boolean values.
Set the integer or boolean values in click events and just call Invalidate in the button click handler.

this.Paint += new PaintEventHandler(KresliTerc);
where are you calling this , since it is a paint event it will call again and again when painting occurs

Boolean isButtonClicked;
protected override void OnPaint(PaintEventArgs e)
{
if (this.isButtonClicked)
{
this.isButtonClicked = false;
// some paint logic goes down here...
e.Graphics.FillEllipse(Brushes.YellowGreen, 12, 12, 54, 54);
}
}
private void HandleOnButtonClick(Object sender, EventArgs e)
{
this.isButtonClicked = true;
this.Invalidate();
}

Related

In C#/NET5.0 Windows Forms - How can I get a panel to NOT clear itself with the BackColor before drawing in the paint event?

I have tried to get rid of the flickering effect when updating the graphical content of a panel in my application. Simplified, the Mainform.cs looks like this:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsAppRefreshTest
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
var g = panel1.CreateGraphics();
g.FillRectangle(Brushes.Red, 20, 20, panel1.Width - 40, panel1.Height - 40);
if(panel1.Width>100 && panel1.Height>100) g.FillRectangle(Brushes.Blue, 40, 40, panel1.Width - 80, panel1.Height - 80);
}
private void panel1_SizeChanged(object sender, EventArgs e)
{
panel1.Refresh();
}
}
}
If I set a breakpoint on the first row of panel1_Paint the panel will be cleared to the BackColor as the event is called. This is not what I want - I want the panel to be painted only by the panel1_Paint method, since I draw the whole area every time with a bitmap that has been prepared in my real application.
How do I get Windows Forms NOT to clear the panel with BackColor before the code can paint it?
Using Panel, I was able to solve this issue using the suggestion from Mark, creating a subclass to Panel looking like this:
public class NoBackgroundPanel : Panel
{
public NoBackgroundPanel() { }
protected override void OnPaintBackground(PaintEventArgs e) { }
}
and using this instead of System.Windows.Forms.Panel in MainForm.Designer.cs - InitializeComponent(): this.panel1 = new NoBackgroundPanel();
This was for me the desired solution since I am manually updating a Bitmap used as a double buffer in my real application, then drawing that to the full area of the panel when it has to be redrawn.

WinForm button background image updates only every other run

I want a blinking LED -- alternately a lighted, then a dark image in a PictureBox -- to appear during a run (that I click a button to start). The image lights when the run starts, and goes dark when it the run finishes. That always works.
This code:
this.timer.SynchronizingObject = this;
this.timer.Interval = 250;
this.timer.Elapsed += (s, ea) =>
{
this.ledLit = !this.ledLit;
ShowInLog(this.ledLit ? "/" : "\\");
this.picMarking.BackgroundImage = this.ledLit ? this.imageStopped : this.imageRunning;
this.picMarking.Refresh();
};
works great to show a blinking LED image during the run...every other run.
On every even-numbered invocation, the display of alternating slashes shows that the timer is working, but the background image does not update (except perhaps a rare flicker).
Why? How do I make it to work on every invocation?
Here's some quick code I put together for your "blinking" effect:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Blinker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 250;
timer1.Tick += timer1_Tick;
timer1.Start();
}
void timer1_Tick(object sender, EventArgs e)
{
Console.WriteLine("Tick");
pictureBox1.BackColor = (pictureBox1.BackColor == System.Drawing.Color.Red) ? System.Drawing.Color.Black : System.Drawing.Color.Red;
}
}
}
I've used event handlers for the Tick event on the timer to trigger the blinking. You can adjust the image like I have with the BackColor property. It's a quick and dirty approach, but it gets the blinking effect achieved.
I found the answer to my problem here: Custom event handler is repeating itself...
My problem is that I subscribed to the tick event each time, rather than only once.

How to select multiple controls by mouse-dragging over them

I want to be able to drag over a bunch of controls and select those of a certain type (TextBoxes).
Once the dragging action has been completed, I want to display an inputbox (yes, I'll have to reference/use the VB .dll) prompting the user for the value that will be entered in each selected TextBox.
Can this be done? (of course, but how?)
Or is there another way to accomplish the same thing (allow the user to quickly select multiple controls and then perform an action on all of them at once)?
Updated:
I've got this sort of working - the "caveat" or "gotcha" being that I have to pop up a MessageBox.Show() to the user for it to work. Essentially, I:
Set a boolean to true on the container's (FlowLayoutPanel, in my case) MouseDown event, if the right mouse button was selected.
Set that same boolean to false on the container's MouseUp event, if the right mouse button was selected.
I then have a shared MouseHover event handler for all of the TextBoxes on that form that, if the boolean is true, changes the BackColor (to Gainsboro, in my case, from Window).
In the container's MouseUp event, I also use an InputBox (referencing/importing/using the VB .dll) requesting the user enter the value that will be common for the "highlighted" TextBoxes. I then loop through them, looking for those with that BackColor, and assing the user-supplied value to their Text properties.
Voila!
Unfortunately, the TextBoxes' Modified property does not seem to be altered when you assign it values this way, so I had to work around that (explicitly setting the "Save" button to enabled), and I had to add more code to duplicate my KeyPressed code which limits the values entered by the user.
So, it is, of course, possible, albeit a little kludgy. I haven't decided if the MessageBox.Show() is a "bug" or a feature, though...
A related post is: Why does MouseHover event only get called if a messagebox.show() or breakpoint occurs before it?
This is actually very simple. I assume by drag you mean that you want to 'select' controls, like you would 'select' some pixels in a paint program for example. Here is an example I wrote you that does just this and only selects TextBox controls. It ignores other controls.
namespace WindowsFormsApplication5
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
/// <summary>
/// Main application form
/// </summary>
public partial class Form1 : Form
{
/// <summary>
/// Initializes a new instance of the WindowsFormsApplication5.Form1 class
/// </summary>
public Form1() {
InitializeComponent();
DoubleBuffered = true;
}
private Point selectionStart;
private Point selectionEnd;
private Rectangle selection;
private bool mouseDown;
private void GetSelectedTextBoxes() {
List<TextBox> selected = new List<TextBox>();
foreach (Control c in Controls) {
if (c is TextBox) {
if (selection.IntersectsWith(c.Bounds)) {
selected.Add((TextBox)c);
}
}
}
// Replace with your input box
MessageBox.Show("You selected " + selected.Count + " textbox controls.");
}
protected override void OnMouseDown(MouseEventArgs e) {
selectionStart = PointToClient(MousePosition);
mouseDown = true;
}
protected override void OnMouseUp(MouseEventArgs e) {
mouseDown = false;
SetSelectionRect();
Invalidate();
GetSelectedTextBoxes();
}
protected override void OnMouseMove(MouseEventArgs e) {
if (!mouseDown) {
return;
}
selectionEnd = PointToClient(MousePosition);
SetSelectionRect();
Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (mouseDown) {
using (Pen pen = new Pen(Color.Black, 1F)) {
pen.DashStyle = DashStyle.Dash;
e.Graphics.DrawRectangle(pen, selection);
}
}
}
private void SetSelectionRect() {
int x, y;
int width, height;
x = selectionStart.X > selectionEnd.X ? selectionEnd.X : selectionStart.X;
y = selectionStart.Y > selectionEnd.Y ? selectionEnd.Y : selectionStart.Y;
width = selectionStart.X > selectionEnd.X ? selectionStart.X - selectionEnd.X : selectionEnd.X - selectionStart.X;
height = selectionStart.Y > selectionEnd.Y ? selectionStart.Y - selectionEnd.Y : selectionEnd.Y - selectionStart.Y;
selection = new Rectangle(x, y, width, height);
}
}
}
Now there are limitations with this currently. The obvious is that this will not select a TextBox within a nested container control (eg. a panel on your form containing a TextBox). If that were the case the selection would be drawn underneath the panel, and the TextBox would not be selected because the code I wrote does not check nested containers.
You can easily update the code to do all of this however, but this should give you a solid start.

how to stop flickering C# winforms

I have a program that is essentially like a paint application. However, my program has some flickering issues. I have the following line in my code (which should get rid of flickering - but doesn't):
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
my code(minus the super and sub classes for the shapes is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Paint
{
public partial class Paint : Form
{
private Point startPoint;
private Point endPoint;
private Rectangle rect = new Rectangle();
private Int32 brushThickness = 0;
private Boolean drawSPaint = false;
private List<Shapes> listOfShapes = new List<Shapes>();
private Color currentColor;
private Color currentBoarderColor;
private Boolean IsShapeRectangle = false;
private Boolean IsShapeCircle = false;
private Boolean IsShapeLine = false;
public SPaint()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
currentColor = Color.Red;
currentBoarderColor = Color.DodgerBlue;
IsShapeRectangle = true;
}
private void panelArea_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelArea.CreateGraphics();
if (drawSPaint == true)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
if (IsShapeRectangle == true)
{
g.DrawRectangle(p, rect);
}
else if (IsShapeCircle == true)
{
g.DrawEllipse(p, rect);
}
else if (IsShapeLine == true)
{
g.DrawLine(p, startPoint, endPoint);
}
}
foreach (Shapes shape in listOfShapes)
{
shape.Draw(g);
}
}
private void panelArea_MouseDown(object sender, MouseEventArgs e)
{
startPoint.X = e.X;
startPoint.Y = e.Y;
drawSPaint = true;
}
private void panelArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (e.X > startPoint.X)
{
rect.X = startPoint.X;
rect.Width = e.X - startPoint.X;
}
else
{
rect.X = e.X;
rect.Width = startPoint.X - e.X;
}
if (e.Y > startPoint.Y)
{
rect.Y = startPoint.Y;
rect.Height = e.Y - startPoint.Y;
}
else
{
rect.Y = e.Y;
rect.Height = startPoint.Y - e.Y;
}
panelArea.Invalidate();
}
}
private void panelArea_MouseUp(object sender, MouseEventArgs e)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
drawSPaint = false;
if (rect.Width > 0 && rect.Height > 0)
{
if (IsShapeRectangle == true)
{
listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeCircle == true)
{
listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeLine == true)
{
listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
}
panelArea.Invalidate();
}
}
private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = true;
IsShapeCircle = false;
IsShapeLine = false;
}
private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = false;
IsShapeCircle = true;
IsShapeLine = false;
}
private void lineToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeCircle = false;
IsShapeRectangle = false;
IsShapeLine = true;
}
private void ThicknessLevel0_Click(object sender, EventArgs e)
{
brushThickness = 0;
}
private void ThicknessLevel2_Click(object sender, EventArgs e)
{
brushThickness = 2;
}
private void ThicknessLevel4_Click(object sender, EventArgs e)
{
brushThickness = 4;
}
private void ThicknessLevel6_Click(object sender, EventArgs e)
{
brushThickness = 6;
}
private void ThicknessLevel8_Click(object sender, EventArgs e)
{
brushThickness = 8;
}
private void ThicknessLevel10_Click(object sender, EventArgs e)
{
brushThickness = 10;
}
private void ThicknessLevel12_Click(object sender, EventArgs e)
{
brushThickness = 12;
}
private void ThicknessLevel14_Click(object sender, EventArgs e)
{
brushThickness = 14;
}
private void FillColour_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentColor = fillColourDialog.Color;
panelArea.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentBoarderColor = fillColourDialog.Color;
panelArea.Invalidate();
}
}
}
How do i stop the flickering?
*UPDATE:*This code actually works great when i'm drawing directly on the form. However, when i try to draw on the panel, flickering becomes an issue
For a "cleaner solution" and in order to keep using the base Panel, you could simply use Reflection to implement the double buffering, by adding this code to the form that holds the panels in which you want to draw in
typeof(Panel).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, DrawingPanel, new object[] { true });
Where "DrawingPanel" is the name of the panel that you want to do the double buffering.
I know quite a lot of time has passed since the question was asked, but this might help somebody in the future.
Finally solved the flickering. Since I was drawing on a panel instead of the form the line of code below will not solve the flickering:
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,
true);
SetStyle must be of type 'YourProject.YourProject' (or derived from it) hence, you have to create a class as such (so that you can use MyPanel which will be derived from SPaint.SPaint and hence allowing you to use doublebuffering directly for the panel - rather than the form):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPaint;
namespace YourProject
{
public class MyPanel : System.Windows.Forms.Panel
{
public MyPanel()
{
this.SetStyle(
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer,
true);
}
}
}
After you've done this(although you should really never edit the designer code unless you truly know what you're doing) you'll have to edit the Form.Designer.cs. Inside this file you will find code that looks like this:
this.panelArea = new YourProject.MyPanel();
The above line needs to be changed to:
this.panelArea = new MyPanel();
After I completed these steps, my paint program no longer flickers.
For anyone else having the same issue, the problem is finally solved.
Enjoy!
Copy and paste this into your project
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return handleParam;
}
}
This enables double-buffering for all controls from the form level down, otherwise double buffering needs to be individually enabled for each one... you may want to fine tune double bufferring after this because blanketed double buffering may give unwanted side effects.
I have had the same problem. I was never able to 100% rid myself of the flicker (see point 2), but I used this
protected override void OnPaint(PaintEventArgs e) {}
as well as
this.DoubleBuffered = true;
The main issue for flickering is making sure you
paint things it the right order!
make sure your draw function is < about 1/60th of a second
winforms invokes the OnPaint method each time the form needs to be redrawn. There are many ways it can be devalidated, including moving a mouse cursor over the form can sometimes invoke a redraw event.
And important note about OnPaint, is you don't start from scratch each time, you instead start from where you were, if you flood fill the background color, you are likely going to get flickering.
Finally your gfx object. Inside OnPaint you will need to recreate the graphics object, but ONLY if the screen size has changed. recreating the object is very expensive, and it needs to be disposed before it is recreated (garbage collection doesn't 100% handle it correctly or so says documentation). I created a class variable
protected Graphics gfx = null;
and then used it locally in OnPaint like so, but this was because I needed to use the gfx object in other locations in my class. Otherwise DO NOT DO THIS. If you are only painting in OnPaint, then please use e.Graphics!!
// clean up old graphics object
gfx.Dispose();
// recreate graphics object (dont use e.Graphics, because we need to use it
// in other functions)
gfx = this.CreateGraphics();
Hope this helps.
Double buffering is not going to be of much help here I'm afraid. I ran into this a while ago and ended up adding a separate panel in a rather clumsy way but it worked for my application.
Make the original panel that you have ( panelArea ) a transparent area, and put it on top of a 2nd panel, which you call panelDraw for example. Make sure to have panelArea in front. I whipped this up and it got rid of the flickering, but left the shape that was being drawn smeared out so it's not a full solution either.
A transparent panel can be made by overriding some paint actions from the original panel:
public class ClearPanel : Panel
{
public ClearPanel(){}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= 0x00000020;
return createParams;
}
}
protected override void OnPaintBackground(PaintEventArgs e){}
}
The idea is to handle drawing the temporary shape during the MouseMove event of the 'panelArea' and ONLY repaint the 'panelDraw' on the MouseUp Event.
// Use the panelDraw paint event to draw shapes that are done
void panelDraw_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelDraw.CreateGraphics();
foreach (Rectangle shape in listOfShapes)
{
shape.Draw(g);
}
}
// Use the panelArea_paint event to update the new shape-dragging...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelArea.CreateGraphics();
if (drawSETPaint == true)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
if (IsShapeRectangle == true)
{
g.DrawRectangle(p, rect);
}
else if (IsShapeCircle == true)
{
g.DrawEllipse(p, rect);
}
else if (IsShapeLine == true)
{
g.DrawLine(p, startPoint, endPoint);
}
}
}
private void panelArea_MouseUp(object sender, MouseEventArgs e)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
drawSETPaint = false;
if (rect.Width > 0 && rect.Height > 0)
{
if (IsShapeRectangle == true)
{
listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeCircle == true)
{
listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeLine == true)
{
listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
}
panelArea.Invalidate();
}
panelDraw.Invalidate();
}
I know this is really old question but maybe someone will find it useful.
I'd like to make little enhancement to viper's answer.
You can make simple extension to Panel class and hide setting property through reflection.
public static class MyExtensions {
public static void SetDoubleBuffered(this Panel panel) {
typeof(Panel).InvokeMember(
"DoubleBuffered",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
null,
panel,
new object[] { true });
}
}
If your Panel variable's name is myPanel you can just call
myPanel.SetDoubleBuffered();
and that's it. Code looks much cleaner.
I'd advise overriding OnPaintBackground and handling the background erase yourself. If you know you are painting the whole control you can just do nothing in OnPaintBackground (don't call the base method) and it will prevent the background colour being painted first
In this condition you have to enable double buffer .
Open current form and go to form properties and apply double buffer true;
or you can also write this code .
this.DoubleBuffered = true;
In form load.
if all of the above doesn't work you can always create your own double buffer
link to Microsofts tutorial: https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce-graphics-flicker-with-double-buffering-for-forms-and-controls
hopes it works for you
It's a bit of and old question, but just to be complete: There is yet another solution, that worked for me where the double-buffering did not.
As it turns out Microsoft offers the BufferedGraphics class as a solution. Nice thing about this class is that it enables you to copy one Graphics object to another, so except from setting up a temporary Graphics object and eventually copying it to the final destination, one can use pretty much the same code that one would have done when the flickering should not have been a problem:
private void Indicator_Paint(object sender, PaintEventArgs e)
{
Control pbIndicator = (Control)sender;
Rectangle targetRect = pbIndicator.ClientRectangle;
Image img = Bitmap.FromFile("bitmap.bmp");
BufferedGraphicsContext ctx = new BufferedGraphicsContext();
BufferedGraphics bg = ctx.Allocate(e.Graphics, targetRect);
// Do the graphic stuff
bg.Graphics.Clear(this.BackColor);
bg.Graphics.DrawImage(img, 0, 0);
// etcetera
bg.Render(e.Graphics);
bg.Dispose();
ctx.Dispose();
}
Downside of this solution that it might clutter your code. Furthermore I'm not sure whether it is a good idea to setup the context each time, or whether it would suffice to create one in advance and keep using that context.
For more information see https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bufferedgraphicscontext?view=dotnet-plat-ext-3.1
here is the program of moving circle in .net, that doesn't flicker.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace CircleMove
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
int x=0,y=0;
Thread t;
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
}
void MainFormPaint(object sender, PaintEventArgs e)
{
Graphics g=e.Graphics;
Pen p=new Pen(Color.Orange);
Brush b=new SolidBrush(Color.Red);
// g.FillRectangle(b,0,0,100,100);
g.FillEllipse(b,x,y,100,100);
}
void MainFormLoad(object sender, EventArgs e)
{
t=new Thread( new ThreadStart(
()=>{
while(true)
{
Thread.Sleep(10);
x++;y++;
this.Invoke(new Action(
()=>{
this.Refresh();
this.Invalidate();
this.DoubleBuffered=true;
}
)
);
}
}
)
);
t.Start();
}
}
}
If memory is tight (so you don't want the memory cost of double-buffering), one possible way to REDUCE, though not eliminate, flicker, is to set background color to the dominant color in your current scene.
Why this helps: flicker is a momentary flash of the background color, which the OS draws before drawing child controls or your custom drawing code. If that flash is a color that is closer to the final color to be displayed, it will be less noticeable.
If you are not sure what color to start with, start with 50% gray, because this is an average of black and white, so will be closer to most colors in your scene.
myFormOrControl.BackColor = Color.Gray;
Try to insert drawing logic in current form's
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
method. In this case you should use parameter e to get Graphics object. Use e.Graphics property. Then you should invoke Invalidate() method for this form whenever form must be redrawn.
PS: DoubleBuffered must be set to true.
Drawing onto a Label instead of a Panel, solved the problem for me.
No need to use DoubleBuffering or anything either.
You can remove the text from the label, set AutoSize to false, then Dock it or set the Size and use it as for the Panel.
Best wishes,
Quite late into the game... but for me WS_EX_COMPOSITED worked fine for a while and after some time of application start, layout started behaving very weird. I use TabControl and some TabPages on addition/removal started displaying titles of its siblings, so be careful with using it. Without WS_EX_COMPOSITED all worked fine, but I still had the flickering issue on new TabPage add.
None solution here really worked for me so I started looking at my code. I have a functionality where I create RichTextBox, then this RichTextBox is added to newly created TabPage and finally TabPage is added to TabControl. I decided to change order of that action - firstly I create TabPage, then add it to TabControl, and when TabPage is already added to TabControl, I add RichTextBox to it. When I did that, no more flickering and all works perfectly without any 'hacks'.
The moral of the story is that if nothing really works for you, try to (if possible and applicable in your case) play around with order in which you add controls.
Can you try using a timer and boolean to check if mouse is down, and paint in that spot, using a variable again check if user has moved his mouse, if moved paint that spot too etc.
Or just check if mouse down(via boolean that sets true when mouse is down) using a timer and paint it considering you are probably trying to just paint one pixel, not like you have shadow etc. Instead of using actual mousedown. So you check every 1 second instead of 0.0001 and it wont flicker. Or vice-versa, try it with your own times.
just do this.Refresh() when shown the form.

Drawing rectangle does not work in Windows Forms

The program should draw a rectangle on click. But it does not. Maybe some problem with my understanding of delegates. What's the catch?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace forms1
{
public partial class MainForm : Form
{
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//this.Paint+= new PaintEventHandler(MujPaintHandler);
this.Click += new EventHandler(MujClickHandler);
}
public void MujPaintHandler(object sender,PaintEventArgs e)
{
Graphics gfx=e.Graphics;
gfx.FillRectangle(new SolidBrush(Color.DarkViolet),100,100,200,200);
}
public void MujClickHandler(object sender,EventArgs e)
{
this.Text="aaaaa";
this.Paint+= new PaintEventHandler(MujPaintHandler);
}
}
}
The code you've written works for me, but your form's window has to be big enough to show the rectangle.
Diagnostically, the first thing I'd check is whether the title of the form changes to "aaaaa". If it does, then you know the click handler is being called - but maybe you've got a problem with the paint handler. If it doesn't, then for some reason your click handler isn't being called.
Note that this isn't the normal way you'd draw a rectangle on a click in Windows Forms, but I'm assuming this is just a learning exercise.
Try forcing a redraw after assigning the event handler:
public void MujClickHandler(object sender,EventArgs e)
{
this.Text="aaaaa";
this.Paint+= new PaintEventHandler(MujPaintHandler);
this.Invalidate();
}
Also, if you click twice, the event handler gets assigned twice, which is not something you want.
Maybe you have to force a redraw. Does the rect appear if you move the window? Just call the PaintHandler after click.
I think you are only attaching the Paint event handler. You are not invoking the Paint event.
Try this
public void MujClickHandler(object sender,EventArgs e)
{
this.Text="aaaaa";
this.Paint+= new PaintEventHandler(MujPaintHandler);
this.Invalidate();
}
It will only draw the rectangle if it happens to be inside the area that is invalidated to update the text that you change.
When you change the text, it creates a message that the text has to be redrawn, which will call the Paint event to do the drawing. The event will have a Graphics object that is clipped to the rectangle that needs to be redrawn to update the text, so only the part of the rectangle that intersects with the text will be drawn.
You have to cause a redraw that covers the entire rectangle, so the easiest is to cause the whole window to be redrawn:
this.Invalidate();
Note that you should not hook up the Paint event from the Click event handler. That means that the event will be hooked up one more time each click, so after five clicks the Paint event handler will be called five times every time something needs to be redrawn.
Looking at your code, I understand that you are trying to add an eventhandler for paint event on Click... You would also need to invoke the paint event.. Here is a sample code where I am assigning the Paint EventHandler on a button click and raising the paint event on the click as you are doing
public Form1()
{
InitializeComponent();
this.Click += new EventHandler(MujClickHandler);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
MessageBox.Show(e.KeyCode.ToString());
}
}
private void Form1_Load(object sender, EventArgs e)
{
//this.Paint += new PaintEventHandler(MujPaintHandler);
}
public void MujPaintHandler(object sender,PaintEventArgs e)
{
Graphics gfx=e.Graphics;
gfx.FillRectangle(new SolidBrush(Color.DarkViolet),100,100,200,200);
}
public void MujClickHandler(object sender,EventArgs e)
{
this.Text="aaaaa";
this.RaisePaintEvent(this, new PaintEventArgs(this.CreateGraphics(), this.RectangleToClient(new Rectangle())));
}
private void button1_Click(object sender, EventArgs e)
{
this.Paint += new PaintEventHandler(MujPaintHandler);
}

Categories