Add dynamically added textbox value from User Control to main form - c#

In my main form I have a textbox namely textBoxTotalTotal and I want all the dynamically added textbox to be the added/sum in my textBoxTotalTotal How can I achieve that?
In my main form I have this:
textBoxTotalTotal
Then in my User Control I have this
public void textBoxTranspo_TextChanged(object sender, EventArgs e)
{
int intTranspo = 0, intBoxDaily = 0;
if (int.TryParse(textBoxTranspo.Text, out intTranspo) && int.TryParse(textBoxDaily.Text, out intBoxDaily))
textBoxTotalAmount.Text = (intTranspo + intBoxDaily).ToString();
}
and
public void textBoxDaily_TextChanged(object sender, EventArgs e)
{
int intTranspo = 0, intBoxDaily = 0;
if (int.TryParse(textBoxTranspo.Text, out intTranspo) && int.TryParse(textBoxDaily.Text, out intBoxDaily))
textBoxTotalAmount.Text = (intTranspo + intBoxDaily).ToString();
}
Note that I am dynamically adding that User Control via button in the main form as many times I like. So the textBoxTotalTotal should just add them up wheter a new 2 textbox comes up.

Assuming that UserControls' Parent is Form1 and User Control type MyUserControl:
(You should set the correct parent, and user control type)
MyUserControl[] controls = Form1.Controls.OfType<MyUserControl>().ToArray();
int Total = 0;
for(int i=0;i<controls.Length;i++){
controls[i].Controls.OfType<TextBox>().ToList()
.ForEach(txt => Total += int.Parse(txt.Text));
}
If you are doing it from the UserControl itself then:
MyUserControl[] controls = Parent.Controls.OfType<MyUserControl>().ToArray();
int Total = 0;
for(int i=0;i<controls.Length;i++){
controls[i].Controls.OfType<TextBox>().ToList()
.ForEach(txt => Total += int.Parse(txt.Text));
}

Related

How to assign tab order from left to right - winforms C#

I have a windows form in C# project that keeps some information. I created a bunch of textbox and combobox dynamically, depend upon user input.
So here there is two rows since user has given the input as 2. All the components in the image are dynamically created. For each component i have created a class to set the property and its behaviour.
Now the issue is I need to traverse the component using tab.
When i tried to set tabindex = 1 for the first textbox and tabindex = 2 for the second textbox. I'm traversing the components vertically like mentioned below
Actual Output : enter image description here
The code in which i have added are following.
public class addDynamicCptboxComponents : add_components
{
public override void add_dynamic_components(int getNoOfTxtBox, int pointX, int pointY, Form1 f)
{
TextBox txtBox = new TextBox();
f.panel1.Controls.Add(txtBox);
txtBox.Location = new Point(pointX, pointY);
txtBox.Size = new System.Drawing.Size(75, 23);
f.panel1.Controls.Add(txtBox);
txtBox.Name = "Add_txtBox" + getNoOfTxtBox;
//assigned the tabindex as 2 for the second textbox
txtBox.TabIndex = 2;
}
}
public class addDynamicDateofServiceComponents : add_components
{
public override void add_dynamic_components(int getNoOfTxtBox, int pointX, int pointY, Form1 f)
{
TextBox txtBox = new TextBox();
f.panel1.Controls.Add(txtBox);
txtBox.Location = new Point(pointX, pointY);
txtBox.Size = new System.Drawing.Size(75, 23);
f.panel1.Controls.Add(txtBox);
txtBox.Name = "Add_dos_txtBox" + getNoOfTxtBox;
//assigned the tabindex as 1 for first textbox
txtBox.TabIndex = 1;
}
}
But what i need is , I need to traverse the components horizontally as mentioned below.
Expected Ouput: enter image description here
The Requried tab order is specified in the above image.
Guessing from the name of your class you are adding rows dynamically to your form. But since you are hard coding the tab index the result per row looks like in your expected output. This means by tabbing you go from index 1 to index 1 to index 2 to index 2 and so on and so forth.
I'd advise you to have an incrementing tab index stored somewhere in your application which is incremented after it is assigned to a new dynamically created control.
As a really simple example I created a fresh forms project which just has two buttons. The first one adds a new textbox and the second button switches into a new row. And in this example everything has the tab index you require. The code behind looks like this:
public partial class Form1 : Form
{
private int currentX = 0;
private int currentY = 0;
private const int tbWidth = 75;
private const int tbHeight = 23;
private int currentTabIndex = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var textBoxOne = new TextBox();
this.Controls.Add(textBoxOne);
textBoxOne.Location = new Point(this.currentX, this.currentY);
textBoxOne.Size = new Size(tbWidth, tbHeight);
textBoxOne.TabIndex = currentTabIndex;
textBoxOne.Text = $"{currentTabIndex}";
currentTabIndex++;
this.currentX += tbWidth + 5;
}
private void button2_Click(object sender, EventArgs e)
{
currentY += tbHeight + 5;
currentX = 0;
}
}
Please bare in mind this is just a simple example. I could help you better in the context of your application if I knew more about it.

