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.
Related
I'm very new to coding and am currently learning C#. More specifically I'm currently struggling with using checkboxes in group boxes. What I'm wondering is if it's possible to have multiple checkboxes in the same 'if' statement?
What I mean by is that, instead of just (for example):
private void button1_Click(object sender, Eventargs e)
{
float optionACheckBox = 149.00f;
if (checkBox1.Checkstate == CheckState.Checked)
{
finalPrice = (optionACheckBox + 60)
}
MessageBox.Show(finalPrice);
}
how would I make it so that instead it would be something like:
if (checkBox1 & checkBox2 = CheckState.Checked)
{
((Value of checkBox1) + (Value of checkBox2) + 60)
}
where if the user checks checkBox1 & checkBox2 then both values can be used in the equation?
Regarding this, would it also be possible to make code that checks if any/what combination of the CheckBoxes in the GroupBox have been checked? Rather than me writing an 'if' statement for every single combination of the different CheckBoxes?
Thank you to anyone who takes the time to read this and for any tips given
More Context (Sorry this is my second ever StackOverFlow post so I'm not very good at proper formatting):
Using Visual Studio 2019
There are three CheckBoxes in total, and all three are in one GroupBox
The CheckBoxes and GroupBox were added to the form using the toolbox feature
You didn't really tell us much about your logic but it seems like you want a hypothetical scenario like:
some fixed base price of the pizza
a price for each topping
a fixed price for applying any number of toppings
As such I'd look at accumulation:
float price = 100;
if(anchoviesCheckbox.Checked)
basePrice += 20;
if(olivesCheckbox.Checked)
basePrice += 30;
if(chilliesCheckbox.Checked)
basePrice += 40;
//if anchovies AND chillies are chosen there is a surcharge because the most of the chefs are allergic to this combination and we have to get the manager to do it
if(anchoviesCheckbox.Checked && chilliesCheckBox.Checked)
basePrice += 50;
//if any one OR more topping has been applied, there is a surcharge
if(anchoviesCheckbox.Checked || olivesCheckbox.Checked || chilliesCheckbox.Checked)
basePrice += 60;
These accumulate because there is no else - if the checkbox is checked, cost is added to the final price. Each checkbox is viewed in isolation. Later some also act in combination.
Note you could of course also check if the base price is greater than 100 to know if to apply a surcharge..
Once you've got that clear in your mind how it works, you can save yourself some of those ifs by treating the checkboxes in the group box as a collection
You can do this in the form constructor:
//put the prices in the checkbox Tag:
anchoviesCheckbox.Tag = 20;
olivesCheckbox.Tag = 30;
chilliesCheckbox.Tag = 20;
Then in your price calc method you can do something like:
float basePrice = 100:
foreach(var cb in toppingsGroupBox.Controls.OfType<CheckBox>().Where(cb => cb.Checked))
basePrice += (int)cb.Tag;
This uses LINQ to pull out just the checked checkboxes, and then extract the price from their tag and add it on. You could even do:
float basePrice = 100 +
toppingsGroupBox.Controls
.OfType<CheckBox>()
.Where(cb => cb.Checked)
.Sum(cb => (int)cb.Tag);
For your adding a surcharge if any topping is chosen:
if(toppingsGroupBox.Controls
.OfType<CheckBox>()
.Any(cb => cb.Checked)))
basePrice += 60;
You could now add 100 different toppings checkboxes to the form and so long as you put the price in the tag there is no more code needs adding to the method that calculates the price
The "manager must add anchovies and chillies" surcharge could also be done with LINQ. For that you'd have to define a custom set of checkboxes and if they are all ticked then apply the surcharge:
class PizzaForm:Form{
CheckBox[] managerSurcharge;
PizzaForm() { // constructor
...
managerSurcharge = new[]{ anchoviesCheckBox, chilliesCheckBox };
}
Then in the price:
if(managerSurcharge.All(cb => cb.Checked))
basePrice += 50;
Not an if in sight! 😀 (yeah, it's really hard to do away with every if..)
But do name your controls and variables well; this code here is easily readable and obvious what it does because of the names. That manager surcharge scenario would be a lot more confusing if you write:
CheckBox array1 = new[]{ checkbox12, checkbox7 }
You can use switch case judgments to assign different outcomes to these different choices.
But because every combination is a judgment situation, no matter what judgment method you use, you need to judge them separately.
The judgment to judge that both are selected should be as follows:
if (checkBox1.Checked && checkBox2.Checked)
{
//To do something
}
Some documents:
checked 、&&、||、== and !=
First of all, you can't compare the checkbox itself with a CheckState so you have to use checkBox1.CheckState == CheckState.Checked or better using Boolean "Checked" property: if(checkBox1.Checked) which will check if the checkbox is checked.
As for your last Question on how to detect if any checkbox is checked you can use || the logical "OR" Operator:
if(checkBox1.Checked || checkBox2.Checked || checkBox3.Checked)
Update
Logical Operator vs Conditional Short Circuit Operator
Logical Operator: & is used to ensure all Operands are evaluated and checked.
Conditional Short Circuit Operator: && is used to skip the right side Operand.
As mentioned in the comments, both will work in the vast majority of the cases, but I would always use the "Conditional Short Circuit Operators" when possible, to skip the unnecessary Check of the right side Operand.
I am fairly new to programming and would like to know how I can get my program to subtract 30 from 100 every time I click a button. This is what I have but can't seem to find the correct formula.
int hpLevel = 100;
int hpDrop = 30;
if (hpLevel > 0)
{
lblHpLevel.Text = Convert.ToString(hpLevel - hpDrop);
}
else
{
lblHpLevel.Text = Convert.ToString(hpLevel);
}
The code declares a new hpLevel variable every time the button is clicked. You want that variable defined outside of the method, at the class level, so that the value will be retained between clicks.
Additionally, the code never assigns the result back to the hpLevel variable. It assigns it directly to the TextBox, while hpLevel is never changed. You need to assign the results to hpLevel and then set that value to your textbox.
int hpLevel = 100;
int hpDrop = 30;
void button1_Click( ... )
{
if (hpLevel > 0)
{
hpLevel -= hpDrop;
}
lblHpLevel.Text = Convert.ToString(hpLevel);
}
You must declare int hpLevel = 100 out of the scope of the method that is handling the click event. If int hpLevel = 100 is part of the aformentioned method then you are instantiating an intvariable with name hpLevel and assigning it the value 100 every time you click the button.
You can learn about scopes here
I'm making a sudoku in Windows Form Application.
I have 81 textboxes and I have named them all textBox1a, textBox1b... textBox2a, textBox2b...
I want to make it so that if any of the textboxes, in any of the rows, is equal to any other textbox in the same row, then both will get the background color red while the textboxes are equal.
I tried using this code just for test:
private void textBox1a_TextChanged_1(object sender, EventArgs e)
{
while (textBox1a.Text == textBox1b.Text)
{
textBox1a.BackColor = System.Drawing.Color.Red;
textBox1b.BackColor = System.Drawing.Color.Red;
}
It didn't work, and I don't know where I should put all this code, I know I shouldn't have it in the textboxes.
Should I use a code similar to this or is it totally wrong?
You want to iterate over the collection of text boxes just once, comparing it to those that haven't yet been compared against. If you have your textboxes in an array (let's call it textBoxes), and know which one was just changed (e.g. from the textChanged handler), you could do:
void highlightDuplicates(int i) // i is the index of the box that was changed
{
int iVal = textBoxes[i].Text;
for (int j = 0; j < 82; j++)
{
// don't compare to self
if (i == j) return;
if (textBoxes[j].Text == iVal)
{
textBoxes[i].BackgroundColor = System.Drawing.Color.Red;
textBoxes[j].BackgroundColor = System.Drawing.Color.Red;
}
}
}
If you wanted to get fancier, you could put your data in something like: Dictionary<int, TextBox>, where the key is the value and the TextBox is a reference to the text box with that value. Then you can quickly test for duplicate values with Dictionary.Contains() and color the matching text box by getting its value.
I think your current code would result in an infinite loop. The textboxes' values can't change while you are still in the event handler, so that loop would never exit.
If all of your boxes are named according to one convention, you could do something like this. More than one input can use the same handler, so you can just assign this handler to all the boxes.
The following code is not tested and may contain errors
private void textBox_TextChanged(object sender, EventArgs e){
var thisBox = sender as TextBox;
//given name like "textBox1a"
var boxNumber = thisBox.Name.SubString(7,1);
var boxLetter = thisBox.Name.SubString(8,1);
//numbers (horizontal?)
for(int i = 1; i<=9; i++){
if(i.ToString() == boxNumber)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + i + boxLetter) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
//letters (vertical?)
for(int i = 1; i<=9; i++){
var j = ConvertNumberToLetter(i); //up to you how to do this
if(j == boxLetter)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + boxNumber + j) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
}
I believe you will be more effective if create an Array (or a List) of Integers and compare them in memory, against compare them in UI (User Interface).
For instance, you could:
1) Create an Array of 81 integers.
2) Everytime the user input a new number, you search for it in that Array. If found, set the textbox as RED, otherwise, add the new value to that array.
3) The ENTER event may be allocated fot the entire Textboxes (utilize the Handles keyword with all Textboxes; like handles Text1.enter, Text2.enter, Text3.enter ... and so forth)
Something like:
int[] NumbersByUser = new int[81];
Private Sub Textbox1.Enter(sender as object, e as EventArgs) handles Textbox1.Enter, Textbox2.Enter, Textbox3.enter ...
int UserEntry = Convert.ToInt32(Sender.text);
int ValorSelecionado = Array.Find(NumbersByUser, Z => (Z == UserEntry));
if (ValorSelecionado > 0) {
Sender.forecolor = Red;
}
else
{
NumbersByUser(Index) = UserEntry;
}
You should have a 2 dimensional array of numbers (could be one dimensional, but 2 makes more sense) let's assume its called Values. I suggest that you have each textbox have a incrementing number (starting top left, going right, then next row). Now you can do the following:
All TextBox Changed events can point to the same function. The function then takes the tag to figure out the position in the 2dim array. (X coordinate is TAG % 9 and Y coordinate is TAG / 9)
In the callback you can loop over the textboxes and colorize all boxes as you like. First do the "check row" loop (pseudo code)
var currentTextBox = ((TextBox)sender)
var x = ((int)currentTextBox.Tag) % 9
var y = ((int)currentTextBox.Tag) / 9
// First assign the current value to the backing store
Values[currentTextBox] = int.parse(currentTextBox.Text)
// assuming variable x holding the column and y holding the row of current box
// Array to hold the status of a number (is it already used?)
bool isUsed[9] = {false, false, ...}
for(int col = 0; col <= 9; i++)
{
// do not compare with self
if(col == x) continue;
isUsed[textBox] = true;
}
// now we have the status of all other boxes
if( isUsed[Values[x,y]] ) currentTextBox.Background = Red else currentTextBox.Background = Green
// now repeat the procedure for the column iterating the rows and for the blocks
I would suggest a dynamic approach to this. Consider each board item as a cell (this would be it's own class). The class would contain a numeric value and other properties that could be useful (i.e. a list of possible values).
You would then create 3 collections of the cells, these would be:
A collection of rows of 9 cells (for tracking each row)
A collection of columns of 9 cells (for tracking each column)
A collection of 3x3 cells
These collections would share references - each cell object would appear once in each collection. Each cell could also contain a reference to each of the 3 collections.
Now, when a cell value is changed, you can get references to each of the 3 collections and then apply a standard set of Sudoku logic against any of those collections.
You then have some display logic that can walk the boards of cells and output to the display (your View) your values.
Enjoy - this is a fun project.
I use a numeric updown control to allow the users to adjust numeric values to effect a change in my ultragrid. In my ultragrid InitializeLayout event I have the following:
...
var band = e.Layout.Bands[0];
band.Columns["CalcMarkup"].Formula = "if ([Markup] > 0, [FinalCost]*([Markup]/100), 0)";
band.Columns["RemainingCost"].Formula = "if([CalcMarkup] > 0, 0, [FinalCost] )";
band.Columns["ForcedMarkup"].Formula = "if([RemainingCost] = 0, 0, ([RemainingCost]/[sumRemCost()])*[force()])";
...
grdMain.DisplayLayout.Bands[0].Summaries.Add("force", "[ForcedAdj()]", SummaryPosition.UseSummaryPositionColumn, grdMain.DisplayLayout.Bands[0].Columns["ForcedMarkup"]);
...
var value = 1 - (nudMarginPer.Value/100);
_marginFormula = string.Format("( ( ([finCost()]/{0})-sum([FinalCost]) )-sum([CalcMarkup])-5000000 )", "{0}");
band.Summaries.Add("ForcedAdj", string.Format(_marginFormula, value));
band.Summaries["ForcedAdj"].SummaryDisplayArea = SummaryDisplayAreas.None;
...
grdMain.DisplayLayout.Bands[0].Summaries.Add("force", "[ForcedAdj()]", SummaryPosition.UseSummaryPositionColumn, grdMain.DisplayLayout.Bands[0].Columns["ForcedMarkup"]);
in the numeric updown event:
void nudMarginPer_ValueChanged(object sender, EventArgs e)
{
var value = 1 - (nudMarginPer.Value/100);
grdMain.DisplayLayout.Bands[0].Summaries["ForcedAdj"].Formula = string.Format(_marginFormula, value);
}
This works well in that when the user presses the updown buttons the summary (ForceAdj) values change and is reflected in the grid summary row. However the column formula for ForcedMarkup doesnt change until a second button press. So now the summary is correct however the value in the cell is off.
My question is this... How do I force the formula for ForcedMarkup (or any column formula) to refresh to reflect the current calcs?
I have tried forcing a grid update, calcmanager recalc, etc... nothing seems to work.
*** 7/20: This appears to be an issue when using group by.
From my experience, it could be calcmanager's trick.
Calcmanager recalc() method forces recalculation only for dirtied formulas, so you should dirty them first.
Also, if DeferredCalculationsEnabled property is set to true, calcmanager may defer recalculation until formula cells (or whole grid) becomes visible.
That's what worked for me :
1. If datagrid is not visdible yet
e.g trying to get values of calculated columns/summaries while still in form constructor
calcmanager_instance.DeferredCalculationsEnabled = false;
calcmanager_instance.ReCalc();
2.If datagrid is already visible, but formulas need to be recalculated
calcmanager_instance.DirtyAllFormulas();
calcmanager_instance.ReCalc();
In general, this combination should unconditionally and instantly force recalculation of all formulas :
calcmanager_instance.DeferredCalculationsEnabled = false;
calcmanager_instance.DirtyAllFormulas();
calcmanager_instance.ReCalc();
I think, there must be a way to dirty only selected formulas, but I didn't look for it.
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;
}
}
}