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.
Thanks for the nicely done example!
David
http://ledomoon.blogspot.com/2009/03/export-generic-list-to-excel-file-in.html
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
