How to draw a static length line at random y values - c#

I'm trying to create a mini game where you must use a "turret" to shoot at a target. Currently I'm stuck on my target line. My aim is to get it to spawn at a set X value but for it to spawn at a random Y value everytime I press the redraw button. However I'm having trouble with my code. Instead of the line always being the same length and spawning at random Y values, it seems to be spawning at random lengths and at random Y values. If anyone could help me figure out where I have gone wrong that would be perfect.
The relevant variables are xTarget, yTarget (the first y value of the line) yTargetEnd (the second y value of the line) targetLine (the static length of the line) and the DrawLine() method call for targetLine.
In my head the way I have it set up makes sense but it doesn't seem to be doing what I expect. I try to set yTarget to a random number and then set yTargetEnd to yTarget + targetLine, so it should always be 50 in length, but that doesn't seem to work at all?
public partial class Form1 : Form
{
Random rnd = new Random { };
double xEnd = 0;
double yEnd = 0;
double xOrigin = 30;
double yOrigin = 450;
double xFire;
double yFire;
double xTarget = 500;
double yTarget;
double yTargetEnd;
double xHor = 30;
double yHor = 350;
double xVert = 130;
double yVert = 450;
double fireLine = 750;
double lineLength = 50;
double targetLine = 50;
public Form1()
{
xEnd = xOrigin + lineLength;
yEnd = yOrigin;
xFire = xOrigin + fireLine;
yFire = yOrigin;
InitializeComponent();
}
private void LineDrawEvent(object sender, PaintEventArgs paint)
{
Graphics drawSurface = paint.Graphics;
Pen turretLine = new Pen(Color.Blue);
Pen graphHorizontal = new Pen(Color.Red);
Pen graphVertical = new Pen(Color.Red);
Pen firedLine = new Pen(Color.Blue);
Pen targetLine = new Pen(Color.Black);
float[] dashValues = { 5, 2 };
firedLine.DashPattern = dashValues;
drawSurface.DrawLine(graphVertical, (int)xOrigin, (int)yOrigin, (int)xHor, (int)yHor);
drawSurface.DrawLine(graphHorizontal, (int)xOrigin, (int)yOrigin, (int)xVert, (int)yVert);
drawSurface.DrawLine(firedLine, (int)xOrigin, (int)yOrigin, (int)xFire, (int)yFire);
drawSurface.DrawLine(targetLine, (int)xTarget, (int)yTarget, (int)xTarget, (int)yTargetEnd);
double angleInRadians = ConvertDegsToRads((double)trckBarAngle.Value);
xEnd = xOrigin + lineLength * Math.Cos(angleInRadians / 2.0);
yEnd = yOrigin - lineLength * Math.Sin(angleInRadians / 2.0);
drawSurface.DrawLine(turretLine, (int)xOrigin, (int)yOrigin, (int)xEnd, (int)yEnd);
this.Refresh();
}
private void trckBarAngle_Scroll(object sender, EventArgs e)
{
lblAngle.Text = "Angle is:" + Convert.ToString((double)trckBarAngle.Value / 2.0);
}
private double ConvertDegsToRads(double degrees)
{
return degrees * (Math.PI / 180.0);
}
private void btnFire_Click(object sender, EventArgs e)
{
double angleInDegrees = trckBarAngle.Value;
double angleInRadians = ConvertDegsToRads(angleInDegrees);
xFire = xOrigin + fireLine * Math.Cos(angleInRadians / 2.0);
yFire = yOrigin - fireLine * Math.Sin(angleInRadians / 2.0);
this.Refresh();
}
private void btnRedraw_Click(object sender, EventArgs e)
{
yTargetEnd = yTarget - targetLine;
yTarget = rnd.Next(100, 500);
}
}
Designer-generated code:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblAngle = new System.Windows.Forms.Label();
this.trckBarAngle = new System.Windows.Forms.TrackBar();
this.btnFire = new System.Windows.Forms.Button();
this.btnRedraw = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.trckBarAngle)).BeginInit();
this.SuspendLayout();
//
// lblAngle
//
this.lblAngle.AutoSize = true;
this.lblAngle.Location = new System.Drawing.Point(13, 13);
this.lblAngle.Name = "lblAngle";
this.lblAngle.Size = new System.Drawing.Size(46, 17);
this.lblAngle.TabIndex = 0;
this.lblAngle.Text = "label1";
//
// trckBarAngle
//
this.trckBarAngle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.trckBarAngle.LargeChange = 10;
this.trckBarAngle.Location = new System.Drawing.Point(732, 42);
this.trckBarAngle.Maximum = 360;
this.trckBarAngle.Minimum = -360;
this.trckBarAngle.Name = "trckBarAngle";
this.trckBarAngle.Orientation = System.Windows.Forms.Orientation.Vertical;
this.trckBarAngle.Size = new System.Drawing.Size(56, 495);
this.trckBarAngle.TabIndex = 1;
this.trckBarAngle.Scroll += new System.EventHandler(this.trckBarAngle_Scroll);
//
// btnFire
//
this.btnFire.Location = new System.Drawing.Point(632, 13);
this.btnFire.Name = "btnFire";
this.btnFire.Size = new System.Drawing.Size(75, 23);
this.btnFire.TabIndex = 2;
this.btnFire.Text = "Fire";
this.btnFire.UseVisualStyleBackColor = true;
this.btnFire.Click += new System.EventHandler(this.btnFire_Click);
//
// btnRedraw
//
this.btnRedraw.Location = new System.Drawing.Point(713, 13);
this.btnRedraw.Name = "btnRedraw";
this.btnRedraw.Size = new System.Drawing.Size(75, 23);
this.btnRedraw.TabIndex = 2;
this.btnRedraw.Text = "Redraw";
this.btnRedraw.UseVisualStyleBackColor = true;
this.btnRedraw.Click += new System.EventHandler(this.btnRedraw_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 549);
this.Controls.Add(this.btnRedraw);
this.Controls.Add(this.btnFire);
this.Controls.Add(this.trckBarAngle);
this.Controls.Add(this.lblAngle);
this.Name = "Form1";
this.Text = "Form1";
this.Paint += new System.Windows.Forms.PaintEventHandler(this.LineDrawEvent);
((System.ComponentModel.ISupportInitialize)(this.trckBarAngle)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label lblAngle;
private System.Windows.Forms.TrackBar trckBarAngle;
private System.Windows.Forms.Button btnFire;
private System.Windows.Forms.Button btnRedraw;
}