Dynamic text boxes and using them

I've generated some textboxes and i want to the user input data in them after it added to form and then i use the data in them for some calculations.
how can i use the data?
TextBox t3 = new TextBox();
t3.Top = 222 + ((addalternativebutton - 3) * 60);
t3.Left = 214;
t3.Width = 76;
t3.Height = 22;
t3.Name = "txtwaste" + addalternativebutton.ToString();
this.tabore.Controls.Add(t3);
ww[addalternativebutton] = Convert.ToDouble(t3.Text);
As I mentioned in the comments you need to preserve the textbox instances which you are creating dynamically. You can either use generic dictionary, if you need to deal with the names assigned to them or you can use generic list.
Following solution I provide you which uses generic list.
First thing needed is a list which will preserve the textboxes.
public partial class Form1 : Form
{
private List<TextBox> textBoxes;
private int textBoxCount; //This is used to provide unique names to the
//textboxes and to track the number of dynamic textboxes.
public Form2()
{
InitializeComponent();
}
}
Now in click event of the button the textbox is created, positioned and added to the list as well as Form.Controls.
private void button1_Click(object sender, EventArgs e)
{
textBoxCount += 1;
TextBox t3 = new TextBox();
t3.Top = 20 + (22 * textBoxCount); //You can put your own logic to set the Top of textbox.
t3.Left = 120;
t3.Width = 50;
t3.Height = 20;
t3.Name = "txtwaste" + textBoxCount; //You can use your own logic of creating new name.
this.Controls.Add(t3);
this.textBoxes.Add(t3);
}
Now when you want to calculate the sum of the values of all the textboxes on click of another button.
private void button2_Click(object sender, EventArgs e)
{
double totalValue = 0;
foreach (var textBox in textBoxes)
{
double currentValue;
if (double.TryParse(textBox.Text, out currentValue))
{
totalValue += currentValue;
}
}
// Displaying totalValue in a label.
lblTotalValue.Text = "Total Value : " + totalValue;
}
This should help you resolve your issue.

Switching Panels via Index Methods

