locked
Display objects from different classes in same DataGridView RRS feed

  • Question

  • Hallo,

    How can i display collection of objects from different classes in one datagridview. Classes have similar properties but they can have different number of properties.

    Thanks

    Sunday, November 22, 2020 7:59 PM

Answers

  • Hi Shan,
    you can use one collection with different data objects and DataGridView in virtual mode. Try following demo:

    Public Class Form1
    
      Private WithEvents dgv As New DataGridView With {.AutoGenerateColumns = False,
        .VirtualMode = True, .Dock = DockStyle.Fill, .AllowUserToAddRows = False}
    
      Private col As New List(Of Object)
    
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadData()
        Me.Controls.Add(dgv)
        dgv.RowCount = col.Count
        dgv.ColumnCount = 3
      End Sub
    
      Private Sub LoadData()
        col.Add(New Data1 With {.ID = 1, .Value1 = "Row 1 Data1"})
        col.Add(New Data2 With {.ID = 2, .Value2 = "Row 1 Data2", .Value3 = "Value 3 Row 1 Data2"})
        col.Add(New Data1 With {.ID = 3, .Value1 = "Row 2 Data1"})
        col.Add(New Data2 With {.ID = 4, .Value2 = "Row 2 Data2", .Value3 = "Value 3 Row 2 Data2"})
      End Sub
    
      Private Sub dgv_CellValueNeeded(sender As Object, e As DataGridViewCellValueEventArgs) Handles dgv.CellValueNeeded
        Dim d1 = TryCast(col(e.RowIndex), Data1)
        If d1 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d1.ID.ToString
            Case 1
              e.Value = d1.Value1
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
        Dim d2 = TryCast(col(e.RowIndex), Data2)
        If d2 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d2.ID.ToString
            Case 1
              e.Value = d2.Value2
            Case 2
              e.Value = d2.Value3
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
      End Sub
    
      Public Class Data1
        Public Property ID As Integer
        Public Property Value1 As String
      End Class
    
      Public Class Data2
        Public Property ID As Integer
        Public Property Value2 As String
        Public Property Value3 As String
      End Class
    
    End Class


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    • Proposed as answer by KHURRAM RAHIM Friday, November 27, 2020 11:03 AM
    • Marked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    • Edited by Peter Fleischer Friday, November 27, 2020 5:18 PM
    Monday, November 23, 2020 8:43 PM
  • Hi Shan1986,

    Thanks for your feedback.

    >>I was thinking whether it would be possible to make just two columns in the datagrid view

    Since this thread is related to the other one, you can refer to the following code which is based on the 'Category' attribute to make two columns in the datagridview.

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim lst1 As List(Of String) = GetAllAttributes(GetType(Category1))
            Dim lst2 As List(Of String) = GetAllAttributes(GetType(Category2))
            Dim unionLst As List(Of String) = lst1.Union(lst2).ToList()
            Dim table As DataTable = GenerateTableFromLst(unionLst, "WeightCategories")
            table.Columns.Add("Weight", GetType(System.Int32), "'0'")
            DataGridView1.DataSource = table
        End Sub
        Private Function GetAllAttributes(ByVal type As Type) As List(Of String)
            Dim lstAttr As List(Of String) = New List(Of String)()
    
            For Each prop In type.GetProperties()
                Dim category = CType(prop.GetCustomAttributes(GetType(Category), False).FirstOrDefault(), Category)
                If Not lstAttr.Contains(category.weightCategory) Then
                    lstAttr.Add(category.weightCategory)
                End If
            Next
            Return lstAttr
        End Function
    
        Private Function GenerateTableFromLst(ByVal lst As List(Of String), ByVal columnName As String) As DataTable
            Dim dt As DataTable = New DataTable()
            dt.Columns.Add(columnName)
            For Each str As String In lst
                Dim drow As DataRow = dt.NewRow()
                drow(columnName) = str
                dt.Rows.Add(drow)
            Next
            Return dt
        End Function

    Result of my test.

    Hope it could be helpful.

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).

    • Proposed as answer by KHURRAM RAHIM Friday, November 27, 2020 6:50 AM
    • Marked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    Friday, November 27, 2020 2:20 AM

