I have an object like so
public class Person{
public int ID { get; set; }
public string Name{ get; set; }
public string Surname{ get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames{ get; set; }
}
I am setting data into the DataGridView like
dataGridView1.DataSource = null;
dataGridView1.DataSource = persons; // persons is List<Person>
However I only see 3 columns in the DataGridView that is of ID, Name and Surname, the properties with List<int> and List<string> seem to be ignored.
Is there a way to get theses properties showing up in the DataGridView? Probably like comma seperated values.
most probably the persons is null
you just change your code like this code
first Solution :
your model :
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames { get; set; }
}
PersonViewModel :
public class PersonViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int AltID { get; set; }
public string AltName { get; set; }
}
formLoad :
private void Form1_Load(object sender, EventArgs e)
{
List<Person> people = new List<Person>()
{new Person(){ ID=1,Name="a",Surname="a",AltIDs=new List<int>(){1,2,3,4 },AltNames=new List<string>(){"a","b","c" } },
new Person(){ ID=2,Name="b",Surname="b",AltIDs=new List<int>(){10,20,30,40 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="c",Surname="c",AltIDs=new List<int>(){100,200,300,400 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=4,Name="d",Surname="d",AltIDs=new List<int>(){1000,2000,3000,4000 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="e",Surname="e",AltIDs=new List<int>(){10000,20000,30000,40000 },AltNames=new List<string>(){"a","b","c" }}
};
List<PersonViewModel> pwm = new List<PersonViewModel>();
foreach (var person in people)
{
foreach(var id in person.AltIDs)
{
foreach (var name in person.AltNames)
pwm.Add(new PersonViewModel() { ID = person.ID, Name = person.Name, Surname = person.Surname, AltID = id, AltName = name });
}
}
dgv.DataSource = pwm;
}
Result :
Second Solution :
your model :
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames { get; set; }
}
PersonViewModel :
public class PersonViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string AltID { get; set; }
public string AltName { get; set; }
}
FormLoad :
private void Form1_Load(object sender, EventArgs e)
{
List<Person> people = new List<Person>()
{new Person(){ ID=1,Name="a",Surname="a",AltIDs=new List<int>(){1,2,3,4 },AltNames=new List<string>(){"a","b","c" } },
new Person(){ ID=2,Name="b",Surname="b",AltIDs=new List<int>(){10,20,30,40 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="c",Surname="c",AltIDs=new List<int>(){100,200,300,400 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=4,Name="d",Surname="d",AltIDs=new List<int>(){1000,2000,3000,4000 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="e",Surname="e",AltIDs=new List<int>(){10000,20000,30000,40000 },AltNames=new List<string>(){"a","b","c" }}
};
List<PersonViewModel> pwm = new List<PersonViewModel>();
StringBuilder sbIds;
StringBuilder sbNames;
foreach (var person in people)
{
sbIds = new StringBuilder();
sbNames = new StringBuilder();
person.AltIDs.ForEach(c=> sbIds.Append(c.ToString()).Append(","));
person.AltNames.ForEach(c=> sbNames.Append(c).Append(","));
pwm.Add(new PersonViewModel() { ID = person.ID, Name = person.Name, Surname = person.Surname, AltID = sbIds.ToString().TrimEnd(','), AltName = sbNames.ToString().TrimEnd(',') });
}
dgv.DataSource = pwm;
}
Result :
When DataGridView is bound to DataSource, it auto generates columns for it for the properties with primitive datatypes such as int, string, double etc.
For the datatypes which are collections, DataGridViewComboBoxColumn is used. This type of column is not autogenerated. You need to add such columns manually in the GridView columns collection.
For your use case following is the solution.
Add DataGridView to the form and add columns to it manually from the Form's designer view by click on Add Column. You will have to add 3 DataGridViewTextBoxColumn and 2 DataGridViewComboBoxColumn.
After adding columns, the columns would look as following.
Now while assigning DataSource to the DataGridView you need to write following code. Here IdColumn, NameColumn, and SurnameColumn are the names give to columns when they were created in above steps.
dataGridView1.AutoGenerateColumns = false;
IdColumn.DataPropertyName = "ID";
NameColumn.DataPropertyName = "Name";
SurnameColumn.DataPropertyName = "Surname";
dataGridView1.DataSource = persons;
With the above code you will see Id, Name and Surname columns populated for persons in the collection but the dropdown lists in last two columns are empty.
To populate the dropdown list columns you need to add event handler for CellClick event of the DataGridView. And write following code there.
Here AltIdsColumn and AltNamesColumn are the names given to column when they created manually in earlier steps.
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
var altIdIndex = dataGridView1.Columns["AltIdsColumn"].Index;
var altNameIndex = dataGridView1.Columns["AltNamesColumn"].Index;
if (altIdIndex == e.ColumnIndex || altNameIndex == e.ColumnIndex)
{
var altIdsCell = (DataGridViewComboBoxCell)dataGridView1.Rows[e.RowIndex].Cells[altIdIndex];
var altNamesCell = (DataGridViewComboBoxCell)dataGridView1.Rows[e.RowIndex].Cells[altNameIndex];
if (altIdsCell.DataSource == null || altNamesCell.DataSource == null)
{
var person = dataGridView1.Rows[e.RowIndex].DataBoundItem as Person;
if (person != null)
{
altIdsCell.DataSource = person.AltIDs;
altNamesCell.DataSource = person.AltNames;
}
}
}
}
With this code, the dropdown list of AldIDs and AltNames columns will be populated when you click on those dropdown lists on individual rows.
I hope this will help you resolve your issue.
From what I can tell, this appears as a fairly basic “Master/Slave” type UI. In the grid, there is a “Person” object with the ID, Name and Surname. Obviously from your question, the AltIDs and AltNames Lists are NOT displayed in the grid.
This is due to the fact that the grid has a problem trying to add a “list of multiple” values into a “single” cell. As suggested, it is possible for you to “combine” the values into a “single” string and use that. However, this is extra work on your part and may create more work if the cell is edited.
One issue in this example is that, the AltIDs list for each Person could and will have a different number of elements. One possible solution is to simply ADD theses as new rows where the Person info (ID, Name and Surname) are duplicated. This will work but IMHO not very user friendly. The combo box option will also work, however again it may be confusing to users since a combo box “usually” indicates that the users selects a “single” value from many.
It is also possible to “flatten” each person object and have a column for each AltID in the list. This will work but the possibility of large gaps in the grid are likely. Again, not very user friendly. This is all doubled by the fact that there is another list AltNames that we have to take into account.
Given this, it appears clear that YOU are going to have to do extra work UNLESS you use an advanced third-party grid OR add more grids to the picture… in this case three (3) total. One for the person, another for the IDs and a third for the Names. Proper arrangement of the grids may be a little work, however, with three grids, it will make all the issues described above… go away. In addition the coding will be much easier.
It would be such that the user “selects” a Person from the first grid, then the second grid list all the AltID values and the third grid lists all the AltName values. If the “same” data source is used for each grid then… when the user selects a different Person in the person grid, the AltID grid and AltName grid will “automatically” update/refresh with the proper values. This will also make CRUD operations on any grid/value much easier.
The only problem I seen in the current Person class is that the two Lists are of “primitive” types. Used this way, the lists won’t display properly, the grid wants a CLASS. Therefore, you need to make a wrapper class for the AltID and AltName lists. Then change the list values in the Person class. Something like…
public class AltID_C {
public int AltID { get; set; }
}
public class AltName_C {
public string AltName { get; set; }
}
public class Person {
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<AltID_C> AltIDs { get; set; }
public List<AltName_C> AltNames { get; set; }
}
With this, all that is needed is to set each grid to the “same” data source, then set the AltID grids DataMember to the “AltIDs” property and set the AltNames grids DataMember to the “AltNames` property. Something like…
List<Person> AllPersons;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
AllPersons = GetRandomData(25);
dgvPerson.DataSource = AllPersons;
dgvAltID.DataSource = AllPersons;
dgvAltID.DataMember = "AltIDs";
dgvAltNames.DataSource = AllPersons;
dgvAltNames.DataMember = "AltNames";
}
Additional code below to complete the example.
private List<Person> GetRandomData(int numberOfPersons) {
List<Person> listOPeople = new List<Person>();
Person curPerson;
Random rand = new Random();
for (int i = 0; i < numberOfPersons - 1; i++) {
curPerson = new Person() {
ID = rand.Next(1, 1000),
Name = "Name_" + i + 1,
Surname = "Sname_" + i + 1,
AltIDs = GetRandomNumberOfInts(rand),
AltNames = GetRandomNumberOfStrings(rand)
};
listOPeople.Add(curPerson);
}
return listOPeople;
}
private List<AltID_C> GetRandomNumberOfInts(Random rand) {
List<AltID_C> listOInts = new List<AltID_C>();
int numberOfInts = rand.Next(0, 10);
for (int i = 0; i < numberOfInts - 1; i++) {
listOInts.Add(new AltID_C { AltID = rand.Next(1, 10000) });
}
return listOInts;
}
private List<AltName_C> GetRandomNumberOfStrings(Random rand) {
List<AltName_C> listOStrings = new List<AltName_C>();
int numberOfStrings = rand.Next(0, 10);
for (int i = 0; i < numberOfStrings - 1; i++) {
listOStrings.Add(new AltName_C { AltName = "RandString: " + rand.Next(1, 1000) });
}
return listOStrings;
}
Hope that helps.
I'm trying to store data in a dictionary where the key is an ID and the values are of a class type. The class properties are not all added at the same time so I haven't used a constructor - unless there is a way to add new values using a constructor at a different times? The code below compiles, but I get a run time error saying the key has already been added.Thanks for the help.
public class Students
{
public string FirstName { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
public double Score { get; set; }
}
public void cmdTEST_Click(object sender, EventArgs e)
{
Dictionary<int, Students> Data = new Dictionary<int, Students>();
Data.Add(5, new Students { FirstName = "Bob" });
Data.Add(5, new Students { Age = 34 }); // run time error - "key already added"
Data.Add(5, new Students { Score = 62 });
// extract data
double Score5 = Data[5].Score;
double Age5 = Data[5].Age;
}
You are adding same key multiple times which is not allowed. You can add all properties at once like below
Dictionary<int, Students> Data = new Dictionary<int, Students>();
Data.Add(5, new Students { FirstName = "Bob", Age = 34, Score = 62 });
And if you want to add values later you can use key to add values
Data.Add(5, new Students { FirstName = "Bob"});
Data[5].Age = 34;
Data[5].Score = 62;
Ok.. I've seen some similar discussions so I hope this is unique enough.
I would like to initialize and modify data in a List
The list is a Property of the class User, which is inside a Dictionary
http://ideone.com/0YtpnC
internal class User
{
public string ID { get; set; }
public string Name { get; set; }
public List<ContactNumber> AddressBook { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
Dictionary<int, User> dic = new Dictionary<int, User>();
dic.Add(1, new User { ID = "id1", Name = "name1" });
dic.Add(2, new User { ID = "id2", Name = "name2" });
dic.Add(3, new User { ID = "id3", Name = "name3", AddressBook = new List<ContactNumber>(){new ContactNumber(3432),new ContactNumber(3213)} });
User user = dic.Where(z => z.Value.ID == "id3").FirstOrDefault().Value;
Console.WriteLine(user.Name);
DisplayList(user);
//Console.WriteLine(user.AddressBook.ToString());
//update the list
addOrUpdate(dic,3,user);
//var used = new User { ID = "id1", Name = "Harry" };
//dic[1] = used;
user = dic.Where(z => z.Value.ID == "id3").FirstOrDefault().Value;
Console.WriteLine(user.Name);
DisplayList(user);
}
private static void DisplayList(User user)
{
foreach (ContactNumber number in user.AddressBook)
{
Console.WriteLine(number.PhoneNumber);
}
}
//determin list length
public static void addOrUpdate(Dictionary<int, User> dic, int key, User user)
{
//sets a new user data and just replaces it in the dictionary
//var used = new User { ID = "id1", Name = "Harry" };
var used = new User{ ID = "id3", Name = "Henry" ,AddressBook = new List<ContactNumber>(){new ContactNumber(3111),new ContactNumber(4444)}};
if (dic.TryGetValue(key, out user))
{
// yay, value exists!
dic[key] = used;
}
else
{
// darn, lets add the value
dic.Add(key, used);
}
}
}
the problem is I want to get the user info and just add a contact number to the list. Currently it rewrites the entire list since I make a new version of list in the update function.
http://ideone.com/iI2Rxb
Simply:
dic[key].AddressBook.Add(new ContactNumber(666));
This seems to be a solution.
user.AddressBook.Add(new ContactNumber() { PhoneNumber = 121212});
from MSDN Docs
Wont below help, just add to the list (since it is referenced, it will update whatever you have been getting from the dictionary)
user.AddressBook.Add(...new contact number);
I have a Linq question: (DotNet Framework 4.0)
I have the following classes:
public class Employee
{
public Guid? EmployeeUUID { get; set; }
public string SSN { get; set; }
}
public class JobTitle
{
public Guid? JobTitleSurrogateKey { get; set; }
public string JobTitleName { get; set; }
}
public class EmployeeToJobTitleMatchLink
{
public EmployeeToJobTitleMatchLink()
{
this.TheJobTitle = new JobTitle() { JobTitleSurrogateKey = Guid.NewGuid(), JobTitleName = "SomeJobTitle:" + Guid.NewGuid().ToString("N") };
}
public Guid LinkSurrogateKey { get; set; }
/* Related Objects */
public Employee TheEmployee { get; set; }
public JobTitle TheJobTitle { get; set; }
}
public class Organization
{
public Organization()
{
this.Links = new List<EmployeeToJobTitleMatchLink>();
}
public int OrganizationSurrogateKey { get; set; }
public ICollection<EmployeeToJobTitleMatchLink> Links { get; set; }
}
In my code below, I can compare 2 child-collections and get the results I need (in "matches1".
Here I am using the "SSN" string property to compare and find the overlaps. And the Console.Write for matches1 works as I expect.
What I don't know how to do is compare the first child collection (org10) to all the children in (allOtherOrgsExceptOrg10 (all the Organizations and all the Links of these Organizations )
The commented out code shows kinda what I'm trying to do, one of my many feeble attempts today.
But basically, match2 would be populated with all the SSN overlaps...but comparing org10 with allOtherOrgsExceptOrg10, all their "Links", and their Employee.SSN's.
org10 overlaps with org20 with "AAA", so match2 would contain "AAA". and org10 overlaps with org30 with "BBB" so match2 would contain "BBB".
Organization org10 = new Organization();
org10.OrganizationSurrogateKey = 10;
Employee e11 = new Employee() { SSN = "AAA", EmployeeUUID = new Guid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA") };
EmployeeToJobTitleMatchLink link11 = new EmployeeToJobTitleMatchLink();
link11.TheEmployee = e11;
org10.Links.Add(link11);
Employee e12 = new Employee() { SSN = "BBB", EmployeeUUID = new Guid("BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB") };
EmployeeToJobTitleMatchLink link12 = new EmployeeToJobTitleMatchLink();
link12.TheEmployee = e12;
org10.Links.Add(link12);
Organization org20 = new Organization();
org20.OrganizationSurrogateKey = 20;
Employee e21 = new Employee() { SSN = "AAA", EmployeeUUID = new Guid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA") };
EmployeeToJobTitleMatchLink link21 = new EmployeeToJobTitleMatchLink();
link21.TheEmployee = e21;
org20.Links.Add(link21);
Employee e22 = new Employee() { SSN = "CCC", EmployeeUUID = new Guid("CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC") };
EmployeeToJobTitleMatchLink link22 = new EmployeeToJobTitleMatchLink();
link22.TheEmployee = e22;
org20.Links.Add(link22);
Organization org30 = new Organization();
org30.OrganizationSurrogateKey = 30;
Employee e31 = new Employee() { SSN = "BBB", EmployeeUUID = new Guid("BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB") };
EmployeeToJobTitleMatchLink link31 = new EmployeeToJobTitleMatchLink();
link31.TheEmployee = e31;
org30.Links.Add(link31);
Employee e32 = new Employee();
e32.SSN = "ZZZ";
EmployeeToJobTitleMatchLink link32 = new EmployeeToJobTitleMatchLink();
link32.TheEmployee = e32;
org30.Links.Add(link32);
IList<Organization> allOtherOrgsExceptOrg10 = new List<Organization>();
/* Note, I did not add org10 here */
allOtherOrgsExceptOrg10.Add(org20);
allOtherOrgsExceptOrg10.Add(org30);
IEnumerable<EmployeeToJobTitleMatchLink> matches1 =
org10.Links.Where(org10Link => org20.Links.Any(org20Link => org20Link.TheEmployee.SSN.Equals(org10Link.TheEmployee.SSN, StringComparison.OrdinalIgnoreCase)));
IEnumerable<EmployeeToJobTitleMatchLink> matches2 = null;
//org10.Links.Where(org10Link => ( allOtherOrgs.Where ( anyOtherOrg => anyOtherOrg.Links.Any(dbSideChild => dbSideChild.TheEmployee.SSN == org10Link.TheEmployee.SSN)) );
if (null != matches1)
{
foreach (EmployeeToJobTitleMatchLink link in matches1)
{
Console.WriteLine(string.Format("matches1, SSN = {0}", link.TheEmployee.SSN));
}
}
if (null != matches2)
{
foreach (EmployeeToJobTitleMatchLink link in matches2)
{
Console.WriteLine(string.Format("matches2, SSN = {0}", link.TheEmployee.SSN));
}
}
matches2 =
allOtherOrgsExceptOrg10.SelectMany(x => x.Links)
.Where(x => org10.Links.Select(o => o.TheEmployee.SSN).Contains(x.TheEmployee.SSN));
You can use the SelectMany on the allOther collection to select all Links over all org's. Then check if any SSN is inside the org10 List.
See: http://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany(v=vs.100).aspx
You can use SelectMany to flatten out the collection and then use it just like you have for matches1
IEnumerable<EmployeeToJobTitleMatchLink> matches2 =
org10.Links.Where(
org10Link =>
allOtherOrgsExceptOrg10.SelectMany(allOtherOrgs => allOtherOrgs.Links).Any(
anyOtherLink =>
anyOtherLink.TheEmployee.SSN.Equals(org10Link.TheEmployee.SSN, StringComparison.OrdinalIgnoreCase)));
The SelectMany will make it seem like one IEnumerable instead of and IEnumerable of an IEnumerable.
Well I have a class like this and I want to store in a list which is defined as a static variable. I was following the reference here: Storing data into list with class
public class Faculty
{
public string Name { get; set; }
public string Dept { get; set; }
public string[] subInterest = new string[4];
}
However when I try to initialize the list as follows I have problem with the array part: Invalid initializer member declarator
SaveDirectory.list_of_faculty.Add(
new Faculty
{
Name = txtTeachherName.Text,
Dept = cmbDepts.Items.CurrentItem.ToString(),
subInterest[0] = "HELLO"
});
What am I doing wrong?
You can't reach in to your object and initialize members of an array that it holds using initializer syntax. You could work around this as follows:
var fac = new Faculty {
Name = txtTeachherName.Text,
Dept = cmbDepts.Items.CurrentItem.ToString(),
subInterest = new []{"HELLO", null, null, null}}
or after initialization:
fac.subInterest[0] = "HELLO";
The problem is due to:
subInterest[0] = "HELLO"
You can not partially initialize an array in Object Initializer, either it is full or nothing, You can do:
subInterest = new string[]{"HELLO"}
But, this will leave the array subInterest with size 1, not as size 4, as you have defined in your class. To get the array of same size you can do:
subInterest = new string[]{"HELLO",null,null,null}
Another option is to use List<string> instead of an Array. You can initialize it with value and later you can add items to it.
Modify your class like:
public class Faculty
{
public string Name { get; set; }
public string Dept { get; set; }
public List<string> subInterest;
}
and then:
new Faculty
{
Name = txtTeachherName.Text,
Dept = cmbDepts.Items.CurrentItem.ToString(),
subInterest = new List<string>(){"HELLO"}
};
Using a constructor can also solve the problem like
public class Faculty
{
public string Name { get; set; }
public string Dept { get; set; }
public string[] subInterest = new string[4];
public Faculty(string name, string dept, string[] si)
{
this.Name = name;
this.Dept = dept;
this.subInterest = si;
}
}
Then instantiate the faculty class
string[] subinterest = new string[]{"reading","swmming",null,null};
var fac = new Faculty("dgdfgdfgfg","dfgdgfgdfg", subinterest);