Remove() function seems only removes a couple of controls at a time - c#

I'm trying to remove multiple controls at a time (textboxes and checkboxes) in a windows form application. Basically, I have a row of textboxes and a corresponding checkbox. When the "delete rows" button is clicked, it should remove all rows that have been selected. But it only seems to remove two or three at a time (the same two or three in each row, but there doesn't seem to be any reason it selects those same two or three). I've attached a couple of screenshots showing what is happening.
Here I've selected a couple of rows:
After hitting delete once:
After hitting delete twice:
This just shows the names of each element. As you can see, the names are the same in each row:
Here is the relevant code:
//Gets a list of all ticked checkboxes
public List<string> checkForChecked()
{
var allCheckboxes = tabPage1.GetAllControlsOfType<CheckBox>();
int count = allCheckboxes.Count<CheckBox>();
List<string> checkedChecks = new List<string>();
foreach(Control c in tabPage1.Controls)
{
if(c is CheckBox && ((CheckBox)c).Checked)
{
checkedChecks.Add(c.Name.ToString());
}
}
return checkedChecks;
}
//The button click. Loop through elements and remove ones with the right name
private void button2_Click(object sender, System.EventArgs e)
{
List<string> toDelete = checkForChecked();
foreach (var val in toDelete)
{
foreach (Control item in tabPage1.Controls.OfType<Control>())
{
if (item.Name == val.ToString())
{
tabPage1.Controls.Remove(item);
}
}
}
}
I'm using a windows form app, no asp or other web technology.

It's a common error of removing items from a collection while enumerating it. For example:
foreach (Control c in Controls)
Controls.Remove(c);
will remove only half of the controls and leave every second control.
Some of the common solutions to the general problem are:
removing items in reverse order starting from the end of the collection
removing the first found item until no items found
enumerating a copy of the collection
In your case, both methods can be combined to something like this ( .Find returns Control[] ) :
foreach (var cb in tabPage1.Controls.OfType<CheckBox>())
if (cb.Checked)
foreach (var c in tabPage1.Controls.Find(cb.Name, false))
tabPage1.Controls.Remove(c)

You could try something more like:
private void button2_Click(object sender, EventArgs e)
{
List<CheckBox> CheckedBoxes = new List<CheckBox>();
foreach (CheckBox cb in tabPage1.Controls.OfType<CheckBox>())
{
if (cb.Checked)
{
CheckedBoxes.Add(cb);
}
}
foreach (CheckBox cb in CheckedBoxes)
{
string cbName = cb.Name;
cb.Dispose();
// ... probably more code in here to find the other controls
// ... in the "row" based on "cbName"
}
}

You are iterating the controls collection and removing a control, which effects the index of every item in the collection every time you call the Remove() method. This probably messes with your iterator in the foreach, thus the 'skipping' behavior. When removing from any controls collection it is best not to be iterating it at the same time. Moreover, when looping on a Controls collection where you are removing items, code your loops as iterators working backwards.
for (var i=Parent.Controls.Count-1; i >=0; i--)
{
if (someCondition) Parent.Controls.Remove(Parent.Controls[i]);
}
Here is a bit cleaner version of your code. The list returned from the CheckForChecked method is now a list of controls which is easier to use than a list of the controls names.
In the remove, we are iterating the list of things to delete, not the controls collection from which we are deleting.
//Gets a list of all ticked checkboxes
public List<Control> CheckForChecked()
{
var tabPage1 = new TabPage();
var results = new List<Control>(0);
results.AddRange(from Control c in tabPage1.Controls
where c is CheckBox && (c as CheckBox).Checked
select c);
return results;
}
//The button click. Loop through elements and remove ones with the right name
private void button2_Click(object sender, System.EventArgs e)
{
var toDelete = CheckForChecked();
var tabPage1 = new TabPage();
foreach (var val in toDelete.Where(val => tabPage1.Controls.Contains(val)))
{
tabPage1.Controls.Remove(val);
}
}
Hope this helps someone.

Related

TextBox.TextChange to update an onscreen ListBox in C#

