I want to connect a BindingSource to a list of class objects and then objects value to a ComboBox.
Can anyone suggest how to do it?
public class Country
{
public string Name { get; set; }
public IList<City> Cities { get; set; }
public Country()
{
Cities = new List<City>();
}
}
is my class and I want to bind its name field to a BindingSource which could be then associated with a ComboBox
As you are referring to a combobox, I'm assuming you don't want to use 2-way databinding (if so, look at using a BindingList)
public class Country
{
public string Name { get; set; }
public IList<City> Cities { get; set; }
public Country(string _name)
{
Cities = new List<City>();
Name = _name;
}
}
List<Country> countries = new List<Country> { new Country("UK"),
new Country("Australia"),
new Country("France") };
var bindingSource1 = new BindingSource();
bindingSource1.DataSource = countries;
comboBox1.DataSource = bindingSource1.DataSource;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "Name";
To find the country selected in the bound combobox, you would do something like: Country country = (Country)comboBox1.SelectedItem;.
If you want the ComboBox to dynamically update you'll need to make sure that the data structure that you have set as the DataSource implements IBindingList; one such structure is BindingList<T>.
Tip: make sure that you are binding the DisplayMember to a Property on the class and not a public field. If you class uses public string Name { get; set; } it will work but if it uses public string Name; it will not be able to access the value and instead will display the object type for each line in the combo box.
For a backgrounder, there are 2 ways to use a ComboBox/ListBox
1) Add Country Objects to the Items property and retrieve a Country as Selecteditem. To use this you should override the ToString of Country.
2) Use DataBinding, set the DataSource to a IList (List<>) and use DisplayMember, ValueMember and SelectedValue
For 2) you will need a list of countries first
// not tested, schematic:
List<Country> countries = ...;
...; // fill
comboBox1.DataSource = countries;
comboBox1.DisplayMember="Name";
comboBox1.ValueMember="Cities";
And then in the SelectionChanged,
if (comboBox1.Selecteditem != null)
{
comboBox2.DataSource=comboBox1.SelectedValue;
}
public MainWindow(){
List<person> personList = new List<person>();
personList.Add(new person { name = "rob", age = 32 } );
personList.Add(new person { name = "annie", age = 24 } );
personList.Add(new person { name = "paul", age = 19 } );
comboBox1.DataSource = personList;
comboBox1.DisplayMember = "name";
comboBox1.SelectionChanged += new SelectionChangedEventHandler(comboBox1_SelectionChanged);
}
void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
person selectedPerson = comboBox1.SelectedItem as person;
messageBox.Show(selectedPerson.name, "caption goes here");
}
boom.
Try something like this:
yourControl.DataSource = countryInstance.Cities;
And if you are using WebForms you will need to add this line:
yourControl.DataBind();
If you are using a ToolStripComboBox there is no DataSource exposed (.NET 4.0):
List<string> someList = new List<string>();
someList.Add("value");
someList.Add("value");
someList.Add("value");
toolStripComboBox1.Items.AddRange(someList.ToArray());
public class Country
{
public string Name { get; set; }
public IList<City> Cities { get; set; }
public Country()
{
Cities = new List<City>();
}
}
public class City
{
public string Name { get; set; }
}
List<Country> Countries = new List<Country>
{
new Country
{
Name = "Germany",
Cities =
{
new City {Name = "Berlin"},
new City {Name = "Hamburg"}
}
},
new Country
{
Name = "England",
Cities =
{
new City {Name = "London"},
new City {Name = "Birmingham"}
}
}
};
bindingSource1.DataSource = Countries;
member_CountryComboBox.DataSource = bindingSource1.DataSource;
member_CountryComboBox.DisplayMember = "Name";
member_CountryComboBox.ValueMember = "Name";
This is the code I am using now.
As a small addition to this, I tried to incorporate something similar to this code, and was frustrated that adding/removing from the list was not reflected in the ComboBox. This is because the Add/Remove does not trigger the OnPropertyChange.
If you want to Add/Remove and have them reflected in the ComboBox, you will need to change List<> to ObservableCollection
List<Country> Countries
Should be replaced with
private ObservableCollection<Country> countries;
public ObservableCollection<Country> Countries
{
get { return countries; }
set
{
countries= value;
OnPropertyChanged("Countries");
}
}
Where OnPropertyChanged and ObservableCollection comes from
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
All of this is more eloquently expressed in a previous explanation here
Related
I added 3 columns.
The modified attribute values are as follows.
View - Details
Onwer - True
GirdLines - True
FullRowSelect - True
namespace TestWinForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void add_Click(object sender, EventArgs e)
{
//listView1.BeginUpdate();
string[] row = { "123", "456", "789" };
ListViewItem list_view = new ListViewItem(row);
listView1.Items.Add(list_view);
textBox1.Text = listView1.Items.Count.ToString();
//listView1.EndUpdate();
}
}
}
It is a code that updates the number of current rows to the textbox1 after adding a data row every time the add button is clicked.
Obviously, The number of rows keeps going up.... but the data is not output to listView1.
Which part should I check?
What you have should work in detail view with columns added. Here is an example
Design view
In this case a instance of the following class is used so later we can get information about a specific row.
public class Contact
{
public int CustomerIdentifier { get; set; }
public string CompanyName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneTypeDescription { get; set; }
public string PhoneNumber { get; set; }
public string CountryName { get; set; }
public string[] ItemArray => new[]
{
CompanyName,
FirstName,
LastName,
PhoneNumber,
CountryName
};
}
Code to do a mock add
private void AddRowButton_Click(object sender, EventArgs e)
{
var companyName = "Just added";
Contact contact = new Contact()
{
CompanyName = companyName,
FirstName = "Karen",
LastName = "Payne",
PhoneNumber = "123-333-4444",
CountryName = "USA"
};
ownerContactListView.Items.Add(
new ListViewItem(contact.ItemArray)
{
Tag = contact.CustomerIdentifier
});
CountLabel.Text = $#"Row count: {ownerContactListView.Items.Count}";
var index = ownerContactListView.Items.IndexOf(
ownerContactListView.FindItemWithText(companyName));
ownerContactListView.Items[index].Selected = true;
ownerContactListView.EnsureVisible(index);
ActiveControl = ownerContactListView;
}
After adding the row above (note I shorten the form so what you see is a partial view)
You can see the entire the initial load, see the following.
Havent used Winforms in ages, but I remember that using a binding source was more reliable
var bindingSource = new BindingSource();
bindingSource.DataSource = dataTable;
grid.DataSource = bindingSource;
//Add data to dataTable and then call
bindingSource.ResetBindings(false)
Hello I am looking at data binding in a winform application.
I have two classes: employee and job defined as below.
public class Employee
{
public string Name { set; get; }
public int Id { set; get; }
public Employee(string name, int id)
{
Name = name;
Id = id;
}
}
public class Job
{
public string Name { set; get; }
public Employee Employee { set; get; }
public Job(string name, Employee employee)
{
Name = name;
Employee = employee;
}
}
On one panel, I have one listbox (listBoxEmployees) that lists all the employees, and two textboxes giving id and name of the current employee. I have also one listbox (listBoxJobs) that lists all the jobs, one textbox giving the name of the current job as well a combobox used to select an employee for the current job. I would like that the item listed in the comboxbox get populated from the list of employees.
Any Idea how to do that? Thanks a lot for your help.
The code is as follows.
public Form1()
{
InitializeComponent();
var myListOfEmployees = new BindingList<Employee>
{
new Employee("Employee1", 1),
new Employee("Employee2", 2),
new Employee("Employee3", 3),
};
var bindingSourceEmployees = new BindingSource {DataSource = myListOfEmployees};
var myListOfJobs = new BindingList<Job>
{
new Job("Job1",null),
new Job("Job2", null),
};
var bindingSourceJobs = new BindingSource {DataSource = myListOfJobs};
//Controls Employees
listBoxEmployees.DataSource = bindingSourceEmployees;
listBoxEmployees.DisplayMember = "Name";
textBoxNameEmployee.DataBindings.Add("Text", bindingSourceEmployees,
"Name", true, DataSourceUpdateMode.OnPropertyChanged);
textBoxIdEmployee.DataBindings.Add("Text", bindingSourceEmployees,
"Id", true, DataSourceUpdateMode.OnPropertyChanged);
//Controls Jobs
listBoxJobs.DataSource = bindingSourceJobs;
listBoxJobs.DisplayMember = "Name";
comboBoxJob.DataSource = myListOfEmployees;
comboBoxJob.DataBindings.Add(new Binding("SelectedItem", bindingSourceJobs, "Employee"));
comboBoxJob.DisplayMember = "Name";
textBoxNameJob.DataBindings.Add("Text", bindingSourceJobs,
"Name", true, DataSourceUpdateMode.OnPropertyChanged);
}
Code
I have this UI
with this code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
namespace WinFormsComboBoxDatabinding
{
public partial class Form1 : Form
{
public List<Person> PersonList { get; set; }
public Person SelectedPerson { get; set; }
public Form1()
{
InitializeComponent();
InitializePersonList();
InitializeDataBinding();
}
private void InitializePersonList()
{
PersonList = new List<Person>
{
new Person { FirstName = "Bob", LastName = "Builder" },
new Person { FirstName = "Mary", LastName = "Poppins" }
};
}
private void InitializeDataBinding()
{
SelectedPerson = PersonList[0];
var bindingSource = new BindingSource();
bindingSource.DataSource = PersonList;
comboBox.DisplayMember = "FirstName";
//comboBox.ValueMember = "LastName";
comboBox.DataSource = bindingSource;
textBoxFirstName.DataBindings.Add("Text", SelectedPerson, "FirstName");
textBoxLastName.DataBindings.Add("Text", SelectedPerson, "LastName");
}
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedPerson = comboBox.SelectedItem as Person;
Debug.WriteLine($"SelectedPerson: {SelectedPerson}");
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return $"{FirstName} {LastName}";
}
}
}
Questions
I have two questions about databinding:
When I select Mary in the ComboBox, the two TextBox controls don't get updated. Why is that? What did I do wrong?
When I change the text "Mary" in the ComboBox, the SelectedPerson object doesn't get updated with the new FirstName, say "Mary changed", from the ComboBox. How would I achieve that behaviour of changing the ComboBox FirstName to update the FirstName of the SelectedPerson? Or is that not possible with a ComboBox?
Other experiment
I've seen that one can set the two TextBox controls' Text property when comboBox_SelectedIndexChanged gets called, but that's not really databinding, is it. That would be manually doing all the updating logic.
Let me know if I need to add more details to the question.
You don't need the SelectedPerson variable. It looks like you just have the wrong DataSource wired up. Try it this way:
textBoxFirstName.DataBindings.Add("Text", bindingSource, "FirstName");
textBoxLastName.DataBindings.Add("Text", bindingSource, "LastName");
Try this
private void InitializeDataBinding()
{
SelectedPerson = PersonList[0];
var bindingSource = new BindingSource();
bindingSource.DataSource = PersonList;
comboBox.DisplayMember = "FirstName";
comboBox.DataSource = bindingSource;
textBoxFirstName.DataBindings.Add("Text", bindingSource, "FirstName");
textBoxLastName.DataBindings.Add("Text", bindingSource, "LastName");
}
private void comboBox_TextChanged(object sender, EventArgs e)
{
var selectedPerson = PersonList.FirstOrDefault(x => x.FirstName == comboBox.Text);
if (selectedPerson == null) return;
comboBox.SelectedItem = selectedPerson;
}
You just need to set the ComboBox.DataSource to the List<Person> object, represented by the PersonList property here.
Add a DataBinding to the controls that needs to be updated when the ComboBox selects a new element from its DataSource:
textBoxFirstName.DataBindings.Add("Text", PersonList, "FirstName");
The controls are updated automatically.
In the ComboBox SelectedIndexChanged handler, you can set the SelectedPerson property value to the current SelectedItem, casting it to the Person class.
public List<Person> PersonList { get; set; }
public Person SelectedPerson { get; set; }
private void InitializePersonList()
{
this.PersonList = new List<Person>
{
new Person { FirstName = "Bob", LastName = "Builder" },
new Person { FirstName = "Mary", LastName = "Poppins" }
};
}
private void InitializeDataBinding()
{
comboBox.DisplayMember = "FirstName";
comboBox.DataSource = this.PersonList;
textBoxFirstName.DataBindings.Add("Text", PersonList, "FirstName");
textBoxLastName.DataBindings.Add("Text", PersonList, "LastName");
}
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
this.SelectedPerson = (Person)(sender as ComboBox).SelectedItem;
}
This is the format how I'm getting my list of users from Stored Procedure.
IEnumerable<User> users = new List<User>();
Now I'm trying to bind this list to my combo-box like:
private void BindDropdownList(IEnumerable<User> users)
{
selectuser_dropdown.Items.Insert(0, "+ New User"); // Want this to be at last position.
selectuser_dropdown.DataSource = users;
selectuser_dropdown.DisplayMember = "FullName";
selectuser_dropdown.ValueMember = "Id";
}
My class is:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public bool IsAdmin { get; set; }
public string FullName {
get
{
return LastName + ", " + FirstName;
}
}
}
But the New user doesn't seem to appear in my dropdownlist. What is the issue here and can anybody please suggest a way to do it? Thanks in advance.
your code before added Item on dropdown then set datasource
When you make a datasource assignment, you crush the old data.
add users list a new Item before set datasource for
selectuser_dropdown
or
set selectuser_dropdown dataSource then add
dropdown new Item selectuser_dropdown.Items.Insert(0, "+ New User");
private void BindDropdownList(IEnumerable<User> users)
{
// users.ToList().Add(new User(){ .. , .. , .. });
selectuser_dropdown.DataSource = users;
selectuser_dropdown.DisplayMember = "FullName";
selectuser_dropdown.ValueMember = "Id";
// selectuser_dropdown.Items.Insert(0, "+ New User");
}
do not judge me strictly, i think it would be elegantly to use the decorator pattern here. you could wrap default user list into UserListDecorator, thereby add a new element at the beginning or at the end of the list
public class UserListDecorator : IEnumerable<User>
{
private IEnumerable<User> users;
public UserListDecorator(IEnumerable<User> users)
{
this.users = users;
}
public IEnumerator<User> GetEnumerator()
{
var innerList = users.ToList();
innerList.Add(new User() {LastName = "New User", Id = 0});
return innerList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
you can get two lists, the basic one and another with a new user option.
IEnumerable<User> users = new List<User>()
{
new User() { LastName = "One" },
new User() { LastName = "Two" }
};
var withNewUser = new UserListDecorator(users);
selectuser_dropdown.DataSource = withNewUser;
after trying i found that you cannot add items to a ComboBox after binding it to a data source. To add items from a ComboBox with a bound data source, you have to do it through data source itself.You can use a Datatable and then add your new row to it,
private void BindDropdownList(IEnumerable<User> users)
{
DataTable dt = users.ToDataTable();
selectuser_dropdown.DataSource = dt ;
selectuser_dropdown.DisplayMember = "FullName";
selectuser_dropdown.ValueMember = "Id";
DataRow dr = dt.NewRow();
dr["FullName"] = "Select";
dr["Id"] = 0;
dt.Rows.InsertAt(dr, 0);
}
Let's say there is a List<Person> where Person{Name, age, etc}.
What I want is a ListBox with all Person's names as items.
Only detail. I want it to update automatically.
I wonder if it is possible to create a dynamic datasource that gets the names of the List<Person> and then the ListBox is binded to that datasource.
Any suggestions other than using a datasourse are welcome.
I found LinqDataSource that is used in .net 4. Anything similar to .Net3.5?
I believe you are looking for something "more" than this but I am not sure from your question what that is. So I am posting this code for you to further explain your goal.
namespace SO_Forms_Demo
{
public partial class Form1 : Form
{
List<person> people;
public Form1()
{
InitializeComponent();
List<string> dataList = new List<string>(){"Moe","Larry","Curly"};
listBox1.DataSource = dataList;
people = new List<person>(){new person(){name="Moe",age=44,shoeSize=9},
new person(){name="Larry",age=45,shoeSize=10},
new person(){name="Curly",age=46,shoeSize=11}
};
bindList2();
}
private void button1_Click(object sender, EventArgs e)
{
person Shemp = new person() { name = "Shemp", age = 49, shoeSize = 12 };
people.Add(Shemp);
bindList2();
}
private void bindList2()
{
listBox2.DataSource = null;
listBox2.DisplayMember = "name";
listBox2.DataSource = people;
}
}
public class person
{
public string name { get; set; }
public int age { get; set; }
public int shoeSize { get; set; }
}
}