locked
Binding to inherited class properties? RRS feed

  • Question

  • Ok, this may totally not be possible, but I'll ask anyway.  I have several classes that inherit from each other, and I would like to bind properties from a sub-class to a column in the DataGridView.  Is this possible?

    For example:

    Class Employee has properties Name and ID
    Class Manager has property Salary

    I have a list of Employees bound to a DataGridView, but I want one of the columns to display Salary.  Is this possible?
    Thursday, January 22, 2009 3:35 PM

Answers

  • Hi,

    I'm no expert on the internals of all this,  but if we only rely on the "static" type of the DataSource, which is Employee, then it's normal that you won't have access to the Manager's fields, because en Employee is not a Manager. I have the feeling that the DataBinding mechanism is using the static type, and not the runtime type, though I may be 100% wrong.

    On the other hand, if you were to make your DataSource a List<Manager>, then you won't be able to add Employees to the list.

    Maybe you could trick the DataGridView by handling its CellFormatting event. In there, you receive the RowIndex, from which you can retrieve the DataGridRowView associated with the current DataGrid Row. From this, you have access to the DataBoudItem, which in your case should be either an Employee or Manager object. From there, it should be simply a matter of pulling the Salary, and assigning it to the Value property of the DataGridViewCellFormattingEventArgs.

    This would look something like this:

            private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
            {
                DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

                //Try to cast as Manager
                Manager mgr = row.DataBoundItem as Manager;
                if (mgr != null)
                {
                    e.Value = mgr.Salary;
                    e.FormattingApplied = true;
                }

            }

    I haven't tested this. I just whipped it up as a sample of what I'd do.

    I hope this helps.


    Luc Morin, T.P. http://www.stlm.ca
    • Proposed as answer by Luc Morin Thursday, January 22, 2009 7:44 PM
    • Marked as answer by Kira Qian Tuesday, January 27, 2009 7:38 AM
    Thursday, January 22, 2009 7:44 PM

