January 2009
Posted on the 26th at 6:37 PM CST
Creating an Excel Spreadsheet from a Generic List in VB.NET
FiledFiled under VB.NET

I'm a huge fan of generic lists. They have numerous benefits, and because of that I've been campaigning them at work to the other programmers. The response has been very good. Although given that our previous method was powered by DataTables, it is not much of a surprise that generic lists have evolved into the preference. A question came up today about adding the capability to export a GridView's data source to a Microsoft Excel spreadsheet, and it just so happens the GridView was bound to a List. This is the first time this has come up. We've had several grids export to Excel, and a common function to handle most of the workload was in place, but unfortuntely it was built for a DataTable instance. This task was accomplished fairly simply with DataTables, because it was unnecessary to worry about the parameter type - it was always a DataTable. The function loops through all the rows and builds a tab/line delimited string of data and returns it. DataTables also made it simple to get the name of the columns (which may be used as column headers). Thanks to generics and reflection, I was able to build a function that performs the same task with equivalent flexibility…

''' <summary>
''' Returns a string that can be streamed out to an Excel spreadsheet.
''' Records are delimited by a line feed (13). Fields are delimited by a tab (9).
''' </summary>
''' <param name="Objects">The list of class instances to convert</param>
''' <param name="IncludeHeadings">
''' Include a heading record at the beginning of the string? 
''' Headings will be derived from the property name.
''' </param>
''' <param name="IncludeReadOnlyProperties">
''' Include ReadOnly properties in the response for each instance?
''' </param>
''' <returns>Nothing if the given collection is empty</returns>
Public Shared Function ListToString(Of T)(ByVal Objects As List(Of T), ByVal IncludeHeadings As Boolean, ByVal IncludeReadOnlyProperties As Boolean) As String
    If Objects Is Nothing OrElse Objects.Count = 0 Then
        Return Nothing
    End If

    Dim TAB As String = Convert.ToChar(9)
    Dim LF As String = Convert.ToChar(13)
    Dim Result As New StringBuilder()
    Dim Type As Type = GetType(T)
    Dim Props As PropertyInfo() = Type.GetProperties()

    If IncludeHeadings Then
        For Each Prop As PropertyInfo In Props
            If Prop.CanWrite OrElse IncludeReadOnlyProperties Then
                Result.Append(Prop.Name)
                Result.Append(TAB)
            End If
        Next
    End If

    For Each Obj As Object In Objects
        Result.Append(LF)

        For Each Prop As PropertyInfo In Props
            If Prop.CanWrite OrElse IncludeReadOnlyProperties Then
                Result.Append(Prop.GetValue(Obj, Nothing).ToString())
                Result.Append(TAB)
            End If
        Next
    Next

    Return Result.ToString()
End Function

''' <summary>
''' Returns a string that can be streamed out to an Excel spreadsheet.
''' Records are delimited by a line feed (13). Fields are delimited by a tab (9).
''' Includes headings. Excludes read-only properties.
''' </summary>
''' <param name="Objects">The list of class instances to convert</param>
''' <returns>Nothing if the given collection is empty</returns>
Public Shared Function ListToString(Of T)(ByVal Objects As List(Of T)) As String
    Return ListToString(Objects, True, False)
End Function


And of course, I should mention that Reflection in the .NET framework is not the most efficient approach in the world. That said, if you are setting up something to process batch, you might want to consider using a strongly-typed method. However, if the intended functionality is not expected to be used frequently, then this should work just fine for you. You have to make an educated decision on whether or not the efficiency impact is crucial enough to supercede the convenience factor of having a global function. In our case, it certainly was not.

Also, I want to make it clear that I am well-aware of the approach to exporting GridViews to excel that renders the grid's HTML and relies on Excel to interpret it correctly. So there is no need to point that out to me (although somebody probably will anyways). I am not really a fan of this method. It has it's purpose and it is undeniably simplistic, but it was not right for me. If you are in a similiar situation (you need to get at the data source of the grid, or you aren't even using a visual grid), you'll appreciate the function above.

Here's a quick and dirty example of how to use the function...

Structure Employee
    Private _Id As Integer
    Private _Name As String
    Private _DateHired As DateTime

    Sub New(ByVal Id As Integer, ByVal Name As String, ByVal DateHired As DateTime)
        _Id = Id
        _Name = Name
        _DateHired = DateHired
    End Sub

    ReadOnly Property Id() As Integer
        Get
            Return _Id
        End Get
    End Property

    Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Property DateHired() As DateTime
        Get
            Return _DateHired
        End Get
        Set(ByVal value As DateTime)
            _DateHired = value
        End Set
    End Property
End Structure

Sub Main()
    Dim MyList As New List(Of Employee)
    Dim RightNow As DateTime = DateTime.Now
    Dim Export As String

    MyList.Add(New Employee(1, "Josh Stodola", RightNow.AddDays(-365)))
    MyList.Add(New Employee(2, "John Doe", RightNow.AddDays(-31)))
    MyList.Add(New Employee(3, "Jane Doe", RightNow))

    Export = Utility.ListToString(MyList)

    Console.WriteLine()
    Console.WriteLine(Export)
    Console.WriteLine()
    Console.ReadLine()
End Sub


Hope you found this useful! Oh, and accept my apologies for the inexcusable lack of blog posts lately.

Comments (7)
Permalink Comment from Pr0fess0rX on January 27th, 2009 at 1:43 AM
Can we have these code snippets in C# ?
Permalink Comment from Josh StodolaEmail on January 27th, 2009 at 7:25 AM
Permalink Comment from David on March 5th, 2009 at 8:57 PM
I beleive Chr(13) is a carriage return ... aka.. vbCr

Thanks for the nicely done example!

David
Permalink Comment from Webchester on July 27th, 2009 at 2:39 PM
Sample very interesting. Thanks for a wonderful post.
Permalink Comment from Dennis on September 16th, 2009 at 6:59 AM
But you didn't show anything about putting it in excel. Which is what I am looking for. And how I got to your page. I searched for vb.net excel tabbed string. I want to use tabbed strings to put into excel and I was hoping to find that here. Grrrrrr.
Permalink Comment from waleed on October 8th, 2009 at 6:24 PM
Permalink Comment from tali on November 26th, 2009 at 6:43 AM
I want to enable my user to save table as your save button.
can I see the code of save.ashx?

Guess What?

There are a few basic guidelines you should be aware of before leaving a comment…

  • If you choose to display your email address, it will not be detected by spam bots
  • Comments are limited to 3,000 characters; so far you have used none of them
  • HTML will be encoded; links and line breaks will be converted automatically
  • Comments containing five or more links will be subject to moderation

Have Your Say

← Answer this to prove you are human
 
 

Chill Out…