I've been trying to solve my issue for quite a while and to be honest am getting nowhere. What i would like is when the user clicks the 'top' button on my panel it automatically goes to the top( and swaps with the one there.) and when they click the bottom button it automatically goes to the bottom. I'm setting the index panel manually but of course this doesnt work because its only viable for one panel (i have ten). Greatly appreciate some help in finding a method that can send the panel to the top of the stack regardless of its position.
Here is a image (basic) to help understand
Control ctrlToMove = (Control)this.bookControls[bookName];
int ctrlToMoveIndex = bookPanel.Controls.IndexOf(ctrlToMove);
int ctrlToSwapIndex = ctrlToMoveIndex - 5;
Control ctrlToSwap = bookPanel.Controls[ctrlToSwapIndex];
this.bookPanel.Controls.SetChildIndex(ctrlToMove, ctrlToSwapIndex);
this.bookPanel.Controls.SetChildIndex(ctrlToSwap, ctrlToMoveIndex);
Based on your drawing, I made a UserControl with a button on it:
void uc_ButtonClicked(object sender, EventArgs e) {
UserControl1 uc = sender as UserControl1;
if (uc != null) {
int childIndex = flowLayoutPanel1.Controls.GetChildIndex(uc);
if (childIndex > 0) {
UserControl1 ucTop = flowLayoutPanel1.Controls[0] as UserControl1;
flowLayoutPanel1.Controls.SetChildIndex(uc, 0);
flowLayoutPanel1.Controls.SetChildIndex(ucTop, childIndex);
}
}
}
According to your picture you have one control per row in panel. Thus I suggest you to use TableLayoutPanel instead of FlowLayoutPanel. Also I'd create user control for items in panel. E.g. it will have name PriorityUserControl and four buttons to increase, decrease, maximize, minimize it's 'priority' (I placed buttons horizontally just to save place on screen):
Next, create four events in this user control:
public event EventHandler PriorityMaximized;
public event EventHandler PriorityIncreased;
public event EventHandler PriorityDecreased;
public event EventHandler PriorityMinimized;
And rise appropriate event when button clicked:
private void topButton_Click(object sender, EventArgs e)
{
if (PriorityMaximized != null)
PriorityMaximized(this, EventArgs.Empty);
}
That's it. We have user control which tells whether it want to move up or down. Now add user controls to TableLayoutPanel (either manually or dynamically) and subscribe same event handlers of these four events to ALL user controls. Something like:
// create user control and attach event handlers
PriorityUserControl control = new PriorityUserControl();
control.PriorityMaximized += priorityUserControl_PriorityMaximized;
control.PriorityMinimized += priorityUserControl_PriorityMinimized;
control.PriorityIncreased += priorityUserControl_PriorityIncreased;
control.PriorityDecreased += priorityUserControl_PriorityDecreased;
// add another row to table
panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
panel.RowCount = panel.RowStyles.Count;
// add control table layout panel
panel.Controls.Add(control);
panel.SetRow(control, panel.RowCount - 1);
Good. All you should do now is implement these event handlers. It's simple. E.g. decreasing priority (i.e. moving down):
private void priorityUserControl_PriorityDecreased(object sender, EventArgs e)
{
// sender is a control where you clicked Down button
Control currentControl = (Control)sender;
// get position in panel
var position = panel.GetPositionFromControl(currentControl);
// just to be sure control is not one at the bottom
if (position.Row == panel.RowCount - 1)
return;
// we want to switch with control beneath current
Control controlToSwitch = panel.GetControlFromPosition(0, position.Row + 1);
// move both controls
panel.SetRow(currentControl, position.Row + 1);
panel.SetRow(controlToSwitch, position.Row);
}
Now implementation of maximizing priority (i.e. moving to top):
private void priorityUserControl_PriorityMaximized(object sender, EventArgs e)
{
Control currentControl = (Control)sender;
var position = panel.GetPositionFromControl(currentControl);
if (position.Row == 0 || panel.RowCount < 2)
return;
Control topControl = panel.GetControlFromPosition(0, 0);
panel.SetRow(currentControl, 0);
panel.SetRow(topControl, position.Row);
}
I believe you will create rest two handlers by yourself.
The key of what you want is setting up a clear and extendable algorithm capable to deal with the different positions of the Panels. Here you have a simple code showing certain approach to this problem:
public partial class Form1 : Form
{
int[] panelLocations;
Point[] pointLocations;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
panelLocations = new int[5];
pointLocations = new Point[5];
panelLocations[1] = 1;
panelLocations[2] = 2;
panelLocations[3] = 3;
pointLocations[1] = new Point(panel1.Left, panel1.Top);
pointLocations[2] = new Point(panel2.Left, panel2.Top);
pointLocations[3] = new Point(panel3.Left, panel3.Top);
}
private void relocate(int curPanel, bool goTop)
{
int curLoc = panelLocations[curPanel];
int newLoc = curLoc - 1;
if (!goTop)
{
newLoc = curLoc + 1;
}
if (newLoc < 1) newLoc = 3;
if (newLoc > 3) newLoc = 1;
if (newLoc != curLoc)
{
int otherIndex = Array.IndexOf(panelLocations, newLoc);
panelLocations[curPanel] = newLoc;
relocatePanel(curPanel);
panelLocations[otherIndex] = curLoc;
relocatePanel(otherIndex);
}
}
private void relocatePanel(int curIndex)
{
if (curIndex == 1)
{
panel1.Location = pointLocations[panelLocations[1]];
}
else if (curIndex == 2)
{
panel2.Location = pointLocations[panelLocations[2]];
}
else if (curIndex == 3)
{
panel3.Location = pointLocations[panelLocations[3]];
}
}
private void buttonTop1_Click(object sender, EventArgs e)
{
relocate(1, true);
}
private void buttonBottom1_Click(object sender, EventArgs e)
{
relocate(1, false);
}
}
Open a new project, add 3 panels (Panel1, Panel2 and Panel3... better put different background colors) and include two buttons (buttonUp and buttonDown). This code will make the Panel1 to go up and down (by changing its position with the other panels).
The idea is pretty simple: at the start you store the positions of all the Panels in an array. In another array, you store where each panel is located every time (1 is the original position of Panel1, etc.).
It is a quite simple code which you can improve and extend as much as required, but the idea is pretty reliable and you can use it in any case: a set of fixed positions through which the panels will be moving.

