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; }
}
}
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)
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;
}
I bound a list to a datagridview (using a Bindingsource).
But my DataGridView never fills.
Is there any reason why?
Here is the code:
{
public BindingList<Rat> list = new BindingList<Rat>();
BindingSource bs;
public Form1()
{
InitializeComponent();
bs = new BindingSource();
bs.DataSource = list;
list.Add(new Rat(12, "Hubert", "cousin"));
list.Add(new Rat(7, "Joe", "taxi driver"));
list.Add(new Rat(3, "Bill", "DaBoss"));
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = bs;
}
}
public class Rat
{
public int Age;
public string Name;
private string Nickname;
public Rat(int age, string name, string nick)
{
Age = age;
Name = name;
Nickname = nick;
}
}
Thanks in advance.
try change fields into properties in Rat class
public int Age { get; set }
public string Name { get; set }
private string Nickname { get; set }
I have added a GridView control on my ASP.net webpage and data bound it to a List<> the list contains a collection of a simple custom objects which is defined as:
public class PersonRecord
{
public int PersonId { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
}
I have set AutoGenerateSelectButton to true and and attached an event handler to the SelectedIndexChanged event. I can see my event handler fires and I can get the selected row index by using MyGridView.SelectedIndex.
My question is: how do I use the selected row index to get the PersonId for the selected record?
I thought MyGridView.Rows[MyGridView.SelectedIndex].Cells[0] would do it but it doesn't because MyGridView.Rows.Count is 0.
TIA
Just because I have not played with web applications in awhile, I decided to see if this was something I could dupe. Alas, to no avail. This works fine for me:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
var persons = CreatePersons();
GridView1.DataSource = persons;
GridView1.DataBind();
}
}
private List<PersonRecord> CreatePersons()
{
var person = new PersonRecord
{
PersonId = 1,
Name = "greg",
Title = "Supreme Coder",
Description = "Nada",
Notes = "foo bar"
};
var person2 = new PersonRecord
{
PersonId = 2,
Name = "Sam",
Title = "Junior Coder",
Description = "Nada",
Notes = "foo bar"
};
var list = new List<PersonRecord> {person, person2};
return list;
}
protected void Button1_Click(object sender, EventArgs e)
{
var row = GridView1.Rows[0];
var cell = row.Cells[1];
var value = cell.Text;
}
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
int index = GridView1.SelectedIndex;
var row = GridView1.Rows[index];
var nameCell = row.Cells[2];
var name = nameCell.Text;
Label1.Text = name;
}
}
Yours most likely fails as you are selecting the select column (cell[0]), but I would think you should get something out of this cell (have to play with it). It could also be a bad pattern for binding.
How are you storing the data from your GridView on the server (session, viewstate, or are you not doing so?). Since you have the selected row index, you just need to get your datasource again. If you persist it in session, then you can just get that session object and get find the object at the index the user selected.
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