I am trying to remove selected items in a ListBox which is bound to ObservableCollection.
var selectedFiles = MyList.SelectedItems;
foreach (cListEntry item in selectedFiles)
{
_myList.Remove(item);
}
"Collection was modified; enumeration operation may not execute"
What is the proper way of doing this?
You can't modify the collection while enumerating it as evident from the exception itself.
Explanation:
When you remove item from ObservableCollection, MyList.SelectedItems gets update since ObservableCollecton implement INotifyCollectionChanged.
Now, selectedFiles is pointing to same reference which results in modifying it.
Solution
Instead create a new list and enumerate over that so that any change in ObservableCollection doesn't reflect back to list which you are enumerating. This will work:
var selectedFiles = MyList.SelectedItems.Cast<object>().ToList();
foreach (cListEntry item in selectedFiles)
{
_myList.Remove(item);
}
This happens when trying to modify an ObservableCollection<T> that is bound to a ListBox, for example. This is how you deal with that:
ObservableCollection<Employee> itemsToRemove = new ObservableCollection<Employee>();
foreach (Employee item in lsbxNames.SelectedItems)
{
itemsToRemove.Add(item);
}
foreach (Employee item in itemsToRemove)
{
((ObservableCollection<Employee>)lsbxNames.ItemsSource).Remove(item);
}
Create a new ObservableCollection<T> called itemsToRemove, with the same T as your collection you are trying to modify.
Iterate through your nodes of SelectedItems in your ListBox. Add them to itemsToRemove.
Iterate through itemsToRemove. Cast the ListBox ItemsSource to an ObservableCollection<T> and remove the matches in itemsToRemove from it.
Reference: http://docs.telerik.com/devtools/wpf/controls/radgridview/managing-data/deleting-entry
So this would mean you should be able to do this:
ObservableCollection<cListEntry> itemsToRemove = new ObservableCollection<cListEntry>();
foreach (cListEntry item in MyList.SelectedItems)
{
itemsToRemove.Add(item);
}
foreach (cListEntry item in itemsToRemove)
{
((ObservableCollection<cListEntry>)MyList.ItemsSource).Remove(item);
}
I'm not sure what _myList is, but you don't need to modify it. Just go directly to the ListBox.
Related
I have a list that is bound to a datagrid, a property of the items is a boolean and is bound to a checkbox in the datagrid.
How can I allow only one checkbox to be selected?
For example, if one checkbox is selected, then the other checkboxes should be unselected.
What I have tried so far (in the ViewModel, Update is the boolean property):
var update = item.Update;
Items.ForEach(x => x.Update = false);
Items = new List<Item>(Items);
item.Update = update;
But this not efficient and it throws an Exception that the list was modified (collection was modified; enumeration operation may not execute).
Is there an efficient way to get the job done?
Edit: I'm using Binding to bind the list, the list is from type List<>, and the items of the list implement INotifyPropertyChanged
I've commented the issue that you have in this code:
var update = item.Update;
Items.ForEach(x => x.Update = false);
Items = new List<Item>(Items); //This creates a new, empty Items list
item.Update = update; //this item relates to an item in the collection that no longer exists
You should have ObservableCollection<Items> Items so that if you add/remove items from your list the Binding will follow and Item implementing INotifyPropertyChanged. As ObservableCollection doesn't support ForEach your code becomes:
var update = item.Update;
foreach(var element in Items)
{
element.Update=false;
}
item.Update = update;
My problem is; When click a button I input items in List, next in DropDownList. Problem is where i click button again exist items again into my DropDown.
How to solve this problem(sorry for image)?
List<string> companyList = new List<string>();
foreach (string item in companyList.ToList())
{
companyList.Remove(item); ----> this not working.......
}
foreach (SPListItem item in myItemCol)
{
companyList.Add(item["Company"].ToString());
}
companyList.Sort();
foreach (string item in companyList.Distinct())
{
ddComFilter.Items.Add(item.ToString());
}
You can use the Contains method to check if it is already there
if(!ddComFilter.Items.Contains(items.ToString())
{
ddComFilter.Items.Add(item.ToString());
}
This will only add the item if it is not already in the dropdown
You could check for existence of the item before add it to the list.
foreach (SPListItem item in myItemCol)
{
if(!companyList.Contains(item["Company"].ToString())
{
companyList.Add(item["Company"].ToString());
}
}
Then you need to clear the ddComFilter before adding the values to it:
companyList.Sort();
ddComFilter.Items.Clear();
foreach (string item in companyList.Distinct())
{
ddComFilter.Items.Add(item.ToString());
}
Alternate solution:
You can bind the ddComFilter using the generated list, instead for iterating the collection and add one-by-one. if so you need not to clear the collection, remove items etc. The code for this will be:
ddComFilter.Datasource = companyList;
ddComFilter.DataBind();
Here is an useful article for you
You should clear your dropdown before adding list as items:
companyList.Sort();
ddComFilter.Items.Clear(); // clear
foreach (string item in companyList.Distinct())
{
ddComFilter.Items.Add(item.ToString());
}
I have a c# wpf listbox and I am trying to get the values from the selected items. I cannot use a foreach loop (every value I find will remove an item from the listbox). But this seems impossible.
What I want is somthing like this:
for (int i = <numberofselecteditems> - 1; i >= 0; i--)
{
string displaymembervalue = listbox.selecteditem[i].displaymembervalue;
}
I have a solution which involve to loop over all the listbox items twice. This is not really an option since it will slow the app too much.
Like I said before, this is NOT the
System.Windows.Forms.Listbox
but the
System.Windows.Controls.Listbox
thank you!!
J.
See the solution here, it is essentially using a foreach in the follolwing fashion:
foreach (var item in listBox1.SelectedItems)
{
// Do what you want here... Console.WriteLine(item), etc.
}
If you really want to do it with a for loop rather than a foreach, then do the following:
for(int i = selectedItems.Count - 1; i >= 0; --i)
{
var item = selectedItems[i];
// Do what you want with item
}
Here is your XAML bound to a Observable collection
<ListBox ItemsSource="{Binding items}"/>
Here is your observable collection of objects
private ObservableCollection<Object> _items;
public ObservableCollection<Object> items{
get{ return _items; }
}
Here is the enumeration over them and the removing of each item
for(int x = 0; x < _items.Count; x++){
_items.Remove(_items.Where(n => n == _items[x]).Single());
//You may have to do a notify property changed on this if the UI Doesnt update but thats easily googled.
//Traditionally it would update. However since you are bound to items Im not sure if it will update when you manipulate _items
}
Create a second list. You still have to iterate twice, but the second iteration is not over the entire list of items.
var items List<ListBoxItem>;
foreach (var item in listbox1.SelectedItems)
items.Add(item);
foreach (var item in items)
listbox1.Remove(item);
Alternatively instead of enumerating twice you can create a copy of the list of objects and then remove the items from the original list while still enumerating.
foreach (var selectedItem in listBox1.SelectedItems.Cast<List>())
{
//remove items from the original list here
}
Example
I have
ObservableCollection<Employee> // 1
ObservableCollection<Boss>// 2
ObservableCollection<Department> //3
ObservableCollection<T> // main >>>I want 1, 2, 3 ObservableCollection to main ObservableCollection
How to do?
edited1: I want to add them to be a list. Not for each item.
edited2: I have to display 3 lists of field on the wpf application. the 2nd list can add/remove item in the list.
** please let me know if it unclear.
Since what you want to do is to add a collection to a collection, but those collection types are not compatible, I'd try this
ObservableCollection<Employee> _employees = ...
ObservableCollection<Boss> _bosses = ...
ObservableCollection<Department> _departments = ...
ObservableCollection<IList> _collections = ...
_collections.Add(_employees);
_collections.Add(_bosses);
_collections.Add(_departments);
Note that the generic argument to the _collections collection is IList. ObservableCollection<T> implements IList and is therefore assignable to things of that type, even for different Ts among the sets.
Observable Collection does not support something like AddRange functionality where in you can add your entire list to an already existing list.
By the way, if all your observable collection is of type T, you could use foreach to iterate through and do the adding, something like below:
var observableCollection1 = new ObservableCollection<string>();
var observableCollection = new ObservableCollection<string>();
foreach (var element in observableCollection1)
{
observableCollection.Add(element);
}
But, what you are trying to do, as from my understanding, is trying to add an observable collection of type department to a different type T, which is not possible unless you try to add it to an observable collection of simply, objects. Warning: Note, you might need to box/unbox afterwards.
Code Snippet:
var observableCollection_Department = new ObservableCollection<Department>();
var observableCollection_Employee = new ObservableCollection<Employee>();
var observableCollection_Boss = new ObservableCollection<Boss>();
var observableCollection = new ObservableCollection<object>();
foreach (var element in observableCollection_Department)
{
observableCollection.Add(element);
}
foreach (var element in observableCollection_Employee)
{
observableCollection.Add(element);
}
foreach (var element in observableCollection_Boss)
{
observableCollection.Add(element);
}
List itemsToMove = new List();
foreach (ListViewItem item in lvScanRepository.SelectedItems)
{
itemsToMove.Add(item);
}
foreach (ListViewItem item in itemsToMove)
{
if (!lvBatch.Items.Contains(item))
{
lvScanRepository.Items.Remove(item);
lvBatch.Items.Add(item);
}
}
A ListViewItem can't belong to more than one ListView at the same time, so this condition:
if (!lvBatch.Items.Contains(item))
... will always be true.
What criteria do you want to use to determine whether the item in one ListView is "similar" to an item in another? Depending on that, you have a couple of options:
ListViewItem has a property called Name which can be used to uniquely identify items in a ListView. You can then call Items.ContainsKey(String) to see if an item exists with that name.
Alternatively you can search in lvBatch to find an item with the same Text as the one you're trying to add:
if (!lvBatch.Items.Cast<ListViewItem>().Any(i => i.Text == item.Text))
(You need to cast because ListViewItemCollection doesn't actually implement IEnumerable<ListViewItem>.)