Disabling a dynamic button

Hi I have a small winforms program that will soon develop into something more. The program has 2 panels panel1 and panel2 these panels are populated dynamically with some form controls. the first panel is populated with combo-boxes and the second with a grid of buttons. What I want to achieve is to be able to disable the right button depending on what the user selects from the combobox. Each column of the grid represent a day of the week and the combobox will be used to disable the wanted day by selecting it from the list if you like.
To do this statically is straight forward, however my program will soon expand so that it can handle a large database so that's why I am doing this dynamically. Basically this is where I'm stuck at the moment I want to simply disable the right button.
Below is the interface that i have so far:
And this is my code if any help:
public Form1()
{
InitializeComponent();
}
Button[] btn = new Button[2];
ComboBox[] cmb = new ComboBox[1];
private void Form1_Load(object sender, EventArgs e)
{
placeRows();
}
public void createColumns(int s)
{
for (int i = 0; i < btn.Length; ++i)
{
btn[i] = new Button();
btn[i].SetBounds(40 * i, s, 35, 35);
btn[i].Text = Convert.ToString(i);
panel1.Controls.Add(btn[i]);
}
for (int i = 0; i < cmb.Length; ++i)
{
cmb[i] = new ComboBox();
cmb[i].SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
cmb[i].Text = "Disable";
cmb[i].Items.Add("Monday");
cmb[i].Items.Add("Tuesday");
cmb[i].SetBounds(40 * i, s, 70, 70);
panel2.Controls.Add(cmb[i]);
}
}
void cmb_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox senderCmb = (ComboBox)sender;
if (senderCmb.SelectedIndex == 1)
{
//MessageBox.Show("Tuesday");
btn[1].Enabled = false;
}
}
public void placeRows()
{
for (int i = 0; i < 80; i = i + 40)
{
createColumns(i);
}
}
}
Alternative 1
Every control has a Tag property.
You can set the Tag property of your buttons to represent the column they are in.
When a selection is made in the combo box, simply search through all buttons, and enable or disable the button based on whether each button's Tag property matches the selected text in the combo box.
Alternative 2
Create a
Dictionary<string, List<Button>> buttonMap;
where the key is the value representing the column ("Tuesday") and the value is a list of buttons with that tag. When creating the buttons initially, also populate that dictionary.
If you go with Alternative 2, you'll have to remember the previously selected value of the checkbox so you can re-enable buttons that are no longer disabled.
If you have lots of buttons, you may find that Alternative 2 is noticeably faster.
UPDATE
Here's a complete working sample of Alternative 1.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
const int ROWS = 2;
const int COLS = 2;
Button[,] btn = new Button[ROWS,COLS];
ComboBox[] cmb = new ComboBox[ROWS];
private void Form1_Load(object sender, EventArgs e)
{
placeRows();
}
private readonly string[] cbTexts = new string[] { "Monday", "Tuesday" };
public void createColumns(int rowIndex)
{
int s = rowIndex * 40;
// Your original code kept overwriting btn[i] for each column. You need a 2-D array
// indexed by the row and column
for (int colIndex = 0; colIndex < COLS; colIndex++)
{
btn[rowIndex, colIndex] = new Button();
btn[rowIndex, colIndex].SetBounds(40 * colIndex, s, 35, 35);
btn[rowIndex, colIndex].Text = Convert.ToString(colIndex);
btn[rowIndex, colIndex].Tag = cbTexts[colIndex];
panel1.Controls.Add(btn[rowIndex, colIndex]);
}
cmb[rowIndex] = new ComboBox();
cmb[rowIndex].SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
cmb[rowIndex].Text = "Disable";
foreach (string cbText in cbTexts)
{
cmb[rowIndex].Items.Add(cbText);
}
cmb[rowIndex].SetBounds(40, s, 70, 70);
cmb[rowIndex].Tag = rowIndex; // Store the row index so we know which buttons to affect
panel2.Controls.Add(cmb[rowIndex]);
}
void cmb_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox senderCmb = (ComboBox)sender;
int row = (int)senderCmb.Tag;
for (int col = 0; col < COLS; col++)
{
Button b = btn[row, col];
// These three lines can be combined to one. I broke it out
// just to highlight what is happening.
string text = ((string)b.Tag);
bool match = text == senderCmb.SelectedItem.ToString();
b.Enabled = match;
}
}
public void placeRows()
{
for (int rowIndex = 0; rowIndex < 2; rowIndex++)
{
createColumns(rowIndex);
}
}
}