All replies

  • What's the relation between Employee and Manager ?


    Luc Morin, T.P. http://www.stlm.ca
    Thursday, January 22, 2009 4:54 PM
  • Sorry, I should have made that more clear.  Here's a little pseudo-code example (the actual code is very large)

    public class Employee
    {
      public string Name = "Bob";
      public string ID = "ABC123";
    }

    Public class Manager : Employee
    {
      public int Salary = 10;
    }


    List<Employee> MySource = new List<Employee>();
    MySource.Add(new Manager());

    int columnIndex = this.Grid.Columns.Add("EmployeeName", "Employee Name");
    this.Grid.Columns[columnIndex].DataPropertyName = "Name";

    columnIndex = this.Grid.Columns.Add("EmployeeId", "Employee ID");
    this.Grid.Columns[columnIndex].DataPropertyName = "ID";

    columnIndex = this.Grid.Columns.Add("ManagerSalary", "Manager Salary");
    this.Grid.Columns[columnIndex].DataPropertyName = "Salary";
    MyDataGridView.DataSource = MySource;

    // Now, the grid shows the name and ID, but the Salary column is empty.  This is an over-simplified example, but it the root of the problem.
    Thursday, January 22, 2009 6:19 PM
  • Hi,

    I'm no expert on the internals of all this,  but if we only rely on the "static" type of the DataSource, which is Employee, then it's normal that you won't have access to the Manager's fields, because en Employee is not a Manager. I have the feeling that the DataBinding mechanism is using the static type, and not the runtime type, though I may be 100% wrong.

    On the other hand, if you were to make your DataSource a List<Manager>, then you won't be able to add Employees to the list.

    Maybe you could trick the DataGridView by handling its CellFormatting event. In there, you receive the RowIndex, from which you can retrieve the DataGridRowView associated with the current DataGrid Row. From this, you have access to the DataBoudItem, which in your case should be either an Employee or Manager object. From there, it should be simply a matter of pulling the Salary, and assigning it to the Value property of the DataGridViewCellFormattingEventArgs.

    This would look something like this:

            private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
            {
                DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

                //Try to cast as Manager
                Manager mgr = row.DataBoundItem as Manager;
                if (mgr != null)
                {
                    e.Value = mgr.Salary;
                    e.FormattingApplied = true;
                }

            }

    I haven't tested this. I just whipped it up as a sample of what I'd do.

    I hope this helps.


    Luc Morin, T.P. http://www.stlm.ca
    • Proposed as answer by Luc Morin Thursday, January 22, 2009 7:44 PM
    • Marked as answer by Kira Qian Tuesday, January 27, 2009 7:38 AM
    Thursday, January 22, 2009 7:44 PM
  • You can also bind to a formatter collection which are constructed from employee and manager. 
    MSMVP VC++
    Thursday, January 22, 2009 8:28 PM
  • Sheng Jiang,

    Could you explain what you mean by "Formatter collection" ? I have not the slightest idea what those are.

    Also, if you have some links to suggest I would appreciate.

    Thank you.


    Luc Morin, T.P. http://www.stlm.ca
    Thursday, January 22, 2009 9:56 PM
  • Hi Luc,

    List<> doesn't implement ITypedList which is necessary to get the list working with DataGridView as you need it.

    Use BindingList<> instead or try to employ BindingSource as DataSource for your DataGridView.

    hope this helps,

    franking

     

    Friday, January 23, 2009 10:19 AM
  • BindingList<EmployeeFormater> employeeFormaters=new BindingList<EmployeeFormater> ();
    employeeFormaters.Add(new EmployeeFormater (employee));
    employeeFormaters.Add(new EmployeeFormater (manager));

    class EmployeeFormater:IEmployee,IManager
    {
        EmployeeFormater(Employee employee)
        { 
                 CopyFieldsFromEmployee(employee);
        }
        EmployeeFormater(Manager manager)
        { 
                 CopyFieldsFromEmployee(manager);
                 CopyFieldsFromManager(manager);
        }    
    }

    This avoids the usage of "as Manager", which calls Isinst

    MSMVP VC++
    Friday, January 23, 2009 12:58 PM
  •  @Sheng Jiang:

    This looks like an interesting pattern, but I think it's a bit complicated for what the original poster wants to do. When you give answers like this, please take into account that those who read you have no idea what you're talking about, unless you provide enough context information.

     @franking:

    I'm not the original poster, but I'm sure he will appreciate your help. As for using List<> vs. BindingList<>, this has no effect in the scenario at hand.

    Please try the following snippet:

     public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);

                List<Employee> MySource = new List<Employee>();
                MySource.Add(new Manager());

                int columnIndex = this.MyDataGridView.Columns.Add("EmployeeName", "Employee Name");
                this.MyDataGridView.Columns[columnIndex].DataPropertyName = "Name";

                columnIndex = this.MyDataGridView.Columns.Add("EmployeeId", "Employee ID");
                this.MyDataGridView.Columns[columnIndex].DataPropertyName = "ID";

                columnIndex = this.MyDataGridView.Columns.Add("ManagerSalary", "Manager Salary");
                this.MyDataGridView.Columns[columnIndex].DataPropertyName = "Salary";
                
                MyDataGridView.DataSource = MySource;

            }

            private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
            {
                if (e.ColumnIndex != 2)
                    return;

                DataGridViewRow row = MyDataGridView.Rows[e.RowIndex];

                //Try to cast as Manager
                Manager mgr = row.DataBoundItem as Manager;
                if (mgr != null)
                {
                   e.Value = mgr.Salary;
                }

            }

        }

     

     public class Employee
    {
      public string Name { get; set; }
      public string ID { get; set; }

        public Employee()
        {
            Name  = "Bob";
            ID = "ABC123";
        }
    }

    public class Manager : Employee
    {
        public int Salary { get; set; }

        public Manager()
        {
            Salary = 10;
        }
    }

     

     You will see that it works as expected by the OP, even if it uses List<> and no BindingSource.

     Now, comment out the "e.Value = mgr.Salary;" line in the above code, and you'll be back to the OP's behavior.

     

    @Original poster:

    One thing to notice, is that you need to expose public Properties, not just public members.

     Please test this out and tell me if it works for you.

     HTH

     


    Luc Morin, T.P. http://www.stlm.ca
    Friday, January 23, 2009 1:36 PM