PictureBox Refresh causes layers above to flicker - c#

I am trying to read data from ports and show that on a dial. the background is a PictureBox containing the image of a circular scale giving readings, and a Lineshape has been used to represent the dial. The PictureBox has been 'sent to back' to allow visibility of the Lineshape. I'm enclosing representative codes:
if i do this:
private void timer1_Tick(object sender, EventArgs e)
{
ang += 2.0 * Math.PI / 180.0;
lineShape1.X1 = Convert.ToInt32(lineShape1.X2 - r * Math.Sin(ang));
lineShape1.Y1 = Convert.ToInt32(lineShape1.Y2 - r * Math.Cos(ang));
}
then it leaves a trace on the PictureBox.
But if i use the Refresh function of the PictureBox, then the dial appears to flicker. i have tried with timer intervals of 10, 15, 20, 50 and 100, but the problem persists.
private void timer1_Tick(object sender, EventArgs e)
{
ang += 2.0 * Math.PI / 180.0;
lineShape1.X1 = Convert.ToInt32(lineShape1.X2 - r * Math.Sin(ang));
lineShape1.Y1 = Convert.ToInt32(lineShape1.Y2 - r * Math.Cos(ang));
pictureBox1.Refresh();
}
For the sake of the actual problem, it is not possible to increase the interval. What can i do to Show a smooth variation of the dial?

As #Hans Passant suggested, you should not be using a LineShape object. This is a example of code that implement his suggestion (quick, untested):
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
ang += 2.0 * Math.PI / 180.0;
var X1 = Convert.ToInt32(X2 - r * Math.Sin(ang));
var Y1 = Convert.ToInt32(Y2 - r * Math.Cos(ang));
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawLine(
new Pen(Color.Yellow, 1f),
new Point(X1, Y1),
new Point(X2, Y2 ));
}
Also, try to set this.DoubleBuffered = true; to avoid the flickering problem.

GDI+ is known to have this effect, in general. This is why WinForms has generally been replaced with WPF and XAML. I think, IIRC, PictureBox is especially prone to this problem. What you need to do is perform all of your "drawing" on the image in the PictureBox outsde of the PictureBox. Once all of your drawing is complete, update the PictureBox with the new image.
However, if you're drawing new images really quickly and subsequently updating the PictureBox, you're still likely to get flicker. PictureBox was just not designed for this use case. It's designed, more or less, to display a static image (or one that may change occassionally, but not at a high frequency).

Related

Relocate PictureBox after zoom C#