The basic issue comes down to the code simply not matching what you intended. You wrote:
I try to set yTarget to a random number and then set yTargetEnd to yTarget + targetLine
But that's not actually what the code does. Your code does those two things in the opposite order. So the yTargetEnd value is always based on the previous target location, not the newly selected one.
Your code should look like this instead:
private void btnRedraw_Click(object sender, EventArgs e)
{
yTarget = rnd.Next(100, 500);
yTargetEnd = yTarget - targetLine;
}
I.e. write the code so that it matches your specification. :)
Having answered that, there are some other important issues to address in your code:
First and foremost, you are updating the screen in completely the wrong way. You should not call Refresh(), Update(), or Invalidate() from a Paint event handler. Instead, you should have separate logic somewhere, e.g. based on a timer, background thread, etc. that handles control of the state of the data (i.e. your game world), and which invalidates the relevant window or control(s) if and when the data changes in a way that would require them to be redrawn.
Also, you should use the stock Pen objects when possible, and when not possible, either dispose of your custom Pen object or store it somewhere for reuse.
For example:
private static readonly Pen _firedLine =
new Pen(Color.Blue) { DashPattern = new [] { 5f, 2f } };
private void LineDrawEvent(object sender, PaintEventArgs paint)
{
Graphics drawSurface = paint.Graphics;
Pen turretLine = Pens.Blue;
Pen graphHorizontal = Pens.Red;
Pen graphVertical = Pens.Red;
Pen targetLine = Pens.Black;
drawSurface.DrawLine(graphVertical, (int)xOrigin, (int)yOrigin, (int)xHor, (int)yHor);
drawSurface.DrawLine(graphHorizontal, (int)xOrigin, (int)yOrigin, (int)xVert, (int)yVert);
drawSurface.DrawLine(_firedLine, (int)xOrigin, (int)yOrigin, (int)xFire, (int)yFire);
drawSurface.DrawLine(targetLine, (int)xTarget, (int)yTarget, (int)xTarget, (int)yTargetEnd);
double angleInRadians = ConvertDegsToRads((double)trckBarAngle.Value);
xEnd = xOrigin + lineLength * Math.Cos(angleInRadians / 2.0);
yEnd = yOrigin - lineLength * Math.Sin(angleInRadians / 2.0);
drawSurface.DrawLine(turretLine, (int)xOrigin, (int)yOrigin, (int)xEnd, (int)yEnd);
}

