C# Update combobox bound to generic list - c#

I have a combobox on my form that is bound to a generic list of string like this:
private List<string> mAllianceList = new List<string>();
private void FillAllianceList()
{
// Add alliance name to member alliance list
foreach (Village alliance in alliances)
{
mAllianceList.Add(alliance.AllianceName);
}
// Bind alliance combobox to alliance list
this.cboAlliances.DataSource = mAllianceList;
}
The user may then add or remove items in the combobox.
I have read elsewhere that by simply adding or removing the item in the generic list, the contents of the combobox should automatically be updated; same thing should occur if I use Sort() on it.
But for some reason, I cannot make this work. I can see the combobox's DataSource property is correctly updated as I add/remove/sort items, but the contents displayed in the combobox are not those in the DataSource property.
I am surely missing something or doing something wrong.
Thanks in advance!
EDIT:
The answer I chose solved the issue for adding and removing, but a BindingList object cannot be sorted, and this is necessary for me. I've found a solution where a custom class is built by inheriting BindingList and adding sorting capabilities, but I would like to know if there's an easier solution in my case.
Any suggestions on how to solve this easily?

The easiest way around this would be to simply use a BindingList like so:
private List<string> mAllianceList = new List<string>();
private BindingList<string> bindingList;
private void FillAllianceList()
{
// Add alliance name to member alliance list
foreach (Village alliance in alliances)
{
mAllianceList.Add(alliance.AllianceName);
}
bindingList = new BindingList<string>(mAllianceList);
// Bind alliance combobox to alliance list
this.cboAlliances.DataSource = bindingList;
}
Then, from here on out, just deal with the binding list to add and remove items from there. That will remove it both from the List and from the ComboBox.
EDIT: To answer your question regarding sorting, I guess the easiest (but possibly "hacky" way to do it would be something like this:
mAllianceList.Sort();
bindingList = new BindingList<string>(mAllianceList);
this.cboAlliances.DataSource = bindingList;
So basically, after you sort, you create a new binding list and reset the data source. Maybe there's a more elegant way to go about this, however this should work.

Related

How to filter my WPF-combobox?

first of all: sorry for the bad english, its not my first language.
I am currently working on a project where i have a list of persons which i want to list up in a combobox.
I want the combobox to get filtered, so that there are only the people listed up i am searchig for.
For example if i type in the combobox "Joh", there should only be people who start with "Joh" like "John", "Johann", ... .
The next thing is, my combobox is not "editable", how can i make it that i can write in it ? Currently it is "locked" ...
I hope you understand whats my problem, and how to solve it!
So one of the cool things about WPF is that it allows for binding. Specifically, binding properties in your code to controls in your UI. So to have a filtered combobox I would bind a list of whatever object you have to your combobox, something like below:
C#:
private List<Person> myList = new List<Person>();
public List<Person> MyList
{
get { return myList; }
set { myList = value; }
}
WPF:
<ComboBox Name="cboObjects" ItemsSource="{Binding MyList}"/>
That sets up your combobox to be bound to a list. So now we have to filter it down. So next I would use the KeyDown event to fire everytime the Combobox gets typed into.Then during that event you could capture the user's text, and try to find anything that matched that in the list, then set your list property equal to what was found..
private void cboObjects_KeyDown(object sender, KeyEventArgs e)
{
string temp = ((ComboBox)sender).Text;
var newList = MyList.Where(x => x.Name.Contains(temp));
MyList = newList.ToList();
}
Now your list of people objects has been filtered! Although there are a few issues with doing it this way, like the fact that you now no longer have your original list. Another thing is, if you go this approach, your UI will not update unless its told to. So make use of the INotifyPropertyChanged interface. It will essentially fire an event anytime you update a property which then tells your UI to retrieve the value again.
Finally, As for your combobox not being editable try setting IsReadOnly = false, and IsEditable = true!
This is actually built in!
What you need to do is set the following properties on your ComboBox control.
<ComboBox ItemsSource="{Binding PersonList}"
IsTextSearchEnabled="True"
TextSearch.TextPath="Name"/>
This example assumes you have a PersonList of type Person, in which type Person has a property of Name.
You'll want to set the TextSearch.TextPath to whatever property you want to search on (based on a property of the items in your ItemsSource collection).
More info, see https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.combobox.istextsearchenabled

Bind List<Double> to WinForms-Listbox

I have an small (probably dumb) issue with databinding. I try to bind a List
List<double> _measuredValues = new List<double>();
to a winforms ListBox.
In Form_Load I set:
lstMeasuredValues.DataSource = _measuredValues;
When I update the values, nothing appears?!
_measuredValues.Add(numBuffer);
One thing I thought about is a data type issue. But how do I change the type just to change it into a string?
lstMeasuredValues.DataSource = _measuredValues.ToString().ToList();
Another reason might be that the upper line of code is within another thread. But I think this should not be the problem.
How can I bind this list?
When I update the values, nothing appears?!
_measuredValues.Add(numBuffer);
In order to allow UI to reflect the data source modifications, the data source must provide some sort of a change notification. WinForms list data binding infrastructure uses ListChanged event of the IBindingList Interface. There is a standard provided BindingList<T> class which can be used instead of List<T> to get the desired behavior. All you need is changing this line
List<double> _measuredValues = new List<double>();
to
BindingList<double> _measuredValues = new BindingList<double>();
Another reason might be that the upper line of code is within another thread. But I think this should not be the problem.
That's not good. You must make sure you don't do that because ListChanged event is expected to be raised on the UI thread.
The better way is to clear the items and assign the DataSource again:
lstMeasuredValues.Items.Clear()// clear all items
lstMeasuredValues.DataSource = _measuredValues;
Or even you can define your own refresh function and call like the following:
public void RefreshList()
{
lstMeasuredValues.Items.Clear()// clear all items
lstMeasuredValues.DataSource = _measuredValues;
}
And call them when ever you need to refresh the list:
_measuredValues.Add(numBuffer);
RefreshList();
// Add more values
RefreshList();
The problem is that the common List isn't the right choice for data binding. You should use BindingList if you want to keep updated the ListBox.
Try using it this way:
BindingList<double> bindList = new BindingList<double>(_measuredValues);
lstMeasuredValues.DataSource = bindList;
Keep in mind that when you add a new item in _measuredValues you have to manually refresh the binding, as far as I now, like this:
bindList.ResetBindings();
You could use a BindingList<double> as DataSource of your Listbox
List<double> _measuredValues = new List<double>();
BindingList<double> bindList = new BindingList<double>(_measuredValues);
lstMeasuredValues.DataSource = bindList;
Now everytime you need to add an element use the bindList variable and your listbox will update automatically as well as your _measuredValues list
One of the simplest way to do it is by putting:
lstMeasuredValues.DataSource = null; //the cheapest, trickiest, but the most important line
lstMeasuredValues.DataSource = _measuredValues;
Whenever your _measuredValues element is updated
All you need to do it to refresh the list after updating:
lstMeasuredValues.Refresh();

C# - Remove all items from listbox

I have a C# winform that uses a listbox with a bound list for the datasource. The list is created from a text file on the computer. I'm trying to create a "Remove all" button for this listbox and am having a little trouble.
First, here is the relevant code:
private void btnRemoveAll_Click(object sender, EventArgs e)
{
// Use a binding source to keep the listbox updated with all items
// that we add
BindingSource bindingSource = (BindingSource)listBox1.DataSource;
// There doesn't seem to be a method for purging the entire source,
// so going to try a workaround using the main list.
List<string> copy_items = items;
foreach (String item in copy_items)
{
bindingSource.Remove(item);
}
}
I've tried foreaching the bindingSource, but it gives an enumeration error and just won't work. As far as I can tell, there's not code to just purge an entire source, so I tried going through the List itself and removing them via the item name, but that doesn't work either since the foreach actually returns an object or something and not a string.
Any suggestions on how to do this?
You can do it directly by typing
listBox1.Items.Clear();
If you bound the Listbox to a BindingSource using some generic List then you can just do this:
BindingSource bindingSource = (BindingSource)listBox1.DataSource;
IList SourceList = (IList)bindingSource.List;
SourceList.Clear();
On the other handy, holding a reference to the underlaying List in your Form, Viewmodel or whatever would do the trick aswell.
EDIT:
This only works if your List is a ObservableCollection. For normal List you can try call ResetBindings() on the BindingSource to enforce a refresh.

how to make an ItemsSource not in use?

I have a DataGrid in WPF (a class that extends DataGrid), and I would like to edit the items in it. But of course I am getting the following error:
Operation is not valid while ItemsSource is in use.
Access and modify elements with ItemsControl.ItemsSource instead.
I have tried changing the itemsSource of the DataGrid, and then adding the items, but I still get the same error. Something like:
public class MyDG:DataGrid{
public void add(){
List<TimesheetRecord> records = new List<TimesheetRecord>();
foreach(TimesheetRecord rec in this.Items){
records.Add(rec);
}
//DO SOME STUFF, ADD MORE ITEMS TO records
ItemCollection col = this.Items;
this.ItemsSource = records;
col.Clear();
foreach(TimesheetRecord rec in records){
col.add(red);//exception thrown here
}
this.ItemsSource = col;
}
}
I don't understand why I am getting that error, when I have already changed the itemsSource to a different list...?
I can't (easily) add the items to the list which is originally bound as the itemsSource, because that list exists in a different class. Would it be best for me to just have a global variable in the MyDG class that is List<TimesheetRecord> myItems = new List<TimesheetRecord>(); and then in the constructor for MyDG go this.ItemsSource = myItems
Or do you have any other suggestions how I should go about doing this? I am open to anything, as this is the first time I have used databinding, so I am probably doing something wrong...
Decalre records collection as:
ObservableCollection<TimesheetRecord> records = new ObservableCollection<TimesheetRecord>();
and keep it data-bound to the DataGrid. Manipulate records collection as needed, data binding will take care of keeping UI in sync with the collection.
You have to choose whether to use Items or ItemsSource, you can't use both interchangably. Attempting to modify Items while using ItemsSource assumes an implicit conversion that isn't supported, hence the error.
In this case, it seems like the best approach might be to just set Items and add to that collection directly. To use ItemsSource, you'd need to, exactly as you wrote, pass a reference to the ItemsSource collection (List<TimesheetRecord>) in to your DataGrid class.
Once you assign "records" to the ItemsSource, you've already updated your collection. There's no need to manually add items to the dataGrid.Items collection.

How to remove selected items from ListBox when a DataSource is assigned to it in C#?

How to remove selected items from ListBox when a datasource is assigned to it in C#?
When trying to remove, got error
"Items collection cannot be modified when the DataSource property is set."
But when i try to remove item from datasource (datatable) ,
it thorws error as "datarow is not in current row collection".
Find that item in the DataSource object and remove it, then re-bind the ListBox.
EDIT:
Here's how you delete from a DataTable as your DataSource, regardless of the .NET version.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
dt.Rows.Remove(rowView.Row);
I haven't tried with anything other than WinForms DataGridViews, but I highly recommend BindingListView, which is both faster than DataTables/Views and allows you to bind generic List<T>s as your DataSource.
Alternatively, use a list that implements IBindingList or inherits from BindingList. When objects are added or removed from a Binding List, any controls bound to it are automatically notified of the change and will update themselves accordingly. If you are using BindingList and your class also implements INotifyProperty changed, Any changes to class properties will also be updated automatically in the databinding control. For example, if a column in a datagrid(view) is bound to a property, "Name", and you change "Name" in the datasource, the datagrid will automatically update. If you add a new item to the datasource, the datagrid will update automatically. Binding List also supports notification in the other direction. If a user edits the "Name" field ina datagrid, the bound object will be updated automatically. Going off topic slightly, if you go a little further and impliment "SupportsSortingCore" and the associated methods in BindingList, you can add automatic sorting to your data. Clicking on a columnm header will automatically sort the list and display the header sort direction arrow.
If the ListBox has a datasource assigned, you must remove items from the datasource and then rebind the ListBox
You need to modify the data source rather than the Items collection of the control. Depending on what kind of data source you are binding to, there are going to be different things you have to do so that your UI updates.
The best way is find a collection that fits your needs and implements IBindingList or IBindingListView. Those two interfaces implement even handlers that listen for a CollectionChanged event and update your UI accordingly.
If your collection doesn't support those interfaces, you're going to have to re-bind your data source every time somebody adds/removes an item.
when you get the message "Items collection cannot be modified when the DataSource property is set."
setting the datasource to something else, empty list or null does not help when
the code initializecomponent is not completed.
to avoid that error, one must do the change of datasource or the item list during or after form load.
I know it does not seem to make sense. Hoever, the visual studio designer will generate code in the form designer.cs or vb that will add items to the listbox if any code that changes the items is found before end of initialize components
While Chris Doggett posted a valid solution, I ran into problems while using it. By using that method it was not allowing a subsequent GetChanges(DataRowState.Deleted) to work properly.
To better solve my problem, I only had to change a single line - the last line.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
rowView.Row.Delete();
This allowed my GetChanges call to work properly.
This worked for me
DataTable temp = (DataTable)lstBlocks.DataSource;
temp.Rows.RemoveAt(position);
its vary simple , assign a new blank value to listbox
eg..
Dim ABC As New List(Of String)()
ListBox1.DataSource = ABC
ListBox implementation is bugged, you need to create a new data source instance for the component for it to recognize a change.
Eg:
ActivitiesList.DataSource = _activities;
_activities = new List<Activity>(_activities);
_activities.Remove((Activity)ActivitiesList.SelectedItem);
ActivitiesList.DataSource = _activities;

Categories