I have a PictureBox inside a Panel, and I have implemented the zoom function with a TrackBar.
When I increase (or decrease) the PictureBox, the position of the image remain fixed on the left side of the PictureBox.
See the example to better understand the problem.
What I want is the possibility to relocate the image compared to the center of the Panel. See the following example
For example I tried to define the PictureBox X origin in this way:
before the zoom I calculate the distance (Δdx) between the origin of the PictureBox (x0) and the center of the Panel (x1).
I increase the distance with the zoom factor (Δdx').
I calculate the new origin of the image (x0') as x1 - Δdx'
I do the same with Y and I define the new PictureBox location with x0' and y0'.
Here the code:
// new image width after the zoom
double width = pbImg.Image.Width + (pbImg.Image.Width * trackbar.Value / 100);
// new image height after the zoom
double height = pbImg.Image.Height + (pbImg.Image.Height * trackbar.Value / 100);
// panel center
int cX = panel.Width / 2;
int cY = panel.Height / 2;
// actual origin for the picturebox
int imgX = pbImg.Location.X;
int imgY = pbImg.Location.Y;
// distance the panel center and the picturebox origin
int distFromXc = cX - imgX;
int distFromYc = cY - imgY;
// new distance with zoom factor
distFromXc = distFromXc + (distFromXc * trackbar.Value / 100);
distFromYc = distFromYc + (distFromYc * trackbar.Value / 100);
// new origin point for the picturebox
int pbX = (cX - distFromXc);
int pbY = (cY - distFromYc);
// new dimension for the picturebox
pbImg.Size = new Size(Convert.ToInt32(width), Convert.ToInt32(height));
// relocate picturebox
Point p = new Point(pbX, pbY);
pbImg.Location = p;
I tried to modify this C# code, but I’m not familiar with it.
In my case I want to manage the Picturebox and the image inside it as the same object (if it’s possible).
What I want is the possibility to increase (or decrease) the Picturebox (and the image inside) but I want the Picturebox to stay centered where it currently is.
The SizeMode of the picture is StretchImage.
The Trackbar has 0% as he minimum value and 100% as the maximum.
The size of the Picturebox, and the image isnside, can be variable, I receive the images from another software.
A zoomed Picturebox can be bigger than Panel, but it’s not a problem, because I can move it.
The problems are the following:
1. If i use the code I wrote above, the reposition seems to work, but the Picturebox isn’t resized.
2. If I use a fixed value for the origin of the Picturebox (for example Point p = new Point(50, 50)), the resize works but obviously the position of the Picturebox is fixed.
This is because you are changing the size of the picturebox and not the size of the image within it. To ensure an image matches the size of the picture box ensure you set the stretchimage sizemode
pbImg.SizeMode = PictureBoxSizeMode.StretchImage
to get it working you could add this line just before you change the size of the picture box, however i recommend setting this on picturebox during its creation.
Refer to : Fit Image into PictureBox
If you want the PictureBox to stay centered where it currently is, and only expand or deflate "in place", then try something like this:
double width = pbImg.Width * trackbar.Value / 100;
double height = pbImg.Height * trackbar.Value / 100;
Rectangle rc = pbImg.Bounds;
rc.Inflate((int)((width - pbImg.Width) / 2), (int)((height - pbImg.Height) / 2));
pbImg.Bounds = rc;
Note that this is all based on the size of the PictureBox itself, NOT the Image within. Not sure what SizeMode you have set for the PB...
---------- EDIT ----------
I'm using StretchImage as SizeMode. Is possible to have the same behavior but without the button? When I move the cursor from left to right the Pb increase and decrease from right to left – Scarj
Of course. Put my code into the ValueChanged() and/or Scroll() events. - Idle_Mind
The original post works off the current size of the PictureBox. You might want to store the original Bounds() of the PB (maybe in the Tag() property) and then always compute the new size based on that instead.
Here's an example of that:
private void Form1_Load(object sender, EventArgs e)
{
pbImg.Tag = pbImg.Bounds;
}
private void button1_Click(object sender, EventArgs e)
{
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
ZoomPB();
}
private void trackBar1_ValueChanged(object sender, EventArgs e)
{
ZoomPB();
}
private void ZoomPB()
{
Rectangle rc = (Rectangle)pbImg.Tag;
double width = rc.Width * trackbar.Value / 100;
double height = rc.Height * trackbar.Value / 100;
rc.Inflate((int)((width - rc.Width) / 2), (int)((height - rc.Height) / 2));
pbImg.Bounds = rc;
}

How do I keep previously painted objects from disappearing when a new one is creating in a Windows Form Application?

My problem is that within my Windows Form Application, I want to draw an Ellipse everytime the mouse is clicked within a specific picture box, and I want the previously drawn ellipses to remain present in the picture box.
In its current state, once the mouse is clicked, the previously drawn ellipse will be replaced with the new one drawn at the cursor's new location.
Ball.Paint draws an ellipse.
Here is the relevant code to the problem:
private Ball b;
private void pbField_Paint(object sender, PaintEventArgs e)
{
if (b != null)
b.Paint(e.Graphics);
}
private void pbField_MouseClick(object sender, MouseEventArgs e)
{
int width = 10;
b = new Ball(new Point(e.X - width / 2, e.Y - width / 2), width);
Refresh();
}
If there is any more needed code or information I am able to provide it.
You need some sort of data structure to store prior ellipses. One possible solution is below:
private List<Ball> balls = new List<Ball>(); // Style Note: Don't do this, initialize in the constructor. I know it's legal, but it can cause issues with some code analysis tools.
private void pbField_Paint(object sender, PaintEventArgs e)
{
if (b != null)
{
foreach(Ball b in balls)
{
b.Paint(e.Graphics);
}
}
}
private void pbField_MouseClick(object sender, MouseEventArgs e)
{
int width = 10;
b = new Ball(new Point(e.X - width / 2, e.Y - width / 2), width);
balls.Add(b);
Refresh();
}
If you want more than one ball to be painted, you need to keep track of a list of balls, rather than just b. Every time the control is refreshed, it is expected to redraw all its contents. That means that in pbField_Paint, you need to be ready to draw as many balls as have been added to the scene.

Customer progress bar not filling properly

I created a simple progress bar for my C# Forms Application. It loads and functions properly with the sole exception that it doesn't fill completely by the time it gets to 100% It needs to go to 115% to complete. I have no idea why its doing this. Any insight, you could be my hero. Here is the code:
private void CustomProgressBar()
{
picBoxPB.Visible = true;
width = picBoxPB.Width;
height = picBoxPB.Height;
Unit = width / 100;
complete = 0;
bmp = new Bitmap(width, height);
t.Interval = 30;
t.Tick += new EventHandler(this.t_Tick);
t.Start();
}
private void t_Tick(object sender, EventArgs e)
{
graph = Graphics.FromImage(bmp);
graph.Clear(Color.DarkGoldenrod);
graph.FillRectangle(Brushes.ForestGreen, new Rectangle(0,0,(int)(complete * Unit),height));
graph.DrawString(complete + "%", new Font("Papyrus", height / 3), Brushes.Black, new PointF(width / 2 - height, height / 10));
picBoxPB.Image = bmp;
complete++;
if (complete>100)
{
graph.Dispose();
t.Stop();
}
}
Without any more information all I can see is that you are incrementing the complete variable after you draw the graph.
A wild guess is you move the complete to the top of the method such as
private void t_Tick(object sender, EventArgs e)
{
complete++;
graph = Graphics.FromImage(bmp);
graph.Clear(Color.DarkGoldenrod);
graph.FillRectangle(Brushes.ForestGreen, new Rectangle(0,0,(int)(complete * Unit),height));
graph.DrawString(complete + "%", new Font("Papyrus", height / 3), Brushes.Black, new PointF(width / 2 - height, height / 10));
picBoxPB.Image = bmp;
if (complete>100)
{
graph.Dispose();
t.Stop();
}
}
This assumption is made bearing no understanding of the Unit field (which appears to be a percentage of the Width field (not shown).
You initialize Unit to be width / 100 that's ok, but you do it in CustomProgressBar() and not where it is used.
If your control is used elsewhere, it will probably be resized. So the width will change. But you did not update Unit accordingly (nether your Bitmap object).
Yo have two option:
Listen for size change and update Unit when the event occurs.
Move your Unit variable in your t_tick function and compute it each time.
The second option cost a little more in time but the code readability is way better.
If you did not use the Unit variable elsewhere, choose the second option.
Also, if your goal is to draw a custom control, you should learn on how to overload Control.OnPaint, using a bitmap is not the solution here.

C# transparent image above 2 images

I have make a playbar for my new MP3 player but I have a problem as shown in attachement
and this is my simple code :
public void setpercent(int percent)
{
pic1.Width = pic2.Width / 100 * percent;
pic3.Left = pic1.Width - ( pic3.Width /2);
}
I thought to use Graphics but I can't move it after.
and the method of ".Parent" doesn't work in this case.
In the Load() event of your Form, modify the Region() property of pic3 so that it becomes circular instead of rectangular. This can be done by construction a GraphicsPath and adding an Ellipse to it. If this doesn't line up properly with your image, then manually determine the correct bounding box for the ellipse and use that instead:
private void Form1_Load(object sender, EventArgs e)
{
System.Drawing.Drawing2D.GraphicsPath GP = new System.Drawing.Drawing2D.GraphicsPath();
GP.AddEllipse(pic3.ClientRectangle);
pic3.Region = new Region(GP);
}

Lag when using graphics.drawline in winforms/C#

Beginner coder here getting into C#.. I'm making a program that involves drawing. Basically whenever I'm moving my mouse to draw, the actual line on the image appears delayed - and it's more.. straight, than it's supposed to be. It used to work fine, but at some point I guess something went wrong - can't remember what I did at the time so it's hard to retrace.. I tried to replicate just the drawing part of the program in a new solution, and it seems to work fine..
I'd post the .exe file so you can see what I mean but I'm not sure if we're allowed to post executables around here.
Edit: I've confirmed that the code works fine, look at the answer by sa_ddam213 for an example of the code. It seems as though it works fine in other peoples computers, so I'm completely confused.
Yo are creating a new Graphic and Pen object with every mouse move event, this will be a lot slower than creating these variables once in the Mouse_Down event.
Something like this may be a bit faster.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
b = new Bitmap(this.Width, this.Height);
}
private Graphics _graphics;
private Pen _pen;
private int pX = 0;
private int pY = 0;
private bool paint = false;
private Bitmap b;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
pX = e.X;
pY = e.Y;
_graphics = Graphics.FromImage(b);
_pen= new Pen(Color.Black, 3);
paint = true;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (paint)
{
_graphics.DrawLine(_pen, pX, pY, e.X, e.Y);
pictureBox1.BackgroundImage = b;
pictureBox1.Refresh();
pX = e.X;
pY = e.Y;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
paint = false;
_graphics.Dispose();
_pen.Dispose();
}
}
Instead of pictureBox1.Invalidate(), try using pictureBox1.Refresh()
You might also need to move it AFTER the pictureBox1.BackgroundImage = b
Also, in your MouseDown, you need to set this.Capture = true, and in your MouseUp you should set this.Capture = false. If you don't do that and you release the mouse button while your mouse cursor is over a different application, your's will never receive the MouseUp message.
May be It's a problem with your VGA . Check with another PC and let us know

Categories

Resources