Related

Foreach stops finding dynamically created controls after some amount of time. Resizing window makes it work again. Why? How can I fix it?

I'm a fairly new c# programmer currently in class and I've been doing this side project that (kind of) accurately represents bouncing balls. I also want it to be able to handle balls that are created on mouse click. I've done all of this and it works, for a few seconds. The original ball always works and always bounces/rolls. Dynamically created balls that are created on mouse click will hop and roll for a little but then they all freeze(not all at the same time) as the foreach statement I use stops recognizing that they are there. Weirdly enough when the window is resized using the bottom they start to work again. Am I missing something or is this a bug I can't fix? Here's the form code and the designer code. If you need anything else let me know.
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 testBouncingBallMulti
{
public partial class Form1 : Form
{
const int INITY = 0;
const int INITX = 3;
const int GRAVITY = 1;
public Form1()
{
InitializeComponent();
this.ball0.Tag = new Point(INITX, INITY);
}
byte counter = 1;
private void createBall(object sender, MouseEventArgs e)
{
Label l = new Label();
l.BackColor = System.Drawing.Color.Black;
l.Location = new Point(MousePosition.X - this.Left, MousePosition.Y - this.Top);
this.Controls.Add(l);
l.Size = new System.Drawing.Size(15, 15);
l.Tag = new Point(INITX, INITY);
l.Name = "ball" + Convert.ToString(counter);
counter++;
}
private void physicsLoop1(object sender, EventArgs e)
{
foreach (var ball in this.Controls.OfType<Label>())
{
moveBall(ball);
bounce(ball);
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0, 0));
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
return;
}
velocities.Y = velocities.Y + GRAVITY;
ball.Tag = new Point(velocities.X, velocities.Y);
}
}
private void moveBall(Control ball)
{
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0,0));
ball.Location = new Point(ball.Location.X + velocities.X, ball.Location.Y + velocities.Y);
}
private void bounce(Control ball)
{
Point? init = ball.Tag as Point?;
Point velocities = init.GetValueOrDefault(new Point(0,0));
if (ball.Location.Y >= ClientSize.Height - 15)
{
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
}
else if (ball.Location.Y > ClientSize.Height - 15)
{
ball.Location = new Point(ball.Location.X, ClientSize.Height - 15);
velocities.Y = -(velocities.Y - 2);
}
}
if (ball.Location.X >= ClientSize.Width - 15)
{
velocities.X = -(velocities.X);
}
if (ball.Location.X <= 0)
{
ball.Location = new Point(0, ball.Location.Y);
velocities.X = -(velocities.X);
}
ball.Tag = new Point(velocities.X, velocities.Y);
}
private void physicsLoop2(object sender, EventArgs e)
{
}
private void physicsLoop3(object sender, EventArgs e)
{
}
}
}
and the designer code:
namespace testBouncingBallMulti
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.ball0 = new System.Windows.Forms.Label();
this.timer2 = new System.Windows.Forms.Timer(this.components);
this.timer3 = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 20;
this.timer1.Tick += new System.EventHandler(this.physicsLoop1);
//
// ball0
//
this.ball0.BackColor = System.Drawing.Color.Black;
this.ball0.Location = new System.Drawing.Point(12, 24);
this.ball0.Name = "ball0";
this.ball0.Size = new System.Drawing.Size(15, 15);
this.ball0.TabIndex = 0;
//
// timer2
//
this.timer2.Tick += new System.EventHandler(this.physicsLoop2);
//
// timer3
//
this.timer3.Tick += new System.EventHandler(this.physicsLoop3);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(504, 396);
this.Controls.Add(this.ball0);
this.Name = "Form1";
this.Text = "Form1";
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.createBall);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label ball0;
private System.Windows.Forms.Timer timer2;
private System.Windows.Forms.Timer timer3;
}
}
Replace return with continue in
if (velocities.Y == 0 && ball.Location.Y >= ClientSize.Height - 15)
{
return;
}
You need to give some breathing room in your label move procedure to repaint the form. Either use best practice and place your painting into a thread or at least process a windows message or two in your timer loop.

How to change parameter continuously in Paint event

