I'm making a simple calculator to save myself from having to add up a bunch of invoice totals on basic calculator at work (since we use paper invoices). I'm just getting the basic functionalities in place currently and one of the functions I have is adding extra textboxes and then later trying to add all of the values in the textboxes to a subtotal. The problem I am having (I think) is that the compiler needs to have the the textbox ID's before the program is compiled. I'm also going to apologize for the sloppy variable names, I tried everything I could think of in my basic mindset until 4AM and by the time I was just using any variable. I've tried all the iterations, (ended up with do while statement as you can see).
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
int partboxes = 3;
int lastY = 83;
public Form1()
{
InitializeComponent();
}
private void buttonFinal_Click(object sender, EventArgs e)
{
AddParts();
}
public double AddParts()
{
int i = 1;
double megavalue = 0;
do
{
double totalsum = double.Parse(("partBox" + i).Text);
megavalue = totalsum + megavalue;
i = i + 1;
} while (i < partboxes);
string supervalue = megavalue.ToString();
lblPartsTotal.Text = supervalue;
return megavalue;
}
private void button1_Click(object sender, EventArgs e)
{
TextBox partBox = new TextBox();
partBox.Name = "partBox" + partboxes++;
partBox.Location = new System.Drawing.Point(12, lastY + 26);
partBox.Size = new System.Drawing.Size(100, 20);
// Add the textbox control to the form's control collection
this.Controls.Add(partBox);
lastY = lastY + 26;
partboxes = partboxes++;
}
}
}
You can get a textbox by name from the Controls property with
var tb = (TextBox)Controls["partBox" + i];
where i is the textbox number.
The indexer returns a Control. But since every control has a Text property, you can use it without casting.
double totalsum = double.Parse(Controls["partBox" + i].Text);
See: Control.ControlCollection Class
Related
I would like to add MaterialSingleLineTextField dynamically to a Form.
I have used MaterialSkin NuGet package:
I am trying to create multiple MaterialSkin TextBoxes dynamically on Form.Load. But no Controls are displaying in the hosing Panel.
private void Form1_Load(object sender, EventArgs e)
{
int n = 5;
int pointX = 30;
int pointY = 40;
//panel1.Controls.Clear();
for (int i = 0; i < n - 1; i++)
{
MaterialSingleLineTextField a = new MaterialSingleLineTextField();
a.Text = (i + 1).ToString();
a.Visible = true;
a.Location = new Point(pointX, pointY);
panel1.Controls.Add(a);
panel1.Show();
pointY += 20;
}
}
This code block works perfectly fine for normal TextBoxes.
Is there any way to add MaterialSingleLineTextField dynamically?
A sample Form initialization with default Theme values.
The MaterialSkinManager is initialized in the Form Constructor, setting the Theme to MaterialSkinManager.Themes.LIGHT and default color scheme. The Form base Type is set to MaterialForm.
A specified number of MaterialSingleLineTextField controls is added to a parent container (a Panel), starting from a defined Location downwards.
These Controls are anchored to the parent and the Height is set to Parent.Font.Height + 4.
It's important that you specify the Size of these Controls, otherwise you'll get a minimal size that prevents the Controls from showing their content.
As you can see in the AddTextFields() method, it's important that you dispose of the previous Controls added to the Parent container, if you want to replace the existing with new ones. Even more important using this Library.
Calling the Clear() method of a Control.Controls collection (as in the line you have commented out) doesn't dispose of anything, those controls are still alive.
public partial class Form1 : MaterialForm
{
private readonly MaterialSkinManager msManager = null;
public Form1()
{
InitializeComponent();
msManager = MaterialSkinManager.Instance;
msManager.AddFormToManage(this);
msManager.Theme = MaterialSkinManager.Themes.LIGHT;
}
private void Form1_Load(object sender, EventArgs e)
{
AddTextFields(panel1, 5, new Point(30, 10), false);
}
private void AddTextFields(Control parent, int controlsCount, Point startPosition, bool ClearExisting)
{
if (clearExisting && parent.Controls.Count > 0) {
for (int i = parent.Controls.Count - 1; i >= 0; i--) {
parent.Controls[i].Dispose();
}
}
int controlHeight = parent.Font.Height + 4;
int yIncrement = 0;
for (int i = 0; i < controlsCount; i++) {
var textField = new MaterialSingleLineTextField() {
Text = (i + 1).ToString(),
Size = new Size(parent.ClientSize.Width - startPosition.X - 4, controlHeight),
Location = new Point(startPosition.X, startPosition.Y + yIncrement),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right
};
parent.Controls.Add(textField);
yIncrement += (controlHeight + 10);
}
}
private void matBtnChangeTheme_Click(object sender, EventArgs e)
{
msManager.Theme = MaterialSkinManager.Themes.DARK;
msManager.ColorScheme = new ColorScheme(Primary.Blue600, Primary.Blue900, Primary.Blue500, Accent.LightBlue200, TextShade.WHITE);
}
private void matBtnAddControls_Click(object sender, EventArgs e)
{
AddTextFields(panel1, 7, new Point(30, 10), true);
}
}
Sample functionality:
I want to create a form with a button where button can create a set of components(2 text boxes, 1 label). I want the value entered in the text boxes to be stored into a int array.
I have worked this far
Button Click Event
private void button1_Click(object sender, EventArgs e)
{
AddNewLabel();
AddNewTextBox1();
AddNewTextBox2();
}
Adds Textboxes
public System.Windows.Forms.TextBox AddNewTextBox1()
{
System.Windows.Forms.TextBox txt1 = new System.Windows.Forms.TextBox();
this.Controls.Add(txt1);
txt1.Top = txtBoxCounter * 28;
txt1.Left = 125;
string txtName1 = "txtLL" + txtBoxCounter;
txt1.Name = txtName1;
string txtValue1 = txt1.Text;
lowerLimit[txtBoxCounter-1] = Int32.Parse(txtValue1);
return (txt1);
}
Adds Labels
public System.Windows.Forms.Label AddNewLabel()
{
System.Windows.Forms.Label lbl = new System.Windows.Forms.Label();
this.Controls.Add(lbl);
lbl.Top = lblCounter * 28;
lbl.Left = 15;
lbl.Text = "Range" + lblCounter;
lblCounter = lblCounter + 1;
return (lbl);
}
After Start Debbuging
After Button Click
There are several approaches to this: you could store TextBox objects that you create in a collection in an instance variable on the form, or since text boxes are already added to this.Controls, you could walk them dynamically.
The approach is up to you; here is an example of using the second approach:
var total = this.Controls
.OfType<TextBox>
.Sum(tb => {
int val;
if (int.TryParse(tb.Text, out val)) {
return val;
}
return 0;
});
Note: You need to import System.Linq for this approach to compile.
Here is my code so far where I have a numericupdown item named numericUpDown and button. Once the user selects a number when they press the button it dynamically created the fields.
private void createPerson_Click(object sender, EventArgs e)
{
Label[] person_Name = new Label[(int)this.numericUpDown.Value];
TextBox[] person_txtinput = new TextBox[(int)this.numericUpDown.Value];
for (int i = 0; i < this.numericUpDown.Value; i++)
{
//create person name label
Person_Name[i] = new Label();
Person_Name[i].Location = new System.Drawing.Point(20, 114 + i * 25);
Person_Name[i].Size = new System.Drawing.Size(120, 15);
Person_Name[i].Text = (i + 1).ToString() + #")" + "Person Name:";
this.Controls.Add(Person_Name[i]);
//create person name textbox
PersonNameTxtInput[i] = new TextBox();
PersonNameTxtInput[i].Location = new System.Drawing.Point(140, 114 + i * 25);
PersonNameTxtInput[i].Size = new System.Drawing.Size(125, 20);
this.Controls.Add(PersonNameTxtInput[i]);
}
}
private void save_Click(object sender, EventArgs e)
{
for (int i = 0; j < this.numericUpDown.Value; i++)
{
MessageBox.Show("" + PersonNameTxtInput[i].Text);
}
}
My question is, how can I get all the values from the textboxes depending on how many fields are created by the user when the save button is pressed?
I have tried using the code within the save button listener however how can i make Label[] person_Name = new Label[(int)this.numericUpDown.Value]; a global variable so i can access it within the save button for loop.
Well, I don't know exactly why you are doing this in this particular way and I must admit it doesn't seem very effective, but you could just do what Ryan_L suggested and iterate through this.Controls like this
for(int i = 0; i < this.Controls.Count; i++)
{
if(this.Controls[i] is TextBox) //skip buttons and labels
{
MessageBox.Show("" + this.Controls[i].Text);
}
}
Now, regarding your question how to define a global variable so you can access it within the save button for loop...just define the two arrays outside of the createPerson_Click event like this:
Label[] person_Name;
TextBox[] person_txtinput;
private void button1_Click(object sender, EventArgs e)
{
person_Name = new Label[(int)this.numericUpDown.Value];
person_txtinput = new TextBox[(int)this.numericUpDown.Value];
//the rest of the code
}
Hope this helps. However, you might want to reconsider your entire approach.
I have created a custom user control that adds combo boxes to itself.
My custom user control has 1 button which Adds Two Combo Boxes.
Clicking on it adds 2 combo boxes under the last two.
When the selected index is changed on any of the combo boxes it will run a calculation using the selected values in each box.
This is my code so far...
public partial class PlateSectionAdder : UserControl
{
private List<Dictionary<string, CueComboBox>> listOfTwoCombos = new List<Dictionary<string, CueComboBox>>();
public List<string> ColList { get; }
public List<string> PlateList { get; }
private int count = 1;
private int gap = 3;
private int edge = 5;
private Button btnAdd = new Button();
public PlateSectionAdder(List<string> collist, List<string> platelist)
{
InitializeComponent();
this.ColList = collist;
this.PlateList = platelist;
btnAdd.FlatStyle = FlatStyle.Flat;
btnAdd.Top = edge / 2;
btnAdd.Left = edge;
btnAdd.Width = this.Width - (edge * 2);
btnAdd.Click += BtnAdd_Click;
this.Controls.Add(btnAdd);
}
private void BtnAdd_Click(object sender, EventArgs e)
{
CueComboBox cmbCols = new CueComboBox();
cmbCols.DataSource = this.ColList;
cmbCols.Top = (btnAdd.Bottom + edge) * count;
cmbCols.Left = edge;
cmbCols.Width = btnAdd.Width / 2 - gap;
cmbCols.SelectedIndexChanged += CalculatePlateSections;
CueComboBox cmbPlates = new CueComboBox();
cmbPlates.DataSource = this.PlateList;
cmbPlates.Top = (btnAdd.Bottom + edge) * count;
cmbPlates.Left = cmbCols.Right + gap;
cmbPlates.Width = btnAdd.Width / 2 - gap;
cmbCols.DataSource = ColList;
this.Controls.Add(cmbCols);
this.Controls.Add(cmbPlates);
this.Height = btnAdd.Height + (cmbCols.Height * count) + edge + edge + 1;
cmbCols.DataSource = ColList;
cmbPlates.DataSource = PlateList;
cmbPlates.SelectedIndexChanged += CalculatePlateSections;
count++;
}
private void CalculatePlateSections(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
The combo boxes seem to be copies of the same instance, rather than independent, I thought by using the new keyword they would be each there own instance.
I am sure there a design pattern out there that would be right for this but not sure which one.
Has a look at the builder pattern but it does not seem exactly what I am looking for.
if there is anything unclear about this let me know and I will clarify.
I am trying to make number of TextBoxes according to number in TextBox1 and I want to use each TextBox value in my program. Their name becames txtbx0, txtbx1... but when I want to use in my program, it gives error "The name 'txtbx1' does not exist in the current context". How can I use them in my program?
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int y = Convert.ToInt32(textBox1.Text);
TextBox[] txtbx = new TextBox[y];
for (int i = 0; i < y; i++)
{
txtbx[i]= new TextBox();
txtbx[i].Location = new Point(20, i * 50);
txtbx[i].Size = new Size(100,50);
txtbx[i].Name = "txtbx"+i.ToString();
txtbx[i].Text = txtbx[i].Name;
flowLayoutPanel1.Controls.Add(txtbx[i]);
}
}
The way you have it right now, the textboxes could be accessed by using your array txtbx[whateverNumber]. In order to make them accessible outside of the method you posted, you'll need to make your txtbx array a class member instead of a method-scoped variable.
Something like:
class Form1 : Form
{
TextBox[] txtbx;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int y = Convert.ToInt32(textBox1.Text);
txtbx = new TextBox[y]; // Now this references the class member
for (int i = 0; i < y; i++)
... etc.
}
}
Accessing them individually by name is not really feasible because you'd have to have class member variables for each of them, but you don't know up front how many to make. The array method like you're doing is much better. You can just access them in other methods using txtbx[0] through txtbx[numBoxes - 1].