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
Related
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.
I have 2 comboboxes created with Ajax Toolkit. One of them has a list of systems. I wanted to fill the other combobox with a list of subsystems whenever a system is selected. I did not use DisplayMember or ValueMember and most examples are using them.
.aspx side just in case:
<ajaxToolkit:ComboBox ID="cbox1" runat="server" OnSelectedIndexChanged="cbox1_SelectedIndexChanged" />
Is this doable with what I'm trying? Is the event I used correct for the situation?(I guess not,but other events seem to be unrelated) Let me show you the code:
protected void fillSystemCombo()
{
var sysOperations = new ModelOperations.ConstantSystem.ConstantSystemOperations();
var sys = sysOperations.GetSystemList().TransactionResultList;
foreach (var item in sys)
{
cbox1.Items.Add(item.description);
}
}
This works fine and I can see the systems in my first combobox.
This is what I tried for populating the second one:
protected void cbox1_SelectedIndexChanged(object sender, EventArgs e)
{
var subSysOperations = new ModelOperations.ConstantSubSystem.ConstantSubSystemOperations();
int index = Convert.ToInt32(cbox1.SelectedItem.Value);//i think this should get the id...
var subsys = subSysOperations.GetSubSystemList().TransactionResultList;
foreach (var item in subsys)
{
if (item.sysID == index)
{
cbox2.Items.Add(item.description);
}
}
}
sysID is the foreign key in SubSystem which is the ID of System. By the way, my SelectedIndexChanged event never fired when I was debugging the program even though I clicked on an item in combobox.
I've actually found the answer after carefully reading the parameters taken by Items.Add. It wants a ListItemso if I create a ListItem inside the loop I can finally add my items with both a Text and a Value like this:
foreach (var item in sys)
{
combo1.Items.Add(new ListItem { Text = item.description, Value = item.ID.ToString() });
}
After that I can get the index with
int index = Convert.ToInt32(combo1.SelectedValue);
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
I have a long list selector that is populated dynamically meaning the user adds items there.
This is the way items are added.
source.Add(new ShoppingList(Item1.Text));
Item1 is a textbox via which user adds stuff to the list.
I have a long list selector. Let's call it LLS. I want that when a certain itam is tapped, the text within the item is copied and pasted into a textblock.
So far I have tried the following:
string item = LLS.SelectedItems.ToString(); TextBlock.Text = item;
How can this be achieved?
Thank You for your attention and answers.
ShoppingList sitem = LLS.SelectedItem as ShoppingList;
string item = string.empty;
if ( sitem != null )
{
item = sitem. (property where you text is stored)
}
You have to subscribe to SelectionChanged Event:
private void LLS_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (LLS.SelectedItem != null)
{
ShoppingList item = LLS.SelectedItem as ShoppingList;
TextBlock.Text = item.yourProperty;
}
}
BTW - there are already similar questions: one, two and probably more.
I have a ListView which displays multiple rows of ListViewItems. The user is able to edit one of these items through a dialog which opens after clicking 'Edit.' When the dialog closes I would like to modify the selected ListViewItem such that it reflects the new settings.
Here is how I currently update my item:
private void btnEditSnmpV3Setting_Click(object sender, EventArgs e)
{
if (lstVwSNMPv3Settings.SelectedItems.Count > 0)
{
ListViewItem selectedItem = lstVwSNMPv3Settings.SelectedItems[0];
NetworkDiscoverySnmpSetting settings = (NetworkDiscoverySnmpSetting)selectedItem.Tag;
NetworkDiscoverySnmpV3SettingsDialog dialog = new NetworkDiscoverySnmpV3SettingsDialog(settings);
//Pass in the owner for centering of dialog.
if (dialog.ShowDialog(this) == DialogResult.OK)
{
selectedItem.SubItems.Clear();
selectedItem.Text = settings.SnmpV3Username;
selectedItem.SubItems.Add(settings.SecurityMode.ToString());
selectedItem.SubItems.Add(settings.AuthenticationProtocol.ToString());
selectedItem.SubItems.Add(settings.PrivacyProtocol.ToString());
selectedItem.Tag = settings;
}
}
}
I found this to be a poor solution due to the fact that I need to touch code in multiple places if my ListView's number of columns changes.
I handled this code-reuse issue during the 'Add' event (as opposed to 'Edit') by giving NetworkDiscoverySnmpSetting a utility method:
public ListViewItem ToListViewItem()
{
ListViewItem listViewItem = new ListViewItem();
listViewItem.Text = SnmpV3Username;
listViewItem.SubItems.Add(SecurityMode.ToString());
listViewItem.SubItems.Add(AuthenticationProtocol.ToString());
listViewItem.SubItems.Add(PrivacyProtocol.ToString());
listViewItem.Tag = this;
return listViewItem;
}
which is used like so:
private void btnAddSnmpV3Setting_Click(object sender, EventArgs e)
{
NetworkDiscoverySnmpSetting settings = new NetworkDiscoverySnmpSetting(NetworkDiscovery.ID);
NetworkDiscoverySnmpV3SettingsDialog dialog = new NetworkDiscoverySnmpV3SettingsDialog(settings);
//Pass in the owner for centering of dialog.
if (dialog.ShowDialog(this) == DialogResult.OK)
lstVwSNMPv3Settings.Items.Add(settings.ToListViewItem());
}
Unfortunately, ListView.SelectedItems does not allow collection-modification. As such, this does not compile:
lstVwSNMPv3Settings.SelectedItems[0] = settings.ToListViewItem();
How should I change my first code-snippet so that I do not need to update my code in multiple places when ListView's columns change?
You can modify the element itself rather than replacing it with another one, because ListViewItem is a class, so it's a reference type.
In order to do this follow these steps:
get currently selected item and save it to variable like this: ListViewItem selectedItem = lstVwSNMPv3Settings.SelectedItems[0];
modify your ToListViewItem method to void ToListViewItem(ListViewItem listViewItem) (return void and take ListViewItem object as parameter and modify it instead of creating a new object. It should also rather modify properties of existing subitems than creating new ones. It can look more or less like this:
public void ToListViewItem(ListViewItem listViewItem)
{
listViewItem.Text = SnmpV3Username;
listViewItem.SubItems[0].Text = SecurityMode.ToString();
listViewItem.SubItems[1].Text = AuthenticationProtocol.ToString();
listViewItem.SubItems[2].Text = PrivacyProtocol.ToString();
listViewItem.Tag = this;
}
call ToListViewItem(selectedItem);
you don't have to assign the modified item back to the collection, because you use a reference, which means you've just modify the same object that's in the ListView
I did a quick test and the method seems to modify texts of existing items without issues.
ListViewItems have a bool Selected property that you can toggle to make them selected or not selected.
A much simpler solution, that worked for me:
lstVwSNMPv3Settings.Items[lstVwSNMPv3Settings.SelectedIndices[0]] = myNewItem;
But be careful to first make sure there is an item selected:
if (lstVwSNMPv3Settings.SelectedIndices.Count > 0) { ... }