I make a program for interactively display the foot pressure.
Bigger pressure is indicated by bigger diameter of the ellipse.
I work on the label's Paint events.
Here is the event handler:
private void labelGrad2_Paint(object sender, PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
int weight_forefoot_left;
weight_forefoot_left = Convert.ToInt32( l_press05 + l_press06 + l_press07); // summing forefoot pressure sensors
size_pressure_forefootLeft = (weight_forefoot_left * 2 ); //2 is based on trial and error for display only
//protection diameter
if (size_pressure_forefootLeft == 0 ){
size_pressure_forefootLeft = 1;
}
if (size_pressure_forefootLeft > 26) {
size_pressure_forefootLeft = 26; //protection diameter display
}
//---variable sizing---------
labelGrad_forefoot_left.Font = new Font("MS UI Gothic", size_pressure_forefootLeft);
int x_forefoot = (pictureBox_CoP.Location.X - (labelGrad_forefoot_left.Size.Width / 2)) + 58; //location of left x corner in pictureBox_CoP
int y_forefoot = (pictureBox_CoP.Location.Y - (labelGrad_forefoot_left.Size.Height / 2)) + 100; //location of left y corner in pictureBox_CoP
labelGrad_forefoot_left.Location = new Point(x_forefoot, y_forefoot);
labelGrad_forefoot_left.BackColor = Color.White;
gp.AddEllipse(labelGrad_forefoot_left.ClientRectangle);
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.CenterPoint = new PointF(labelGrad_forefoot_left.ClientRectangle.Width / 2,
labelGrad_forefoot_left.ClientRectangle.Height / 2);
pgb.CenterColor = Color.Red;
pgb.SurroundColors = new Color[] { Color.Blue};
pgb.SetBlendTriangularShape(.5f, 1.0f);
pgb.FocusScales = new PointF(0f, 0f);
e.Graphics.FillPath(pgb, gp);
pgb.Dispose();
gp.Dispose();
}
private void timer1_Tick(object sender, EventArgs e)
{
size_pressure_forefootLeft = trackBar1.Value;
textBox4.Text = Convert.ToString( size_pressure_forefootLeft);
this.Invalidate();
this.Update();
}
I need to change variable "size_pressure_forefootLeft" continuously.
I have tried using timer event but timer event do not have PaintEventArgs, there is an error in line e.Graphics.FillPath(pgb, gp).
How to handle this?

Relocating panels gets mixed up WinForms