Now this might not sound horribly cryptic, but I'm a little new to handling on-screen elements and Forms and such in C# so bear with me here. It's also going to be a bit of a long one, as I feel I should provide as much information as possible.
I have a TextBox object, which is added to my fSelect (which is my Form in this case), and it's done like this:
TextBox searchBox = new TextBox();
fSelect.Controls.Add(searchBox);
searchBox.Location = new Point(40, 255);
searchBox.Width = 520;
searchBox.TextChanged += new EventHandler(searchBox_TextChanged);
Now, as you can see every time something changes in the TextBox a certain operation is carried out. That operation would be:
private void searchBox_TextChanged(object sender, EventArgs e)
{
TextBox s = (TextBox)sender;
bool b = false;
List<string> f = new List<string>();
ListBox updatedLb = new ListBox();
updatedLb.Size = new System.Drawing.Size(568, 255);
updatedLb.SelectionMode = SelectionMode.MultiSimple;
foreach (string value in lb.Items)
{
if (value.IndexOf(s.Text, StringComparison.OrdinalIgnoreCase) >= 0)
{
f.Add(value);
b = true;
}
}
for (int i = 0; i < f.Count; i++)
{
updatedLb.Items.Add(f[i]);
}
s.FindForm().Controls.Remove(lb);
s.FindForm().Controls.Add(updatedLb);
}
Looking at that code, there's one element that needs explanation, lb. lb is in this case a public ListBox which contains a set amount of string elements. It is defined as a ListBox outside any method.
Now, in the method my searchBox was defined I filled my lb as follows:
foreach (string value in list)
{
lb.Items.Add(value.title);
}
(If it matters, I should also mention that the adding of strings to lb happens before it is inserted into the fSelect Form later.)
Now, for those who haven't guessed my question yet; I wish to present a user with a Form that has a ListBox on it. The elements in this ListBox will be whatever elements contain the string the user types into searchBox, AS the user types it.
The problem is, that the search is only carried out once, and the only thing that is searched for is the first key the user puts in the searchBox. IE: If I was the user and I was going to search for "key", and the first letter was "k", the list would update to show anything with "k" in the title. But when I tried to type the "e" it would not update or change. It would also not go revert back to the old list if the user removes part of the text in searchBox.
How do I go about getting a Form that shows me a ListBox with elements based on what a user entered into searchBox?
The problem is that you are looping though lb, which you delete the first time the text_Changed-Event triggers. So you don't have any items more that you can loop through and filter your ListBox. The easiest way to fix that would be not getting the items you loop though from the ListBox itself, but from a List<string> that you use to supply the ListBox.
Edit
Here's the way I'd do it:
In your form class you add a List<string>, this list is the base for your ListBox.
List<string> listBoxItems = new List<string>();
Now you can fill this list in your constructor with the items you want. Here are some ways to do it. For my example I'll just add all the items manually for simplicity.
listBoxItems.Add("Item1");
listBoxItems.Add("abc");
listBoxItems.Add("CDE");
listBoxItems.Add("Abra");
Then you want to fill your ListBox with the items from your List.
foreach (var s in listBoxItems)
{
listBox1.Items.Add(s);
}
Now you just need to handle the Text_Changed-Event. Instead of creating a new ListBox every time, you just edit the one we already have in the form and instead of looping though the items in the ListBox, which are already filtered, you loop though all the item saved in your List<string>.
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox s = (TextBox)sender;
listBox1.Items.Clear();
foreach (string value in listBoxItems)
{
if (value.IndexOf(s.Text, StringComparison.OrdinalIgnoreCase) >= 0)
{
listBox1.Items.Add(value);
}
}
}
In your code, you created a new ´ListBox´ each time you go into the loop, but you still try to use the items from your old one.
I hope this helps,
Dominik

How can I programmatically select specified ListBox items?

I've got this code that populates a ListBox when a flyout is opened:
private void flyoutOpenPhotosets_Opened(object sender, object e)
{
lstbxPhotosets.ItemsSource = PhotraxSQLiteUtils.GetPhotosets();
foreach (String pset in App.CurrentlyMappedPhotosets)
{
int lstbxIndex = lstbxPhotosets.Items.IndexOf(pset);
if (lstbxIndex >= 0)
{
lstbxPhotosets.Items[lstbxIndex].? what now?
}
}
}
GetPhotosets returns a List. That part works (the list box is populated with the appropriate string values)
The problem is with the rest of the code (the foreach block).
CurrentlyMappedPhotosets is also a List. I want matching members among the strings in CurrentlyMappedPhotosets and those in the ListBox to cause the item in the ListBox to be selected when the flyout displays.
I was hoping to do be able to do something like this:
lstbxPhotosets.Items[lstbxIndex].Selected = true;
...but lstbxPhotosets is disallowing that.
So how can I programmatically select specified ListBox items?
Use
lstbxPhotosets.SelectedIndex = lstbxIndex

Dynamically creating checkboxes from an array and then iterate through them to check for selected

I have been creating a list of checkboxes from returned information from a webservice. The checkboxes render as expected but when I try and then read them to check if they have been selected the code cannot find them.
I have created a panel called planList and had the code loop creating the dynamic list of boxes - then on a button being pressed it should iterate through the list of checkboxes to see if the user has selected any values. The code doesn't seem to be picking up any checkboxes unless created dynamically. Anyone able to assist? At the moment I am just trying to pull out the ID if it picks up a checkbox
Code:
planList.Controls.Add(new LiteralControl("<h2>Plan List </h2>"));
foreach (string[] ar in ws.planS(this.txtGetDetails.Text)) {
CheckBox cb = new CheckBox();
cb.Text = ar[1].ToString();
cb.ID = ar[0];
planList.Controls.Add(cb);
planList.Controls.Add(new LiteralControl("<b> Application ID: " + ar[2] + "</b>"));
planList.Controls.Add(new LiteralControl("<br>"));
}
protected void Uploadbutton_Click1(object sender, System.EventArgs e) {
foreach (Control c in planList.Controls) {
CheckBox chx = c as CheckBox;
if (chx != null) {
var planid = c.ID;
}
}
}
You have to override the CreateChildControls method and add the checkboxes there. With this, you are creating the controls tree before loading the ViewState, which contains the data of which one was checked.

