I have a Windows Forms Dialog in C# which shows Checkboxes for each Element in a Dictionary. The Dialog returns a List with all selected Elements(Checkboxes). However I noticed that if I select a Checkbox and then uncheck it again, the Element is still in the returned List of Selected Elements.
How can I fix this?
My Dialog looks like this:
public SelectDialog(Dictionary<string, string> Result)
{
int left = 45;
int idx = 0;
InitializeComponent();
for (int i = 0; i < Result.Count; i++)
{
CheckBox rdb = new CheckBox();
rdb.Text = Result.Values.ElementAt(i).Equals("") ? Result.Keys.ElementAt(i) : Result.Values.ElementAt(i);
rdb.Size = new Size(100, 30);
this.Controls.Add(rdb);
rdb.Location = new Point(left, 70 + 35 * idx++);
if (idx == 3)
{
idx = 0; //Reihe zurücksetzen
left += rdb.Width + 5; // nächste Spalte
}
rdb.CheckedChanged += (s, ee) =>
{
var r = s as CheckBox;
if (r.Checked)
this.selectedString.Add(r.Text);
};
}
}
//Some more Code
}
As per the comment:
You need to remove the items from the list if the raised event is unchecked, I think you have to check for already added items to avoid duplicates, and remove the items if exists. so the handler would be like this:
rdb.CheckedChanged += (s, ee) =>
{
var r = s as CheckBox;
var itemIndex = this.selectedString.IndexOf(r.Text)
if (r.Checked && itemIndex == -1)
this.selectedString.Add(r.Text);
else if(!r.Checked && itemIndex != -1)
{
this.selectedString.RemoveAt(itemIndex);
}
};
Related
I have dynamically created array CheckBoxes and I want to do a proper validation if none of them are selected inside the Panel but if I keep on using for loops, the MessageBox keeps on appearing.
Can anyone help me find a way to do this better? I just want to check if a checkbox control is checked inside the Panel and if not, display a messagebox that will say "Select a Checkbox!" only ONCE.
Here is the code that I made for the dynamically created checkboxes in a panel:
for (int z = 0; z <= dataGridView.Columns.Count - 1; z++)
{
chk[z] = new CheckBox();
chk[z].Name = dataGridView.Columns[z].Name;
chk[z].Text = dataGridView.Columns[z].Name;
chk[z].AutoCheck = true;
chk[z].Bounds = new Rectangle(10, 20 + padding + dynamicHeight, 40, 22);
chk[z].Location = new Point(0, dynamicHeight);
chk[z].Size = new Size(120, 21);
panelCol.BackColor = Color.White;
//MessageBox.Show(chk[z].Name + "" + dataGridView.Columns[z].Name);
panelCol.Controls.Add(chk[z]);
//panelCol.AutoScrollMinSize = new Size(0, 100);
dynamicHeight += 20;
panelCol.Size = new Size(120, dynamicHeight);
}
Here is the code that I have came up:
btnValidate.MouseClick += (s, e) => //btnValidate Event
{
for (int z = 0; z < dataGridView.Columns.Count - 1; z++ )
{
if(chk[z].Checked == true)
{
ValidateCheck(dataGridView, chk);
}
else if(chk[z].Checked == false)
{
MessageBox.Show("Select a CheckBox!");
}
}
};
ValidateCheck method:
public static void ValidateCheck(DataGridView dataGridView, CheckBox[] chk)
{
FileStream fs = new FileStream(#"C:\brandon\InvalidColumnCheck.txt", FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.BaseStream.Seek(0, SeekOrigin.End);
StringBuilder sb = new StringBuilder();
decimal num;
sw.WriteLine("----------------------------");
sw.WriteLine("");
for (int j = 0; j < dataGridView.ColumnCount - 1; j++)
{
if (chk[j].Checked == true && chk[j].Name.Contains(dataGridView.Columns[j].Name))
{
string column = chk[j].Name;
for (int k = 0; k < dataGridView.RowCount; k++)
{
if (!Decimal.TryParse(dataGridView.Rows[k].Cells[column].Value.ToString(), out num))
{
if (dataGridView.Rows[k].Cells[dataGridView.Columns[column].Name].Value.ToString() == null || dataGridView.Rows[k].Cells[dataGridView.Columns[column].Name].Value.ToString() == "" || dataGridView.Rows[k].Cells[dataGridView.Columns[column].Name].Value.ToString() == column)
{
}
else
{
//MessageBox.Show("COLUMN" + dataGridView.Columns[j].Name.ToString() + "" + dataGridView.Rows[k].Cells[column].Value.ToString() + " NOT A DECIMAL!");
sb.AppendLine("[Column " + chk[j].Name.ToString().ToUpper() + "] :" + dataGridView.Rows[k].Cells[column].Value.ToString() + " NOT A DECIMAL!");
}
}
}
sb.AppendLine("");
}
}
if (sb.ToString() == null || sb.ToString() == "" || sb.Length < dataGridView.Columns.Count)
{
sw.WriteLine("No Errors!");
sw.WriteLine("");
sw.WriteLine("----------------------------");
MessageBox.Show("No errors!");
Process.Start(#"C:\brandon\InvalidColumnCheck.txt");
}
else if (sb.ToString() != null || sb.ToString() != "")
{
sw.WriteLine(sb.ToString());
sw.WriteLine("----------------------------");
//MessageBox.Show(sb.ToString());
Process.Start(#"C:\brandon\InvalidColumnCheck.txt");
}
sw.Flush();
sw.Close();
}
Here another way to get all Checkboxes from Panel, which are checked (with Linq):
List<CheckBox> selectedItems = panelCol.Controls.OfType<CheckBox>().Where(chk => chk.Checked).ToList();
Please change the validation method like below,
public List<CheckBox> GetSelectedItems()
{
List<CheckBox> selectedList = new List<CheckBox>();
foreach(Control control in panelCol.Controls) // panelCol is your panel
{
if(control is CheckBox)
{
CheckBox chkCtrl = control as CheckBox;
if(chkCtrl.Checked)
{
selectedList.Add(chkCtrl);
}
}
}
return selectedList;
}
btnValidate.MouseClick += (s, e) =>//btnValidate Event
{
List<CheckBox> selectedItems = GetSelectedItems();
if(selectedItems.Count == 0)
MessageBox.Show("Select a CheckBox!");
else{
// Continue with other validation for the selected checkboxes from the list
}
}
Hope it helps!
I'm trying to turn textboxes and buttons visible when the number of tracks it's selected in a combobox.
For example: when I select 3, just 3 textboxes and the 3 respective buttons to select the tracks are enabled. How can I change this code that I've made to a simple foreach or a for?
if (numero_faixas == 1) {
txtFaixa1.Visible = true;
btnFaixa1.Visible = true;
} else if (numero_faixas == 2) {
txtFaixa1.Visible = true;
btnFaixa1.Visible = true;
txtFaixa2.Visible = true;
btnFaixa2.Visible = true;
} else if (numero_faixas == 3) {
txtFaixa1.Visible = true;
btnFaixa1.Visible = true;
txtFaixa2.Visible = true;
btnFaixa2.Visible = true;
txtFaixa3.Visible = true;
btnFaixa3.Visible = true;
}
You can reduce the lines of code by changing your conditions, so you don't have to reference the same control so many times:
if (numero_faixas > 0)
{
txtFaixa1.Visible = true;
btnFaixa1.Visible = true;
}
if (numero_faixas > 1)
{
txtFaixa2.Visible = true;
btnFaixa2.Visible = true;
}
if (numero_faixas > 2)
{
txtFaixa3.Visible = true;
btnFaixa3.Visible = true;
}
To use a foreach loop, you could cast the Controls collection to an IEnumerable<Control> and then, using System.Linq;, you can filter on controls of type TextBox and Button, where the control name contains "Faxia". Then, in the loop body, we can use int.TryParse to try to convert the last character of the control name to an int, and if that succeeds, then set the control to Visible if the control number is less than numero_faixas + 1:
foreach (Control control in Controls.Cast<Control>()
.Where(c => (c is Button || c is TextBox) && c.Name.Contains("Faixa")))
{
// Get the number associated with this control and compare it to numero_faixas
int controlNumber;
if (int.TryParse(control.Name.Substring(control.Name.Length - 1), out controlNumber) &&
controlNumber < numero_faixas + 1)
{
control.Visible = true;
}
}
Here's a proof of concept that you could respin using your business rules.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ShowHideButtons_47439046
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitOurThings();
}
private void InitOurThings()
{
//lets create a combo box with options to select
ComboBox combo = new ComboBox();
combo.Location = new Point(5, 5);//place it somewhere
//add selectable items
for (int i = 0; i < 10; i++)
{
combo.Items.Add(i);
}
combo.SelectedValueChanged += Combo_SelectedValueChanged;//the event which will handle the showing/hidding
Controls.Add(combo);//add the combo box to the form
//lets create some buttons and textboxes
int btnx = 5;
int btny = combo.Height + combo.Location.Y + 5;
for (int i = 0; i < 10; i++)
{
Button btn = new Button();
btn.Location = new Point(btnx, btny);
btn.Name = i.ToString();
btn.Text = i.ToString();
Controls.Add(btn);
btny += btn.Height + 5;
TextBox txtbx = new TextBox();
txtbx.Location = new Point(btn.Location.X + btn.Width + 5, btn.Location.Y);
txtbx.Name = i.ToString();
txtbx.Text = i.ToString();
Controls.Add(txtbx);
}
}
private void Combo_SelectedValueChanged(object sender, EventArgs e)
{
int selectedValue = int.Parse(((ComboBox)sender).SelectedItem.ToString());
foreach (Control item in Controls)
{
//show/hide the controls based on their Name being Equal Or Smaller than the selectedItem
if (item is TextBox)
{
int itemNumber = int.Parse(item.Name);
item.Visible = itemNumber <= selectedValue ? true : false;
}
if (item is Button)
{
int itemNumber = int.Parse(item.Name);
item.Visible = itemNumber <= selectedValue ? true : false;
}
}
}
}
}
I have a form which changes Title depends on users selection. Then i want to create a properties for the control, either radiobutton or checkbox. They have same properties but i cant implement well.
private void DeleteEdit()
{
Control[] EmployeesControl; //I think this is the problem, but i cant figure it out.
if (Form_AddEmployee.Text.Contains("Edit"))
{
EmployeesControl = new RadioButton[numberOfEmployees];
}
else if (Form_AddEmployee.Text.Contains("Delete"))
{
EmployeesControl = new CheckBox[numberOfEmployees];
}
for (int i = 0; i < EmployeesControl.Count(); i++)
{
EmployeesControl[i] = new EmployeesControl();
InitializeControls(EmployeesControl[i]);
EmployeesControl[i].Visible = true;
panelEmployee.Controls.Add(EmployeesControl[i]);
EmployeesControl[i].Text = stringTemp;
EmployeesControl[i].Location = new Point(100, 100 * (i+1));
EmployeesControl[i].Font = MyFont;
EmployeesControl[i].CheckedChanged += EmployeesDeleteEditEmployees_CheckedChanged;
}
}
How can i make a variable if a certain control is a radiobutton or checkbox.
You can use GetType to check, after you will change EmployeesControl[i] to CheckBox or RadioButton.
private void DeleteEdit()
{
Control[] EmployeesControl; //I think this is the problem, but i cant figure it out.
if (Form_AddEmployee.Text.Contains("Edit"))
{
EmployeesControl = new RadioButton[numberOfEmployees];
for (int i = 0; i < EmployeesControl.Count(); i++)
{
EmployeesControl[i] = new RadioButton();
}
}
else if (Form_AddEmployee.Text.Contains("Delete"))
{
EmployeesControl = new CheckBox[numberOfEmployees];
for (int i = 0; i < EmployeesControl.Count(); i++)
{
EmployeesControl[i] = new CheckBox();
}
}
ButtonBase b;
CheckBox chk;
RadioButton rdo;
for (int i = 0; i < EmployeesControl.Count(); i++)
{
b = (ButtonBase)EmployeesControl[i];//You use b to set property
if (EmployeesControl[i].GetType() == typeof(RadioButton))
{
rdo = (RadioButton)EmployeesControl[i];
//Your code
//................
rdo.CheckedChanged += EmployeesDeleteEditEmployees_CheckedChanged;
}
else
{
chk = (RadioButton)EmployeesControl[i];
//Your code
//...............
chk.CheckedChanged += EmployeesDeleteEditEmployees_CheckedChanged;
}
//EmployeesControl[i] = new EmployeesControl();
//InitializeControls(EmployeesControl[i]);
//EmployeesControl[i].Visible = true;
//panelEmployee.Controls.Add(EmployeesControl[i]);
//EmployeesControl[i].Text = stringTemp;
//EmployeesControl[i].Location = new Point(100, 100 * (i + 1));
//EmployeesControl[i].Font = MyFont;
//EmployeesControl[i].CheckedChanged += EmployeesDeleteEditEmployees_CheckedChanged;
}
}
I hope it will help you.
You can use ButtonBase class to your perpouse.
Declare array of controls by ButtonBase, so common properties of CheckBox and RadioButton you can use without casting.
Properties of CheckBox or RadioButton you can access by
var a = EmployeesControl[i] as CheckBox;
if (a != null)
{
//this is checkbox
continue;
}
var b = EmployeesControl[i] as RadioButton;
if (b != null)
{
//this is RadioButton
}
I keep getting stuck on this part of my program.
whenver i call an listbox.selectitemchange event, i want the proper amount of trackbar and labels to be displayed.
now, it does not work properly.
Some of them get removed when the event is called, some of them don't.
foreach (Label label in Controls.OfType<Label>())
{
if (label.Tag != null && label.Tag.ToString() == "dispose")
{
label.Dispose();
}
}
foreach (TrackBar trackBar in Controls.OfType<TrackBar>())
{
if (trackBar.Tag != null && trackBar.Tag.ToString() == "dispose")
{
trackBar.Dispose();
}
}
for (int i = 0; i < calc; i++)
{
//string[] LineWidthSplitted = lines[lineWidth].Split(' ');
//Int32.TryParse(LineWidthSplitted[2], out WidthValue);
Label Label = new Label();
Label.Name = "TrackbarWidth" + LabelName++;
Label.Tag = "dispose";
Label.Text = "Board -" + LabelName + "- Height:";
Label.Location = new Point(10, 450 + (50 * LabelName));
Label.Size = new System.Drawing.Size(100, 25);
this.Controls.Add(Label);
TrackBar trackBar = new TrackBar();
trackBar.Name = "TrackbarWidth" + trackbarName++;
trackBar.Tag = "dispose";
trackBar.Maximum = 85;
trackBar.Minimum = 65;
trackBar.SmallChange = 5;
trackBar.TickFrequency = 5;
trackBar.Value = 65;
trackBar.Location = new Point(150, 450 + (50 * trackbarName));
trackBar.Size = new System.Drawing.Size(100, 25);
this.Controls.Add(trackBar);
lineWidth += 4;
}
while, when i remove the foreach for the trackbar, all labels get properly displayed.
they all get deleted, en recreated for the pricese amount needed to be created, no exceptions.
Any reason why?
thank you.
Don't use "Dispose" on the labels right away. First remove them. Note that you can't modify the Controls collection inside the foreach so you have to do something like this:
List<Label> itemsToRemove = new List<Label>();
foreach (Label label in Controls.OfType<Label>())
{
if (label.Tag != null && label.Tag.ToString() == "dispose")
{
itemsToRemove.Add(label);
}
}
foreach (Label label in itemsToRemove)
{
Controls.Remove(label);
label.Dispose();
}
If you want to remove all different kinds of controls in one swoop:
List<Control> itemsToRemove = new List<Control>();
foreach (Control ctrl in Controls)
{
if (ctrl.Tag != null && ctrl.Tag.ToString() == "dispose")
{
itemsToRemove.Add(ctrl);
}
}
foreach (Control ctrl in itemsToRemove)
{
Controls.Remove(ctrl);
ctrl.Dispose();
}
I can't test this now, but I think you should also remove the controls from the Form Controls collection where you have added them. By the way, in your case I think you could avoid the OfType extension and use the old fashioned for..loop that will allow to execute just one loop....
for(int x = this.Controls.Count - 1; x >= 0; x--))
{
Control ctr = this.Controls[x];
if (ctr Is Label && ctr.Tag != null && ctr.Tag.ToString() == "dispose")
{
this.Controls.Remove(ctr);
ctr.Dispose();
}
if(ctr Is TrackBar && ctr.Tag != null && ctr.Tag.ToString() == "dispose")
{
this.Controls.Remove(ctr);
ctr.Dispose();
}
}
Notice how removing elements from a collection with a for..loop should be done in reverse order, from the end to start of the collection
I am creating a few checkboxes when I open a form with the following code:
private void OpenFolder_Load(object sender, EventArgs e)
{
int i = 0;
foreach (string file in filesToOpen)
{
Label lbl = new Label();
lbl.Text = Path.GetFileNameWithoutExtension(file);
lbl.Width = 200;
lbl.Height = 25;
lbl.AutoEllipsis = true;
lbl.Location = new System.Drawing.Point(10, 40 + 25 * i);
this.Controls.Add(lbl);
string checkName = "check" + i;
CheckBox check = new CheckBox();
check.Checked = true;
check.AccessibleName = checkName;
check.Location = new System.Drawing.Point(340, 40 + 25 * i);
check.CheckedChanged +=new EventHandler(check_CheckedChanged);
this.Controls.Add(check);
CheckBoxes.Add(check);
i++;
}
and I am trying to check the state of the checkboxes everytime one changes to toggle my OK button (the user can validate only if there are a certain number of the checkboxes checked)
here is the code I use, but it fails as I am not able to target the checkboxes:
private void check_CheckedChanged(Object sender, EventArgs e)
{
for (int i = 0; i < filesToOpen.Count(); i++)
{
string tbarName = "tbar" + i;
string checkName = "check" + i;
CheckBox ckb = this.Controls.OfType<CheckBox>()
.Where(c => c.AccessibleName.Equals(checkName)) as CheckBox;
TrackBar tkb = this.Controls.OfType<TrackBar>()
.Where(t => t.AccessibleName.Equals(tbarName)) as TrackBar;
//TrackBar tkb = this.Controls.Find(tbarName, false).First() as TrackBar;
//CheckBox ckb = this.Controls.Find(checkName, false).First() as CheckBox;
if (ckb.Checked == true)
{
//do stuff
}
}
}
what am I doing wrong/really wrong?
Given that you add the checkboxes to your own list:
CheckBoxes.Add(check);
it would be simpler to loop over that rather than trying to find the control associated with the file:
foreach (var checkBox in CheckBoxes)
{
if (checkbox.Checked)
{
// Do stuff...
}
}
However, you shouldn't need to use a separate list. This line is wrong:
CheckBox ckb = this.Controls.OfType<CheckBox>()
.Where(c => c.AccessibleName.Equals(checkName)) as CheckBox;
Where returns a IEnumerable<CheckBox> but you are trying to cast it directly to a CheckBox which will return null. What you should have is:
CheckBox ckb = this.Controls.OfType<CheckBox>()
.Where(c => c.AccessibleName.Equals(checkName)).First();
You will still need to check to see if ckb is null (just in case there is nothing on the list) but this should return you the control you are looking for.
Check the type of "this" and then check its Controls collection - your checkboxes are probably a few iterations down the tree.
You'd need some kind of recursive find controls function such as the one found in this article
Iterating over all the checkboxes with every check is not required and is readlly hard processing work. Instead when creating you always know in what state you've created those - so just keep the count of "Checked" checkboxes. When a checkbox being checked increment the count, and when one unchecked - take out 1 from the count. And later have a check: "if (count == requiredCount) {//Logic here}"
So the code will look like:
private int checkedCount;
private void check_CheckedChanged(Object sender, EventArgs e)
{
this.checkedCount += (sender as CheckBox).Checked?1:-1;
if(this.checkedCount == requiredCount)
{
//do stuff
}
}
Good luck with development.