I am creating an application using C# and WinForms. I have three panels. Two of them are fairly large and are currently positioned side by side. I am trying to implement a feature for users who have smaller screens or wish to have the form as a smaller size. I am relocating the right panel underneath the middle one and re-centering them in the form.
The issue that I have though is when the user scrolls down and re-sizes the form again the two middle panels (the big ones) move lower in the form leaving a chunk of blank space at the top.
My code is fairly simple
namespace ResizeCheck
{
public partial class Form1 : Form
{
Point originalLeft, originalRight;
bool flag = false;
public Form1()
{
InitializeComponent();
originalLeft = leftInnerPanel.Location;
originalRight = rightInnerPanel.Location;
}
private void vertical()//move the right panel under the left one
{
leftInnerPanel.Location = new Point(this.Width / 2 - leftInnerPanel.Width / 2, 5);
if (leftInnerPanel.Location.X <= buttonPanel.Location.X + buttonPanel.Width)
{
leftInnerPanel.Location = new Point(buttonPanel.Location.X + buttonPanel.Width, leftInnerPanel.Location.Y);
}
rightInnerPanel.Location = new Point(leftInnerPanel.Location.X, leftInnerPanel.Height + 10);
MessageBox.Show("inside vertical " + leftInnerPanel.Location.Y);
}
private void horizontal()//relocate to their original horizontal position
{
leftInnerPanel.Location = new Point(buttonPanel.Location.X + buttonPanel.Width + 10, 5);
if (leftInnerPanel.Location.X <= buttonPanel.Location.X + buttonPanel.Width)
{
leftInnerPanel.Location = new Point(buttonPanel.Location.X + buttonPanel.Width, leftInnerPanel.Location.Y);
}
rightInnerPanel.Location = new Point(leftInnerPanel.Location.X + leftInnerPanel.Width + 20, 5);
MessageBox.Show("inside horizontal " + leftInnerPanel.Location.Y);
}
private void Form1_SizeChanged(object sender, EventArgs e)//handler for when the form is resized by the user
{
if ((leftInnerPanel.Width + rightInnerPanel.Width + buttonPanel.Width) >= this.Width)
{
vertical();
//flag = true;
}
else if ((leftInnerPanel.Width + rightInnerPanel.Width + buttonPanel.Width) + 50 < this.Width)
{
horizontal();
//flag = false;
}
}
}
}
My designer code is also fairly simple.
namespace ResizeCheck
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.rightInnerPanel = new System.Windows.Forms.Panel();
this.leftInnerPanel = new System.Windows.Forms.Panel();
this.buttonPanel = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// rightInnerPanel
//
this.rightInnerPanel.BackColor = System.Drawing.Color.Yellow;
this.rightInnerPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.rightInnerPanel.Location = new System.Drawing.Point(887, 13);
this.rightInnerPanel.Name = "rightInnerPanel";
this.rightInnerPanel.Size = new System.Drawing.Size(662, 936);
this.rightInnerPanel.TabIndex = 1;
//
// leftInnerPanel
//
this.leftInnerPanel.BackColor = System.Drawing.Color.Yellow;
this.leftInnerPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.leftInnerPanel.Location = new System.Drawing.Point(219, 13);
this.leftInnerPanel.Name = "leftInnerPanel";
this.leftInnerPanel.Size = new System.Drawing.Size(662, 936);
this.leftInnerPanel.TabIndex = 0;
//
// buttonPanel
//
this.buttonPanel.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
this.buttonPanel.Location = new System.Drawing.Point(13, 13);
this.buttonPanel.Name = "buttonPanel";
this.buttonPanel.Size = new System.Drawing.Size(200, 258);
this.buttonPanel.TabIndex = 1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoScroll = true;
this.ClientSize = new System.Drawing.Size(1008, 601);
this.Controls.Add(this.rightInnerPanel);
this.Controls.Add(this.buttonPanel);
this.Controls.Add(this.leftInnerPanel);
this.Name = "Form1";
this.Text = "Form1";
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.SizeChanged += new System.EventHandler(this.Form1_SizeChanged);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel rightInnerPanel;
private System.Windows.Forms.Panel leftInnerPanel;
private System.Windows.Forms.Panel buttonPanel;
}
}
You are not accounting for how much the form has scrolled, you are simply plopping the panel at the top of the visible client area.
Change your Vertical() method thusly:
private void vertical()
{
leftInnerPanel.Location = new Point(this.Width / 2 - leftInnerPanel.Width / 2, 5 - VerticalScroll.Value);
if (leftInnerPanel.Location.X <= buttonPanel.Location.X + buttonPanel.Width)
leftInnerPanel.Location = new Point(buttonPanel.Location.X + buttonPanel.Width, leftInnerPanel.Location.Y);
rightInnerPanel.Location = new Point(leftInnerPanel.Location.X, leftInnerPanel.Bottom + 10);
}
Two points to note:
1) use of the VerticalScroll member.
2) use of Bottom instead of Height (and Right instead of Width in the Horizontal() method).
In vertical()
since you are not adjusting the Y of leftInnerPanel, we can straightaway use the buttonPanel.Top
leftInnerPanel.Location = new Point(this.Width / 2 - leftInnerPanel.Width / 2, buttonPanel.Top);
Secondly,
//rightInnerPanel.Location = new Point(leftInnerPanel.Location.X, leftInnerPanel.Height + 10);
rightInnerPanel.Location = new Point(leftInnerPanel.Location.X, leftInnerPanel.Bottom + 10);

Graphical Timer control not working correctly