Iterating through textboxes using asp.net

I am building a page with asp.net. I have a form with a table that contains TextBoxes and a submit button. When the form is submitted, I want to grab all the text that was entered into the TextBoxes and operate on them. To do this, I have the following method:
protected void Button1_Click(object sender, EventArgs e)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (Control c in this.Controls)
{
if (c.GetType().Name == "TextBox")
{
TextBox tb = (TextBox)c;
sb.AppendLine(tb.Text);
}
}
Label1.Text = sb.ToString();
}
The problem with this is that the controls apparently doesn't include any of my textboxes. When I iterate through the controls and print out their names, the only one I get is "site_master." (I also tried Controls and Page.Controls instead of this.Controls).
Is there something wrong with my iterator? Is there another way in which I could iterate through all of the textboxes in the table or page? What is the best way to accomplish this?
Would it be too much to build a List<Textbox>, given you know all your textbox controls?
List<Textbox> txtBoxes = new List<Textbox>();
txtBoxes.Add(tb1);
txtBoxes.Add(tb2);
//etc..
Then you have a nice list to work with
If I knew the controls were all in a given containing control, I would simply poll the controls of that control. For example, this.Form.Controls. However, if they could be nested within other child controls, then you could recursively explore the depths from a common outer container.
private IEnumerable<T> FindControls<T>(Control parent) where T : Control
{
foreach (Control control in parent.Controls)
{
if (control is T)
yield return (T)control;
foreach (T item in FindControls<T>(control))
yield return item;
}
}
So this would allow you to retrieve all TextBox children.
List<TextBox> textBoxes = this.FindControls<TextBox>(this).ToList();
string output = string.Join(",", textBoxes.Select(tb => tb.Text));
I'm going to assume that you are using web forms ASP.NET. Typically you declare your controls on the aspx page using something similar to
<asp:TextBox ID="someId" runat="server/>
If you have done this then in your code behind your should just be able to reference the variable someId and the property Text to get/set the text in the control.
If you are building the controls dynamically on the server you should be able to stick them in a list and iterate through it. Make sure you are creating the controls and adding them to the table during the correct part of the page lifecycle. When you add them to a cell in the table you could also keep a reference to the control in a list and just enumerate through that list in your event handler.
Maybe something along the lines of (I didn't compile this so there are probably issues):
public class MyPage: Page
{
private List<TextBox> TxtBoxes = new List<TextBox>();
//registered for the preinit on the page....
public void PreInitHandler(object sender, EventArgs e)
{
for(var i = 0; i < 2; i++)
{
var txtBox = new TextBox{Id = textBox+i};
//...add cell to table and add txtBox Control
TxtBoxes.Add(txtBox);
}
}
}

How to Get Updates(changed Items) From CheckboxList?

I have a Checkbox list in my page and its datasource set programatically in PreLoad() event:
protected void Page_PreLoad()
{
if (!Page.IsPostBack)
{
CheckBoxList1.DataSource = NoK.AcceptedNoks((Guid)Membership.GetUser().ProviderUserKey);
CheckBoxList1.DataTextField = "FullName";
CheckBoxList1.DataValueField = "NoKId";
CheckBoxList1.DataBind();
}
foreach (ListItem chk in CheckBoxList1.Items)
{
if (PrivateMessage.HasAccess(Request.QueryString["MessageId"], chk.Value))
{
chk.Selected = true;
}
}
}
as you see in foreach will check for whether an item must be checked or Not. and it works nice. this means that end-user can Edit list Items and by default some of Item has been checked. now I want to get items by clicking a Button:
protected void UpdateRightBtn_Click(object sender, EventArgs e)
{
var SelectedNokIds =
CheckBoxList1.Items
.OfType<ListItem>()
.Where(li =>
li.Selected == true)
.Select(l => new Guid(l.Value));
}
but the Items in SelectedNokIds are still Old Items and if user change checkboxes no effect apeares in SelectedNokIds. Why???
Please Help!
It looks like it is because you are re-setting the values again at postback, effectively clearing the user's selection. You need to only initialize the values when it is not a postback.
foreach (ListItem chk in CheckBoxList1.Items){ if (PrivateMessage.HasAccess(Request.QueryString["MessageId"], chk.Value)) { chk.Selected = true; }}
This line fires on every page load, so that would reset the selection (at least the ones that set selected to true). Shouldn't that be within !Page.IsPostback too? And in the update button, you could rebind there...
If you need to figure out what changed, you need to query the items in the data source again, and cross-reference those against the new selection list.

Categories