C# Forms with negative minimum value in progress bar - c#

I need to show a progress bar that have negative values, but when i try to modify the value to a negative number, the c# gets an error (invalid property error). Does anyone knows any method to create a negative value progress bar?
HPHeroi.Maximum = CriacaoDePersonagem.protagonista.status.Vida;
HPHeroi.Minimum = -20;

Does anyone knows any method to create a negative value progress bar?
You cannot. From the documentation:
Exceptions
ArgumentException
The value specified for the property is less than 0.
Instead, what you need to do is offset the entire range of the progress bar to account for the negative value. E.g.:
HPHeroi.Maximum = CriacaoDePersonagem.protagonista.status.Vida + 20;
HPHeroi.Minimum = 0;
Then you need to also account for that when you set the current value of the ProgressBar, e.g.:
HPHeroi.Value = CriacaoDePersonagem.protagonista.status.ActualVida + 20;
The above strategy can be encapsulated in a UserControl that wraps a ProgressBar, exposing the various value-related properties but using an offset to ensure that the actual ProgressBar object doesn't see negative values.
For example:
public partial class ProgressBarWithNegativeValues : UserControl, INotifyPropertyChanged
{
private int _offset;
public ProgressBarWithNegativeValues()
{
InitializeComponent();
}
public int Minimum
{
get => progressBar1.Minimum + _offset;
set
{
int offsetChange = value - _offset;
_offset = value;
progressBar1.Maximum -= offsetChange;
_RaisePropertyChanged();
}
}
public int Maximum
{
get => progressBar1.Maximum + _offset;
set
{
progressBar1.Maximum = value - _offset;
_RaisePropertyChanged();
}
}
public int Value
{
get => progressBar1.Value + _offset;
set
{
progressBar1.Value = value - _offset;
_RaisePropertyChanged();
}
}
public int Step
{
get => progressBar1.Step;
set
{
progressBar1.Step = value;
_RaisePropertyChanged();
}
}
public void Increment(int step) => progressBar1.Increment(step);
public void PerformStep() => progressBar1.PerformStep();
public event PropertyChangedEventHandler PropertyChanged;
private void _RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I've omitted the *.Designer.cs code because that's just a single ProgressBar object dropped onto the UserControl with its Dock property set to Fill.
Note in the above that the ProgressBar.Minimum property is always left at 0, and the other properties offset accordingly, whether the minimum value is negative or positive. This keeps the code nice and simple instead of trying to deal with the negative and positive cases separately.

As Peter have stated, a progress bar can not hold a negative number. Therefore you can not use a Progress bar for this.
What I suggest, is instead of using a progress bar, use a track bar and disable it (set it's Enabled property to false).
Therefore, Imagine HPHeroi is a TrackBar object:
HPHeroi.Maximum = CriacaoDePersonagem.protagonista.status.Vida;
HPHeroi.Minimum = -20;
The code above will not result in a runtime error.
Keep in mind, there might be 2 more runtime errors that will occur when you reach the Maximum and Minimum values. To overcome them, add an if statement before each subtraction and addition of the HPHeroi's value (remember, it is a TrackBar now).
If statement example for subtraction:
if (HPHeroi.Value > HPHeroi.Minimum)
HPHeroi.Value--;
If statement example for addition:
if (HPHeroi.Value < HPHeroi.Maximum)
HPHeroi.Value++;
P.S. If you want, you can add a lable with "0" as content and put it under the line of the value of 0 in the track bar so that the user will know when the value is positive and when it is negative.

ProgressBar has a minimum value of 0, but the maximum can be quite high.
If you need it to represent a negative value, just use an offset.
int Offset = 20;
HPHeroi.Maximum = CriacaoDePersonagem.protagonista.status.Vida + Offset;
HPHeroi.Minimum = 0;
When you set the value of HPHeroi, simply add Offset to the new value
HPHeroi.Value = newvalue + Offset
newvalue can therefore be anything from -20 to CriacaoDePersonagem.protagonista.status.Vida, the range you are trying to specify, and your ProgressBar will show that.

Related

Comparing a Decimal within a Number Range

ok I am running into a problem. I have a decimal in a textbox that I am trying to Range between a set number. As you can see in the picture I have a value that is in the Full Scale, which updates the value on the CalCert with the decimal 2.9995 mV/V.
The issue here is the decimal value has to be plus/minus 30 of 3 mV. example. 2.9970 - 3.0030 that is the range. Anything outside the range I am needing it to trigger a warning dialog. My code that I was going to use I am not sure why its not working.
I am using if statements but the only error is in the ("3.0031") section.
double value = 0;
if (double.TryParse(TxtFullScale.Text, out value))
{
value /= 10000;
}
TxtCalFullScale.Text = string.Format("{0:#.0000}", value) + " " + "mV/V";
if (TxtFullScale.Text >= TxtFullScale.Text.ToString("3.0031"))
{
MessageBox.Show("Full Scale value is in a non conformence state.");
}
else if (TxtFullScale.Text <= TxtFullScale.Text.ToString("2.9970"))
{
MessageBox.Show("Full Scale value is in a non conformence state.");
}
I can take and make the code work with to a point with
if (TxtFullScale.Text == "3.0031")
{
MessageBox.Show("Full Scale value is in a non conformence state.");
}
else if (TxtFullScale.Text =="2.9970")
{
MessageBox.Show("Full Scale value is in a non conformence state.");
}
However if the range is put in the text as 3.0032 then it never shows the messagebox. What am I missing here?
So, first, you can't do math operations with strings on C#
var a = "3";
var b = "2";
Console.WriteLine(a+b);
Will result in 32!
Second, this line is a bit strange
TxtFullScale.Text >= TxtFullScale.Text.ToString("3.0031")
Should be like
TxtFullScale.Text >= "3.0031"
you need to parse the number as an decimal, then compare, like this:
public const double LIMIT_1 = 3.0031;
public static void Main()
{
var val = "3.0032";
var x = double.TryParse(val, out double dec);
if(dec >= LIMIT_1)
Console.WriteLine("yay");
}

How to stop calculating values on click event if there is no change in winform fields

I am newbie to programming. I am learning on my own and it's the end of my first week. I am creating a C# form to calculate insurance sum against user's requirements. On clicking Calculate Insurance Price button it calculates the total sum and shows it in TotalLabel. The form calculates and shows total accurately but only for the first time. For example if its 325.5 $ first time, if I click Calculate Insurance Price button again without changing credential in form like age, country, sick etc. it recalculates and shows 600$ and on every click it recalculates.
I want to only calculate the total sum in case any field was changed. If there are no changes in credentials it should not recalculate and if it recalculates TotalLabel value should not change which is not in my case.
Here is my code. Please suggest what's the mistake.
public partial class Mainform : Form
{
decimal baseinsuranceprice = 50.5m;
public Mainform()
{
InitializeComponent();
}
private void Insuranceprice_Click(object sender, EventArgs e)
{
int ag = int.Parse(Agetextbox.Text);
age(ag, baseinsuranceprice);
string coun = Countrycombobox.SelectedItem.ToString();
country(coun, baseinsuranceprice);
premiuminsurance(baseinsuranceprice);
Noclaim(baseinsuranceprice);
Sick(baseinsuranceprice);
decimal minimuminsuranceprice = 80.5m;
if (baseinsuranceprice < minimuminsuranceprice)
baseinsuranceprice = minimuminsuranceprice;
TotalLabel.Text = "$" + baseinsuranceprice.ToString();
}
public decimal age(int a, decimal b)
{
if (a > 30)
{
baseinsuranceprice = baseinsuranceprice + 50;
}
else
{
baseinsuranceprice = baseinsuranceprice + 100;
}
return baseinsuranceprice;
}
public decimal country(string a, decimal c)
{
if (a == "USA")
{
baseinsuranceprice = baseinsuranceprice + 40;
}
else
{
baseinsuranceprice = baseinsuranceprice + 50;
}
return baseinsuranceprice;
}
public decimal premiuminsurance(decimal f)
{
if (PremiumInsuranceRadioButtonYes.Checked)
baseinsuranceprice = baseinsuranceprice + 100;
return baseinsuranceprice;
}
public decimal Noclaim(decimal g)
{
if (NoClaimDiscountRadioButtonYes.Checked)
baseinsuranceprice -= 25;
else if (NoClaimDiscountRadioButtonNo.Checked)
baseinsuranceprice += 50;
return baseinsuranceprice;
}
public decimal Sick(decimal h)
{
if (SickCoverRadioButtonYes.Checked)
baseinsuranceprice += 50;
return baseinsuranceprice;
}
}
}
enter image description here
I believe your most significant mistake is that you are holding
decimal baseinsuranceprice = 50.5m;
as a field of your Mainform class. This is why you are getting new result every time you hit the button with the same parameters (after first calculation baseinsuranceprice is not 50.5m anymore).
The easiest solution is to "reset" this value to initial 50.5m one before every calculation.
My suggestion is to implement your price calculating algorithm as a pure function (maybe you even want to have it as a separate class, and maybe you'd like to write some tests for it), so you only give initial parameters to your function / class and it calculates you the result.
This way you won't have any dependency on your form's state. Moreover, in case you need to keep some base insurance price in your system, it's better to have it as a constant or even as a value read from the configuration.
In my opinion, there is nothing bad in recalculating the value if this calculation doesn't take too much time. In your case this is fine to launch the calculation on every click.

C# Accessing an array of labels from another form and altering their "Text" property

This is my first post. So..critique is always welcomed.
My question is straight forward just by looking at the title.
How can I use a loop to insert values to different labels by using their reference (get,set methods are in a different form)
What I've tried is to create an array with the references of the labels. The thing is.. it assigns the new values to the array rather than changing the reference which will change the label.
I find it a bit difficult to explain it further than that.
If you have any questions, I will try to answer them best to my knowledge
private void button1_Click(object sender, EventArgs e)
{
int numOfPeriods = Convert.ToInt32(cmbPeriods.Text)-1;
string initialString = cmbStartTime.Text; //Gets the input from combo box "cmbStartTime".
string newTime;
decimal dec = Convert.ToDecimal(TimeSpan.Parse(initialString).TotalHours); //Converts the set by user Lesson start time to a decimal value.
decimal dec2;
decimal lessonLength = 1; // Default lesson length is set to 1 hour.
TimeSpan time;
Test FormOpen = new Test();
string[] periodStartTimes = new string[9] //Loop referencing the labels on Form TEST
{
FormOpen.startTime,FormOpen.startTime2, FormOpen.startTime3, FormOpen.startTime4,
FormOpen.startTime5, FormOpen.startTime6, FormOpen.startTime7, FormOpen.startTime8,
FormOpen.startTime9
};
if (cmbLessonLength.Text != "") //If the combo box "cmbLessonLength" is not empty, use that value instead of the default lesson lenght.
{
lessonLength = Convert.ToDecimal(cmbLessonLength.Text)/60; //Converts minutes to hours.
}
dec2 = dec + lessonLength;
time = TimeSpan.FromHours(Double.Parse(dec2.ToString()));
newTime = time.ToString();
if (newTime[0] == '0')
{
FormOpen.startTime = initialString + " - " + newTime.Remove(5).Remove(0, 1);
}
else
{
FormOpen.startTime = initialString + " - " + newTime.Remove(5);
}
for (int x = 1; x <= numOfPeriods; x++) //LOOP THAT ASSIGNS VALUES TO THE ARRAY AND NOT THE REFERENCE INSIDE IT
{
decimal workingNumber = lessonLength*x;
decimal Convert0 = dec + workingNumber;
TimeSpan Convert1 = TimeSpan.FromHours(Double.Parse(Convert0.ToString()));
string Convert2 = Convert1.ToString().Remove(5);
periodStartTimes[x] = Convert2;
}
FormOpen.subjectName = cmbSubjects.Text;
FormOpen.startTime2 = periodStartTimes[1]; //IT DOES WORK LIKE THIS
FormOpen.startTime3 = periodStartTimes[2];
FormOpen.ShowDialog();
}
I have provided the whole code, so it's clearer and if there's a more efficient way of coding this I would be really thankful if someone could give me a few tips.
Your code cannot work using that array periodStartTimes This is an array of strings and when you initialize it you get the value of the property (IE the current text of the labels) not a reference to a property that you can use to change the labels.
In any case it is a bad practice to allow a different class instance change the internal values of another class. It is better to provide a public method used by the external classes to change the internal properties.
So for example your Test form class could have a public method named as SetTextLabel
public class Test : Form
{
....
public void SetLabelText(string name, string value)
{
switch(name)
{
case "startTime":
this.labelForStartTime.Text = value;
break;
case "label1":
this.firstLabelToUpdate.Text = value;
break;
...
// and so on for all other labels
}
}
}
In this way the code that changes the label is inside the Test class and you can add all the checks and validations required with full access to all the internal variables of the Test class.
Now your loop can be rewritten as
for (int x = 1; x <= numOfPeriods; x++)
{
decimal workingNumber = lessonLength*x;
decimal Convert0 = dec + workingNumber;
TimeSpan Convert1 = TimeSpan.FromHours(Double.Parse(Convert0.ToString()));
string Convert2 = Convert1.ToString().Remove(5);
FormOpen.SetLabelText("label" + x.ToString, Convert2;
}
Of course now you don't need anymore the array, and this will probably remove another error present in your actual code. The array is fixed at 9 elements but you loop for numOfPeriods starting from 1 (thus skipping the first element) and if numOfPeriods is bigger than 8 your code will crash with IndexOutOfRange exception

Event to NumericUpDown for only user interaction

I have 3 NumericUpDown elements in my form. This elements is synchronized by their sum. For example sum is 9 elements values is 3,3,3 and increment is 2. When user is changed first element up from 3 to 5 we must get 5,2,2.
For synchronized I had tried to use events ValueChanged and VisibleChanged, but they working when we have a programmatic modification or user interaction.
I used this method for every element, but for this events this method starts changing again, when result values other elements is changing in a code.
private void numericUpDown1Change(object sender, EventArgs e)
{
int oldValue = Sum - (int)numericUpDown2.Value - (int)numericUpDown3.Value;
int average;
if ((int)numericUpDown1.Value - oldValue > 0)
{
average = ((int)numericUpDown1.Value - oldValue) / 2;
numericUpDown2.Value = numericUpDown2.Value - average;
numericUpDown3.Value = numericUpDown3.Value - average;
}
else
{
average = (oldValue - (int)numericUpDown1.Value) / 2;
numericUpDown2.Value = numericUpDown2.Value + average;
numericUpDown3.Value = numericUpDown3.Value + average;
}
}
I want to use event, what worked just when user clicking the up or down button, or by the user entering a new value.
What event I must choose for it?
Use the ValueChanged event, but keep a flag telling you if the change is done by code or by user.
For a single control you can keep a Boolean variable at the class level (I would probably call it IsValueChangedByCode) and set it to false. Inside your methods, right before you change the value, set it to true and right after that back to false. Inside the event handler check that flag is true or false, and decide if you want to execute the code or not.
For 3 controls the logic is the same, but instead of using a boolean flag, you need 2 different booleans, or you can use an integer or flags enum.

C# Texbox's 'MaxLength' Property - Faults (Winform)

There might be some workaround for this one - however, I'm not sure what it is at the moment. After setting the MaxLength property of a textbox, I am unable to manually exceed the MaxLength of the textBox. On the other hand, if I were to create a loop which programmatically added characters to the textbox - this loop could exceed the maxLength property.
textBox1.MaxLength = 5; // I am now unable to manually type in more than 5 chars.
for (int i = 0; i < 20; i++)
{
textBox1.AppendText("D");
}
// Textbox now holds 20 chars.
Without having to write more lines of code to take a portion of this data, is there a way to ensure that the maxlength property is not exceeded?
Regards,
Evan
MaxLength: Gets or sets the maximum number of characters the user can type or paste into the text box control. (Forms) http://msdn.microsoft.com/en-us/library/system.windows.forms.textboxbase.maxlength.aspx and Gets or sets the maximum number of characters allowed in the text box. (web) http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.textbox.maxlength.aspx
In other words, that's the point of MaxLength - it's all about controlling user input. Since you own the textbox to begin with, you don't need to set your own hard programmatic restrictions.
So in short, no - you can't change this behavior without overriding some other functionality - for instance on OnChanged - or adding the conditional tests like those shown by Ben and Sres.
From the MSDN docs:
In code, you can set the value of the
Text property to a value that has a
length greater than the value
specified by the MaxLength property.
This property only affects text
entered into the control at run time.
If you want to prevent Text from being longer than MaxLength, some extra code is needed.
How about:
textBox1.MaxLength = 5;
for (int i = 0; i < 20 && i < textBox1.MaxLength; i++)
{
textBox1.AppendText("D");
}
Not sure if that counts as "more lines of code" but it's a pretty simple extra check.
textBox1.MaxLength = 5;
while(textBox1.Text.Length <= textBox1.MaxLength)
textBox1.AppendText ("D");
This should do it I believe
MaxLength property prevent user to type more than n characters. but when you set the Text property programatically,your textbox will show the value of its Text property even if its length exceed the value MaxLength
so you have to check if your loop exceed the maxlength or not.
As far as I know setting the maximum width of a textbox only enforce this restriction to end user who is entering values thorough UI. This restriction doesn't apply on code
MaxLength is used when you don't want the user to be able to input more than the assigned amount. However, programatically, it can be overridden. This is what append text does:
public void AppendText(string text)
{
if (text.Length > 0)
{
int start;
int length;
this.GetSelectionStartAndLength(out start, out length);
try
{
int endPosition = this.GetEndPosition();
this.SelectInternal(endPosition, endPosition, endPosition);
this.SelectedText = text;
}
finally
{
if (base.Width == 0 || base.Height == 0)
{
this.Select(start, length);
}
}
}
}
You could write an extension method, and use it to append text instead of .AppendText()
void Main()
{
var t = new TextBox();
t.MaxLength=5;
t.Text = "123";
t.AppendTextRespectMaxLength("456789");
t.Text.Dump(); // prints 12345
}
public static class ExtensionMethods
{
public static void AppendTextRespectMaxLength(this TextBox textbox,string newText)
{
if(textbox.Text.Length + newText.Length <= textbox.MaxLength)
{
textbox.Text += newText;
}
else
{
var remaining = textbox.MaxLength - textbox.Text.Length;
var subPortion = newText.Substring(0,remaining);
textbox.Text += subPortion;
}
}
}

Categories