All replies

  • Hi Shan,
    yes, you can.

    If your application ist Windows Forms application use virtual mode.

    If your application ist WPF application use template.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sunday, November 22, 2020 10:03 PM
  • If homonymous properties have the same type, then maybe use something like ‘list1.Cast<object>().Concat(list2.Cast<object>()).Concat(…)’ to make a single collection of objects, and assign it to dataGridView.DataSource. Probably you should add the columns explicitly instead of specifying AutoGenerateColumns.

    Show some details about your data.

    Monday, November 23, 2020 1:00 AM
  • if i understand correctly, i have to make a single collection first and then bind to datagridview?
    Monday, November 23, 2020 7:59 AM
  • Hi Shan,
    you can use one collection with different data objects and DataGridView in virtual mode. Try following demo:

    Public Class Form1
    
      Private WithEvents dgv As New DataGridView With {.AutoGenerateColumns = False,
        .VirtualMode = True, .Dock = DockStyle.Fill, .AllowUserToAddRows = False}
    
      Private col As New List(Of Object)
    
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadData()
        Me.Controls.Add(dgv)
        dgv.RowCount = col.Count
        dgv.ColumnCount = 3
      End Sub
    
      Private Sub LoadData()
        col.Add(New Data1 With {.ID = 1, .Value1 = "Row 1 Data1"})
        col.Add(New Data2 With {.ID = 2, .Value2 = "Row 1 Data2", .Value3 = "Value 3 Row 1 Data2"})
        col.Add(New Data1 With {.ID = 3, .Value1 = "Row 2 Data1"})
        col.Add(New Data2 With {.ID = 4, .Value2 = "Row 2 Data2", .Value3 = "Value 3 Row 2 Data2"})
      End Sub
    
      Private Sub dgv_CellValueNeeded(sender As Object, e As DataGridViewCellValueEventArgs) Handles dgv.CellValueNeeded
        Dim d1 = TryCast(col(e.RowIndex), Data1)
        If d1 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d1.ID.ToString
            Case 1
              e.Value = d1.Value1
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
        Dim d2 = TryCast(col(e.RowIndex), Data2)
        If d2 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d2.ID.ToString
            Case 1
              e.Value = d2.Value2
            Case 2
              e.Value = d2.Value3
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
      End Sub
    
      Public Class Data1
        Public Property ID As Integer
        Public Property Value1 As String
      End Class
    
      Public Class Data2
        Public Property ID As Integer
        Public Property Value2 As String
        Public Property Value3 As String
      End Class
    
    End Class


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    • Proposed as answer by KHURRAM RAHIM Friday, November 27, 2020 11:03 AM
    • Marked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    • Edited by Peter Fleischer Friday, November 27, 2020 5:18 PM
    Monday, November 23, 2020 8:43 PM
  • Hi Shan,
    you can use one collection with different data objects ans DataGridView in virtual mode. Try following demo:

    Public Class Form1
    
      Private WithEvents dgv As New DataGridView With {.AutoGenerateColumns = False,
        .VirtualMode = True, .Dock = DockStyle.Fill, .AllowUserToAddRows = False}
    
      Private col As New List(Of Object)
    
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadData()
        Me.Controls.Add(dgv)
        dgv.RowCount = col.Count
        dgv.ColumnCount = 3
      End Sub
    
      Private Sub LoadData()
        col.Add(New Data1 With {.ID = 1, .Value1 = "Row 1 Data1"})
        col.Add(New Data2 With {.ID = 2, .Value2 = "Row 1 Data2", .Value3 = "Value 3 Row 1 Data2"})
        col.Add(New Data1 With {.ID = 3, .Value1 = "Row 2 Data1"})
        col.Add(New Data2 With {.ID = 4, .Value2 = "Row 2 Data2", .Value3 = "Value 3 Row 2 Data2"})
      End Sub
    
      Private Sub dgv_CellValueNeeded(sender As Object, e As DataGridViewCellValueEventArgs) Handles dgv.CellValueNeeded
        Dim d1 = TryCast(col(e.RowIndex), Data1)
        If d1 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d1.ID.ToString
            Case 1
              e.Value = d1.Value1
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
        Dim d2 = TryCast(col(e.RowIndex), Data2)
        If d2 IsNot Nothing Then
          Select Case e.ColumnIndex
            Case 0
              e.Value = d2.ID.ToString
            Case 1
              e.Value = d2.Value2
            Case 2
              e.Value = d2.Value3
            Case Else
              e.Value = String.Empty
          End Select
          Exit Sub
        End If
      End Sub
    
      Public Class Data1
        Public Property ID As Integer
        Public Property Value1 As String
      End Class
    
      Public Class Data2
        Public Property ID As Integer
        Public Property Value2 As String
        Public Property Value3 As String
      End Class
    
    End Class


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Hallo Peter, Thanks for the example , I was trying your code and try to bind to datagridview with bindingsource but no success. With few properties inside i can use this but if i have multiple properties and multiple classes how do i do it? any simple method with Bindingsource?. Thanks 
    • Marked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    • Unmarked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    Tuesday, November 24, 2020 11:41 AM
  • Hi Shan,
    instead of binding my demo show how to display different data objects in one collection using virtual mode.

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Tuesday, November 24, 2020 5:02 PM
  • Hi Shan,
    instead of binding my demo show how to display different data objects in one collection using virtual mode.

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    tried.i cant bind.
    Tuesday, November 24, 2020 7:47 PM
  • Hi Shan1986,

    I have some questions to confirm with you based on your description.

    >>Classes have similar properties but they can have different number of properties.

    Are these classes related to the classes shown in your other thread

    How do you want to bind classes to DataGridView? Could you provide more details or an example here?

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).

    Wednesday, November 25, 2020 7:29 AM
  • Hallo Xingyu, yes. How to bind i have no idea but i used to bind the collection with bindinglist , bindingsource. I am open to all ideas. 
    Wednesday, November 25, 2020 9:34 AM
  • Hi Shan,
    via data binding (DataGridView.DataSource) you cannot use different data objects (different properties). In this case you can use only virtual mode like in my demo.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    Wednesday, November 25, 2020 9:53 AM
  • Hi Shan,
    via data binding (DataGridView.DataSource) you cannot use different data objects (different properties). In this case you can use only virtual mode like in may demo.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Thanks peter. I was thinking whether it would be possible to make just two columns in the datagrid view , one column lists all the properties from the classes and another column display its values may be sum of values of all instances.

    Is it possible ? that should be also fine.

     
    Thursday, November 26, 2020 7:25 AM
  • Hi Shan1986,

    Thanks for your feedback.

    >>I was thinking whether it would be possible to make just two columns in the datagrid view

    Since this thread is related to the other one, you can refer to the following code which is based on the 'Category' attribute to make two columns in the datagridview.

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim lst1 As List(Of String) = GetAllAttributes(GetType(Category1))
            Dim lst2 As List(Of String) = GetAllAttributes(GetType(Category2))
            Dim unionLst As List(Of String) = lst1.Union(lst2).ToList()
            Dim table As DataTable = GenerateTableFromLst(unionLst, "WeightCategories")
            table.Columns.Add("Weight", GetType(System.Int32), "'0'")
            DataGridView1.DataSource = table
        End Sub
        Private Function GetAllAttributes(ByVal type As Type) As List(Of String)
            Dim lstAttr As List(Of String) = New List(Of String)()
    
            For Each prop In type.GetProperties()
                Dim category = CType(prop.GetCustomAttributes(GetType(Category), False).FirstOrDefault(), Category)
                If Not lstAttr.Contains(category.weightCategory) Then
                    lstAttr.Add(category.weightCategory)
                End If
            Next
            Return lstAttr
        End Function
    
        Private Function GenerateTableFromLst(ByVal lst As List(Of String), ByVal columnName As String) As DataTable
            Dim dt As DataTable = New DataTable()
            dt.Columns.Add(columnName)
            For Each str As String In lst
                Dim drow As DataRow = dt.NewRow()
                drow(columnName) = str
                dt.Rows.Add(drow)
            Next
            Return dt
        End Function

    Result of my test.

    Hope it could be helpful.

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).

    • Proposed as answer by KHURRAM RAHIM Friday, November 27, 2020 6:50 AM
    • Marked as answer by Shan1986 Friday, November 27, 2020 2:25 PM
    Friday, November 27, 2020 2:20 AM
  • Hi Shan1986,

    Thanks for your feedback.

    >>I was thinking whether it would be possible to make just two columns in the datagrid view

    Since this thread is related to the other one, you can refer to the following code which is based on the 'Category' attribute to make two columns in the datagridview.

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim lst1 As List(Of String) = GetAllAttributes(GetType(Category1))
            Dim lst2 As List(Of String) = GetAllAttributes(GetType(Category2))
            Dim unionLst As List(Of String) = lst1.Union(lst2).ToList()
            Dim table As DataTable = GenerateTableFromLst(unionLst, "WeightCategories")
            table.Columns.Add("Weight", GetType(System.Int32), "'0'")
            DataGridView1.DataSource = table
        End Sub
        Private Function GetAllAttributes(ByVal type As Type) As List(Of String)
            Dim lstAttr As List(Of String) = New List(Of String)()
    
            For Each prop In type.GetProperties()
                Dim category = CType(prop.GetCustomAttributes(GetType(Category), False).FirstOrDefault(), Category)
                If Not lstAttr.Contains(category.weightCategory) Then
                    lstAttr.Add(category.weightCategory)
                End If
            Next
            Return lstAttr
        End Function
    
        Private Function GenerateTableFromLst(ByVal lst As List(Of String), ByVal columnName As String) As DataTable
            Dim dt As DataTable = New DataTable()
            dt.Columns.Add(columnName)
            For Each str As String In lst
                Dim drow As DataRow = dt.NewRow()
                drow(columnName) = str
                dt.Rows.Add(drow)
            Next
            Return dt
        End Function

    Result of my test.

    Hope it could be helpful.

    Best Regards,

    Xingyu Zhao


    Visual Basic and CLR forum will be migrating to a new home on Microsoft Q&A! (VB.NET and CLR) We invite you to post new questions in the new home on Microsoft Q&A ! For more information, please refer to the sticky post(VB.NET and CLR).

    Hallo Xingyu. Thanks for the answer. 
    Friday, November 27, 2020 3:04 PM