Reputation: 9
I'm currently trying to implement alternating colors using Threads for a Graphics object on a Windows Form in C#.
The code to draw a line works as follows:
public void DrawLine(int toX,int toY) //draw line
{
g.DrawLine(currPen, xPos, yPos, toX, toY);
xPos = toX;
yPos = toY;
}
The variables assigned at the top of the Canvass class are as follows:
Graphics g;
public static Pen Red, Blue, Green, redGreen;
public static int xPos, yPos;
public static Pen currPen;
public static Thread newThread;
public static bool flag = false;
public static bool running = false;
The method of thread so far changes the color of the box which will be used to turn on flashing colors (This method is currently inside another class and will interact with static void's inside the Canvass class):
private void pictureBox6_Click(object sender, EventArgs e)
{
running = !running;
}
public void redgreenMethod()
{
while (true)
{
if (running != true) continue;
if (flag == false)
{
flag = true;
pictureBox6.BackColor = Color.Red;
}
else
{
pictureBox6.BackColor = Color.Green;
flag = false;
}
Thread.Sleep(500);
}
}
My issue is, I'm not too sure how to implement the threading onto a line on the Graphics canvass.. As seen in the Threads method, it interacts with the background colour of a pictureBox, whereas this kind of property isn't available for a line being drawn.
How do I approach this?
Upvotes: 0
Views: 219
Reputation: 112259
GDI+ (that's the library you are using when drawing in a WinForms application) does not store the things you are drawing as objects but only sets pixel colors.
You must create your own object model to have access to these objects. Example
public abstract class Shape
{
public Pen Pen { get; set;}
public abstract void Draw(Graphics g);
}
public class Line : Shape
{
public Point From { get; set; }
public Point To { get; set; }
public override void Draw(Graphics g)
{
g.DrawLine(Pen, From, To);
}
}
... other shapes
Then you would create a list of shapes
private readonly List<Shape> Shapes = new();
and in the Paint event handler you draw the graphic
private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
foreach (Shape shape in Shapes) {
shape.Draw(g);
}
}
Now, instead of directly drawing things you would add new shapes to the list of shapes and invalidate the picture box to have it redrawn
public void DrawLine(int toX,int toY) //draw line
{
Shapes.Add(
new Line {
Pen = currPen,
From = new Point(xPos, yPos),
To = new Point(toX, toY)
}
);
xPos = toX;
yPos = toY;
pictureBox1.Invalidate();
}
You can user a Timer object to time and trigger your animation. Note that the Timer.Tick
event is raised in the UI thread, so that you will not run into multithreading issues.
See also this answer to the How to Draw Lines with delay? question.
It is important not to draw directly and to delegate this work to the Paint event handler, because this paint event might also be triggered, e.g., when another window on top of your application is closed. On the other hand, e.g., when your application is iconized, this event will not be triggered.
You can also reduce flicker by setting pictureBox1.DoubleBuffered = true;
.
Upvotes: 2
Reputation: 21
What your looking for is the Timer object https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=windowsdesktop-6.0
add it to your win forms using the toolbox enable it in the properties and add the event by double clicking on it
private void pictureBox1_Click(object sender, EventArgs e)
{
running = !running;
if (running)
iter = 0
else
iter = -1
}
private void timer1_Tick(object sender, EventArgs e)
{
switch (iter)
{
case 0:
pictureBox1.BackColor = Color.Red;
iter++;
break;
case 1:
pictureBox1.BackColor = Color.Green;
iter = 0;
break;
}
}
don't forget to add your iterator int to your class
private int iter = 0;
Upvotes: 2