This is a custom control I have made, a graphical timer, however, it is not working correctly. As the time left decreases a pie is filled to represent the amount of time left decreasing, but it is decreasing with unexpected angles (which are output to the listbox for debugging purposes). How do I get it to work correctly? Also I'm very new to making Custom Controls (this is my first) so any pointers on good coding guidelines, what not to do, etc, would be very helpful.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TestCustomControl
{
class GraphicalTimer : Control
{
public Color Timer { get; set; }
public Color TimerEmpty { get; set; }
public Color BorderColor { get; set; }
private Timer t;
public int MaxTime { get; set; }
private int timeElapsed = 0;
public GraphicalTimer()
{
DoubleBuffered = true;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
t = new Timer();
t.Interval = 1000;
t.Tick += t_Tick;
}
public void Start()
{
t.Start();
}
public void Stop()
{
t.Stop();
}
public void Reset()
{
timeElapsed = 0;
Invalidate();
}
void t_Tick(object sender, EventArgs e)
{
timeElapsed += 1;
if (timeElapsed == MaxTime)
{
t.Dispose();
}
Invalidate();
}
private float getAngleFromTime()
{
if (timeElapsed == 0)
{
return 0;
}
else
{
MainWindow.lb.Items.Add((360 / (MaxTime / timeElapsed)).ToString());
return (360 / (MaxTime / timeElapsed));
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle rc = ClientRectangle;
g.FillEllipse(new SolidBrush(Timer), rc);
g.FillPie(new SolidBrush(TimerEmpty), rc, -90, getAngleFromTime());
g.DrawEllipse(new Pen(BorderColor, 4), rc);
Font font = new Font("Arial", (float)rc.Height * 0.4f, FontStyle.Bold, GraphicsUnit.Pixel);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString((MaxTime - timeElapsed).ToString("D2"), font, new SolidBrush(Color.Black), new RectangleF((rc.Width - rc.Width / 2) / 2, (rc.Height - rc.Height / 2) / 2, rc.Width * 0.7f, rc.Height * 0.7f));
}
}
class MainWindow : Form
{
GraphicalTimer gt;
Button startButton;
Button stopButton;
Button resetButton;
public static ListBox lb;
public MainWindow()
{
this.Text = "Test Application";
gt = new GraphicalTimer();
gt.MaxTime = 20;
gt.BorderColor = Color.BurlyWood;
gt.Timer = Color.Aqua;
gt.TimerEmpty = Color.White;
gt.Top = 10;
gt.Left = 10;
gt.Width = 50;
gt.Height = 50;
this.Controls.Add(gt);
startButton = new Button();
startButton.Top = 70;
startButton.Left = 30;
startButton.AutoSize = true;
startButton.Text = "Start Timer";
startButton.Click += startButton_Click;
this.Controls.Add(startButton);
stopButton = new Button();
stopButton.Top = 70;
stopButton.Left = startButton.Right + 10;
stopButton.AutoSize = true;
stopButton.Text = "Stop Timer";
stopButton.Click += stopButton_Click;
this.Controls.Add(stopButton);
resetButton = new Button();
resetButton.Top = 70;
resetButton.Left = stopButton.Right + 10;
resetButton.AutoSize = true;
resetButton.Text = "Reset Timer";
resetButton.Click += resetButton_Click;
this.Controls.Add(resetButton);
lb = new ListBox();
lb.Top = resetButton.Bottom + 10;
lb.Left = 10;
lb.Width = this.ClientSize.Width - 20;
lb.Height = this.ClientSize.Height - lb.Top - 10;
this.Controls.Add(lb);
}
void resetButton_Click(object sender, EventArgs e)
{
gt.Reset();
}
void stopButton_Click(object sender, EventArgs e)
{
gt.Stop();
}
void startButton_Click(object sender, EventArgs e)
{
gt.Start();
}
}
class StartClass
{
static void Main()
{
MainWindow form = new MainWindow();
Application.EnableVisualStyles();
Application.Run(form);
}
}
}
You are using integers to calculate the angle, and in a way that can cause quite some jitters.
360 / (MaxTime / timeElapsed) will first evaluate temp = MaxTime / timeElapsed in integer arithmetics, and then 360 / temp also using integer division.
Try using floating point numbers for the calculation and then converting the final result into an integer value if you need it that way. Even writing 360 * timeElapsed / MaxTime might reduce the artifacts as you then first multiply 360 * timeElapsed which is accurate (unless timeElapsed is very large).
The method of calculating AngleForTime is wrong. Just replace your method with below code and it should do your job.
;)
private float getAngleFromTime()
{
if (timeElapsed == 0)
{
return 0;
}
else
{
MainWindow.lb.Items.Add((360*timeElapsed) / MaxTime ).ToString();
return (360*timeElapsed) / MaxTime ;
}
}

Windows Forms C# "Input string was not in a correct format"

