1
votes

I have an existing Data Grid View whose DataSource is a Data Table that I populate from a list of a custom object.

private myDataTable = new DataTable();

List<SomeObjectModel> dataSource = (from e in queryResults
    select
        new SomeObjectModel
        {
            Id = e.Id,
            Priority = e.Name,
            Channel = e.Channel
        }).ToList();

myDataTable = ToDataTable(dataSource); //See method below
dataGridView.DataSource = myDataTable;

From a previous question on StackOverflow, I found the ToDataTable method I'm using there that works for turning my list of objects into a DataTable using Reflection:

    public static DataTable ToDataTable<T>(List<T> items)
    {
        DataTable dataTable = new DataTable(typeof(T).Name);
        PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo prop in Props)
        {
                dataTable.Columns.Add(prop.Name);
        }

        foreach (T item in items)
        {
            var values = new object[Props.Length];
            for (int i = 0; i < Props.Length; i++)
            {
                values[i] = Props[i].GetValue(item, null);
            }
            dataTable.Rows.Add(values);
        }

        dataTable.AcceptChanges();

        return dataTable;
    }

This works for all of my DataGridViewTextBox columns. Now, I would like to add a new DataGridViewComboBoxColumn called "Person" to the data grid that has a ValueMember = "PersonId" and a DisplayMember = "PersonName" populated from a list of a Person object.

I'm getting stuck here understanding how to add this type of column to my data grid from a Data Table.

My Person object simply has the properties PersonId and PersonName, again, which I would like to use as the ValueMember and DisplayMember of the combo box.

I'm stuck, but my thinking is: 1.) Update SomeObjectModel to contain Person, 2.) Update ToDataTable method to contain an if clause to catch when the item name is "Person", but I'm not sure what to do for the rows. Also, it feels hacky to simply look for the property name, as I would like to keep ToDataTable clean.

 public static DataTable ToDataTable<T>(List<T> items)
    {
        DataTable dataTable = new DataTable(typeof(T).Name);
        PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo prop in Props)
        {
           if (prop.Name = "Person") { // Create DataGridViewComboBoxColumn } 

            else
            {
                dataTable.Columns.Add(prop.Name);
            }

        }

        foreach (T item in items)
        {
            var values = new object[Props.Length];
            for (int i = 0; i < Props.Length; i++)
            {
                values[i] = Props[i].GetValue(item, null);
            }

            dataTable.Rows.Add(values);
        }

        dataTable.AcceptChanges();

        return dataTable;
    }
1
Why do you want to bind DataGridView to a DataTable which you created using a List<SomeObjectModel>? - Reza Aghaei
Also ToDataTable method needs a fix when adding columns, you should add columns this way: dataTable.Columns.Add(prop.Name, prop.PropertyType);. If you don't specify the type of column it would be of string type. - Reza Aghaei
I'm using a DataTable because I would like to use the DataTable RowFilter to do basic filtering from a text box. - Wes Doyle
Basic filtering from a TextBox is completely possible using Linq. For example, take a look at this post or this one. - Reza Aghaei
Thanks. I feel like using Linq may be the right approach after all; I may be simply over-complicating the matter by trying to work my list into a DataTable in the first place. - Wes Doyle

1 Answers

0
votes

Here is an example of what I have done in the past for this...simple form with a dropdown loaded with the "persons" list. Button that filters based on whatever criteria you have...hope it helps.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private class Person
        {
            public int PersonId { get; set; }
            public string Name { get; set; }
        }

        private class Course
        {
            public int CourseId { get; set; }
            public string CourseName { get; set; }
            public int ProfessorId { get; set; }
        }

        private List<Course> courses = new List<Course>();
        private List<Person> professors = new List<Person>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            courses.Add(new Course { CourseId = 1, CourseName = "Math", ProfessorId = 1 });
            courses.Add(new Course { CourseId = 2, CourseName = "English", ProfessorId = 2 });
            courses.Add(new Course { CourseId = 3, CourseName = "History", ProfessorId = 1 });

            professors.Add(new Person { PersonId = 1, Name = "John Doe" });
            professors.Add(new Person { PersonId = 2, Name = "Jane Doe" });

            cboProfessors.DisplayMember = "Name";
            cboProfessors.ValueMember = "PersonId";
            cboProfessors.DataSource = professors;

            grdCourses.AutoGenerateColumns = false;
            grdCourses.Columns.Add(new DataGridViewTextBoxColumn { Name = "CourseId", HeaderText = "Course ID #", DataPropertyName="CourseId" });
            grdCourses.Columns.Add(new DataGridViewTextBoxColumn { Name = "Name", HeaderText = "Course Name", DataPropertyName="CourseName" });
            grdCourses.Columns.Add(new DataGridViewComboBoxColumn { Name = "Professor", HeaderText = "Professor", DataSource = professors,
                DisplayMember = "Name", ValueMember = "PersonId", DataPropertyName="ProfessorId" });

            grdCourses.DataSource = courses;
        }

        private void btnFilter_Click(object sender, EventArgs e)
        {
            int professorId = (int)cboProfessors.SelectedValue;
            List<Course> filteredCourses = courses.Where(x => x.ProfessorId == professorId).ToList();
            grdCourses.DataSource = filteredCourses;
        }
    }
}