Linking dynamically created text boxes and labels

I created an array of of TextBoxes and an array of Labels. When the information is updated in the TextBox I want it to change the Labels. How would I be able to do this? Below is piece of my code. I have not created the EvenHandler that I think is the part I need help with. All in C# using windows application form.
textBoxes = new TextBox[value];
labels = new Label[value];
for (int i = 1; i < value; i++)
{
textBoxes[i] = new TextBox();
textBoxes[i].Location = new Point(30, ToBox.Bottom + (i * 43));
labels[i] = new Label();
labels[i].Location = new Point(TopBox3[i].Width + 140, TopBox3[i].Top +3);
textboxes[i].ValueChanged += new EventHandler(this.TextBox_ValueChanged) ;
this.Controls.Add(labels[i]);
this.Controls.Add(textBoxes[i]);
}
You can remember the index of the TextBox in the Tag property
textBoxes[i].Tag = i;
and then use this value in your eventhandler to get the corresponding label (assuming that you hold the labels array as a local variable)
protected void TextBox_ValueChanged(object sender, EventArgs e)
{
TextBox textbox = sender as TextBox;
if(textbox==null)
return;
int index = Convert.ToInt32(textbox.Tag);
if(index >= 0 && index < this.labels.Length)
{
Label label = this.labels[index];
/* ... */
}
}
You should write something like this:
private void textBox1_ValueChanged(object sender, EventArgs e)
{
TextBox changedTxt = sender as TextBox;
for (int i = 1; i < value; i++)
if (textBoxes[i] == changedTxt)
{
Label lblToChange = labeld[i];
lblToChange.Text = changedTxt.Text;
break;
}
}
In the method the TextBox whose text has changed is passed as "sender". You look into your array for it, so you identify the index "i" which can be used to access the corresponding Label and to set its text.
BTW as Tim said, the event is TextChanged, not ValueChanged. Furthermore be aware that the event is triggered for every change in the text, i.e. as soon as you press a key the label will be updated. If you prefer to update your labels only when the user has finished to enter its text Leave is the event you should use.

Categories