I have a simple Windows Forms application which is used to calculate the solutions to a quadratic equation. Since it requires some values to be inputted into three different textboxes and then upon clicking a "Calculate" button, makes some calculations with the inputted values. Upon testing the application, and clicking the "Calculate" button prior to inputting any values, I get a Input string was not in a correct format This is due to trying to parse a non-existent value. Is there any way to avoid this? I tried to construct a conditional based on if the button was clicked and there was no values in the textboxes, to not do anything, but that didn't quite work. Here is my designer code:
namespace QuadraticSolver
{
partial class QuadraticSolver
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblPrompt = new System.Windows.Forms.Label();
this.lblA = new System.Windows.Forms.Label();
this.lblB = new System.Windows.Forms.Label();
this.lblC = new System.Windows.Forms.Label();
this.txtA = new System.Windows.Forms.TextBox();
this.txtB = new System.Windows.Forms.TextBox();
this.txtC = new System.Windows.Forms.TextBox();
this.btnCalculate = new System.Windows.Forms.Button();
this.lblSolutions = new System.Windows.Forms.Label();
this.txtSolution1 = new System.Windows.Forms.TextBox();
this.txtSolution2 = new System.Windows.Forms.TextBox();
this.chkImaginary = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// lblPrompt
//
this.lblPrompt.AutoSize = true;
this.lblPrompt.Location = new System.Drawing.Point(12, 9);
this.lblPrompt.Name = "lblPrompt";
this.lblPrompt.Size = new System.Drawing.Size(92, 13);
this.lblPrompt.TabIndex = 0;
this.lblPrompt.Text = "Enter Your Values";
//
// lblA
//
this.lblA.AutoSize = true;
this.lblA.Location = new System.Drawing.Point(12, 49);
this.lblA.Name = "lblA";
this.lblA.Size = new System.Drawing.Size(16, 13);
this.lblA.TabIndex = 1;
this.lblA.Text = "a:";
//
// lblB
//
this.lblB.AutoSize = true;
this.lblB.Location = new System.Drawing.Point(12, 85);
this.lblB.Name = "lblB";
this.lblB.Size = new System.Drawing.Size(16, 13);
this.lblB.TabIndex = 2;
this.lblB.Text = "b:";
//
// lblC
//
this.lblC.AutoSize = true;
this.lblC.Location = new System.Drawing.Point(12, 122);
this.lblC.Name = "lblC";
this.lblC.Size = new System.Drawing.Size(16, 13);
this.lblC.TabIndex = 3;
this.lblC.Text = "c:";
//
// txtA
//
this.txtA.Location = new System.Drawing.Point(34, 46);
this.txtA.Name = "txtA";
this.txtA.Size = new System.Drawing.Size(360, 20);
this.txtA.TabIndex = 4;
//
// txtB
//
this.txtB.Location = new System.Drawing.Point(34, 82);
this.txtB.Name = "txtB";
this.txtB.Size = new System.Drawing.Size(360, 20);
this.txtB.TabIndex = 5;
//
// txtC
//
this.txtC.Location = new System.Drawing.Point(34, 122);
this.txtC.Name = "txtC";
this.txtC.Size = new System.Drawing.Size(360, 20);
this.txtC.TabIndex = 6;
//
// btnCalculate
//
this.btnCalculate.Location = new System.Drawing.Point(175, 154);
this.btnCalculate.Name = "btnCalculate";
this.btnCalculate.Size = new System.Drawing.Size(75, 23);
this.btnCalculate.TabIndex = 7;
this.btnCalculate.Text = "Calculate!";
this.btnCalculate.UseVisualStyleBackColor = true;
this.btnCalculate.Click += new System.EventHandler(this.btnCalculate_Click);
//
// lblSolutions
//
this.lblSolutions.AutoSize = true;
this.lblSolutions.Location = new System.Drawing.Point(31, 226);
this.lblSolutions.Name = "lblSolutions";
this.lblSolutions.Size = new System.Drawing.Size(53, 13);
this.lblSolutions.TabIndex = 8;
this.lblSolutions.Text = "Solutions:";
//
// txtSolution1
//
this.txtSolution1.Location = new System.Drawing.Point(34, 242);
this.txtSolution1.Name = "txtSolution1";
this.txtSolution1.ReadOnly = true;
this.txtSolution1.Size = new System.Drawing.Size(165, 20);
this.txtSolution1.TabIndex = 9;
//
// txtSolution2
//
this.txtSolution2.Location = new System.Drawing.Point(222, 242);
this.txtSolution2.Name = "txtSolution2";
this.txtSolution2.ReadOnly = true;
this.txtSolution2.Size = new System.Drawing.Size(172, 20);
this.txtSolution2.TabIndex = 10;
//
// chkImaginary
//
this.chkImaginary.AutoSize = true;
this.chkImaginary.Location = new System.Drawing.Point(33, 189);
this.chkImaginary.Name = "chkImaginary";
this.chkImaginary.Size = new System.Drawing.Size(71, 17);
this.chkImaginary.TabIndex = 11;
this.chkImaginary.Text = "Imaginary";
this.chkImaginary.UseVisualStyleBackColor = true;
//
// QuadraticSolver
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(406, 285);
this.Controls.Add(this.chkImaginary);
this.Controls.Add(this.txtSolution2);
this.Controls.Add(this.txtSolution1);
this.Controls.Add(this.lblSolutions);
this.Controls.Add(this.btnCalculate);
this.Controls.Add(this.txtC);
this.Controls.Add(this.txtB);
this.Controls.Add(this.txtA);
this.Controls.Add(this.lblC);
this.Controls.Add(this.lblB);
this.Controls.Add(this.lblA);
this.Controls.Add(this.lblPrompt);
this.Name = "QuadraticSolver";
this.Text = "Quadratic Solver";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label lblPrompt;
private System.Windows.Forms.Label lblA;
private System.Windows.Forms.Label lblB;
private System.Windows.Forms.Label lblC;
private System.Windows.Forms.TextBox txtA;
private System.Windows.Forms.TextBox txtB;
private System.Windows.Forms.TextBox txtC;
private System.Windows.Forms.Button btnCalculate;
private System.Windows.Forms.Label lblSolutions;
private System.Windows.Forms.TextBox txtSolution1;
private System.Windows.Forms.TextBox txtSolution2;
private System.Windows.Forms.CheckBox chkImaginary;
}
}
Here is my program's code which does the actual data manipulation:
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 QuadraticSolver
{
public partial class QuadraticSolver : Form
{
public QuadraticSolver()
{
InitializeComponent();
}
private void btnCalculate_Click(object sender, EventArgs e)
{
if (txtA.Text == "" || txtB.Text == "" || txtC.Text == "")
{
string stringSol = "Please enter some values!";
txtSolution1.Text = stringSol;
txtSolution2.Text = stringSol;
}
double aValue = double.Parse(txtA.Text);
double bValue = double.Parse(txtB.Text);
double cValue = double.Parse(txtC.Text);
double solution1Double, solution2Double;
//Quadratic Formula: x = (-b +- sqrt(b^2 - 4ac)) / 2a
//Calculate discriminant
double insideSquareRoot = (bValue * bValue) - 4 * aValue * cValue;
if (insideSquareRoot < 0)
{
//No real solution
solution1Double = Double.NaN;
solution2Double = Double.NaN;
txtSolution1.Text = solution1Double.ToString();
txtSolution2.Text = solution2Double.ToString();
}
else if (insideSquareRoot == 0)
{
//One real solution
double sqrtOneSolution = Math.Sqrt(insideSquareRoot);
solution1Double = (-bValue + sqrtOneSolution) / (2 * aValue);
solution2Double = double.NaN;
txtSolution1.Text = solution1Double.ToString();
txtSolution2.Text = solution2Double.ToString();
}
else if (insideSquareRoot > 0)
{
//Two real solutions
double sqrtTwoSolutions = Math.Sqrt(insideSquareRoot);
solution1Double = (-bValue + sqrtTwoSolutions) / (2 * aValue);
solution2Double = (-bValue - sqrtTwoSolutions) / (2 * aValue);
txtSolution1.Text = solution1Double.ToString();
txtSolution2.Text = solution2Double.ToString();
}
}
}
}
You can use the double.TryParse method.
This is better than checking for == "" since it will tell you that you can not parse "Hello" as a double too.
You also need to return from the event handler if one of the TryParse returns false.
You may consider using a NumericUpDown control instead. You are guaranteed the input from this control will be numeric. All the code that is required is then:
double userNumber = myNumUpDown.Value;
You can even restrict the number input to ensure it falls within your defined range
myNumUpDown.Minimum = 300;
myNumUpDown.Maximum = 500;
Along with many other things. Finally, it even comes with up/down GUI controls so your users can be super lazy and enter the number with their mouse!
You can try this format,
replace the textbox1 with your textbox name.
int a = textbox1.Text != "" ? Convert.ToInt32(textbox1.Text) : 0;
int b = textbox2.Text != "" ? Convert.ToInt32(textbox2.Text) : 0;
int total = a + b;
you can reassign the result this way:
textbox3 = total.toString();
if (txtA.Text == "" || txtB.Text == "" || txtC.Text == "")
{
string stringSol = "Please enter some values!";
txtSolution1.Text = stringSol;
txtSolution2.Text = stringSol;
return;
}

Categories