asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Can I Get That As a Spreadsheet?
By Bob McLeod
Rating: 4.3 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction


    Often my users want to have data emailed to them as an Excel workbook consisting of multiple sheets. In the past, I would have created an MS Office solution using VBA. First I'd query the database and create a recordset. Then I'd create an MS Excel object and map my data to the cells in my Workbook. Finally, I'd create an Outlook object to handle sending my email with an attachment. Along the way I would be reading and writing to file. In the end I would get to where I was going, but this solution had its drawbacks.

    Some of the problems included:

    1. The overhead of making sure I didn't leave instances of Excel or Outlook running.
    2. The security risks exposed by emailing Excel workbooks back and forth.
    3. The resulting workbook could end up being too large to send via email.
    4. The solution was not one that lent itself to efficient code reuse.
    5. I would have to deploy the solution on the users machine or
    6. Run the solution myself whenever they needed the report.

    I wanted to spend more time creating cool applications and less time running reports, so I considered some other options.

    There Had To Be a Better Way

    Other options I considered included PERL, VBScript, and one of the many commercial products available for creating reports. I'm familiar with PERL, and there are modules for dealing with workbooks, but I still would face a steep learning curve. For me, a VBScript solution would have been about the same as my VBA version. A commercial product would have cost money and I still would have had a learning curve. One day while tinkering with Excel, a solution presented itself that was simple, easy to deploy, and built on my existing knowledge of VB and VB .NET.

    download source code

    Save As

    It dawned on me that in Excel I could do a File, Save As Html document. This document could then be presented in a browser, multiple sheets and all. I realized all I needed to do was replicate the Excel XML/HTML format, insert my data, and viola!

    Overview of Solution

    I developed a class in VB .NET to handle the writing of the necessary files. The result is an object that accepts a dataset with multiple tables and turns them into an Excel workbook with multiple sheets. The workbook can be presented in a browser. Excel will handle converting it from XML/HTML to a native Excel document when the user chooses the Save As option. By creating a class I now would have a tool rather than just a one-time solution. Combined with ASP.NET and System.Mail I could have not only a tool but a really cool tool. This solution assumes the user has Excel installed on his machine for saving locally. It is possible, however, to view the files in the browser without Excel.

    How Microsoft XML/HTML Works

    We're going to take a look at some of the inner workings of Microsoft XML/HTML. For the sake of brevity I'll only be talking about what we need to create this solution. If you'd like a more detailed insight consider the following links: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnoffxml/html/ofxml2k.asp.

    Excel Knows HTML

    According to the official Microsoft documentation, "Microsoft Office 2000 supports Hypertext Markup Language (HTML) as a native file format." What this means for us is that if we create a simple HTML table, Excel will be able to open it as a worksheet. By placing the following markup at the top of our document we'll be able to render a worksheet in a browser. Consider the following pseudo-code of a function that would accept a dataset and return a string that we could then render to the browser or save to file:

    Function createSheet(ByVal ds As DataSet) AsString

    Dim str AsString

    Dim rw As DataRow

    '#############

    'Set up our Table

    '#############

    str = "<html><head>"

     

    '#######################################

    'This line tells the browser what to do with the file

    '#######################################

    str = str & "<META HTTP-EQUIV='Content-Type'

    CONTENT='application/vnd.ms-excel'>"

    str = str & "/head>" & vbCrLf

    str = str & "<table>" & vbCrLf

     

    '######################################################

    'Walk through the datatable and render an HTML table row for each

    'data row.

    '######################################################

    ForEach rw In ds.Tables(0).Rows

    str = str & "<tr>" & vbCrLf

    str = str & "<td>" & rw.Item(0).ToString() & "</td>" & vbCrLf

    str = str & "<td>" & rw.Item(1).ToString() & "</td>" & vbCrLf

    str = str & "</tr>"

    Next

     

    '##########################

    'Close up our body and html tags

    '##########################

    str = str & "</table> </body></html>"

    createSheet = str

    EndFunction

    We could then save this to file and redirect the user to the page we created. If the user has Excel installed they will be presented with a worksheet inside the browser.

    That's fine for a single worksheet but what if we want to create a workbook with multiple sheets or add formatting?

    Excel Knows XML

    Extensible Markup Language, as the name implies, allows us to create our own markup tags for presenting data to users. XML is similar to HTML in that XML is made up of nested tags with text data in between. MS Office has its own set of XML tags to present Office data. For a more thorough treatment of XML, check out www.xml.org. Office defines its own XML tags for Excel in the following way:

    Excel <x:tagname>

    Word, PowerPoint and Access also have tags:

    Word <w:tagname>
    PowerPoint <p:tagname>
    Access <a:tagname>

    This article only scratches the surface of the potential of XML and Office. We'll get into enough to create our solution. There are tags for defining almost every aspect of an Office document.

    The Files To Create

    With HTML/XML we are going to create an Excel Workbook Frameset. This frameset will consist of a main file and a directory containing the supporting files. You can think of this main file as a container for our workbook. The main file contains references to the multiple worksheets that we'll need as well as supporting files. These supporting files are placed in a directory with "_files" tacked on the end. If our workbook name is mybook then we'll need to create a directory called "mybook_files". We can give our workbook a .htm or an .xls extension. Inside of our "_files" directory we'll create:

    1. Multiple sheet.htm files- one for each worksheet in our workbook.
    2. A filelist.xml file
    3. A stylesheet.css file - Optional
    4. A tabstrip.htm file - Optional

    Format and Function of Each File

    or our purposes we'll not be using all the files in this list nor will we be using all of the options available in Office HTML/XML. What we are going to construct is a barebones workbook/worksheets collection. Let's consider the role and format of each of these files.

    <mainworkbook>.xls

    This is the foundation of our workbook. This document could be divided into three parts.

    1. Namespace declaration
    2. Document type meta tags
    3. XML segment
    We're giving this document an .xls extension so we can open it directly with Excel. We could also give it a .htm extension, and it would render in the browser.

    Namespace declaration:

    We are going to use "x:" as an XML tag to designate our Excel elements. This is the first line of our document:

    <html xmlns:xmlns:x="urn:schemas-microsoft-com:office:excel"

    Document type meta tags:

    Let's tell the browser what kind of document this is. We'll use these for meta tags.

    <meta name="Excel Workbook Frameset">

    <meta http-equiv=Content-Type content="text/html; charset=windows-1252">

    <meta name=ProgId content=Excel.Sheet>

    <meta name=Generator content="Microsoft Excel 9">

    XML Segment:

    Here we tell Excel where to find our worksheets.

    Inside of the <xml> tag is a <x:ExcelWorkbook> tag. Nested in the <x:ExcelWorkbook> tag is an <x:ExcelWorksheets> tag and inside of the <x:ExcelWorksheets> tag we have a <x:ExcelWorksheet> for each of our worksheets.

    We start our xml segment by telling the browser that this is xml.

    <xml>

     

    </xml>

    Then we declare our workbook.

    <xml>

    <x:ExcelWorkbook>

    </x:ExcelWorkbook>

    </xml>

    Now we'll tell Excel that there is more than one worksheet.

    <x:ExcelWorkbook>

    <x:ExcelWorksheets>

    </x:ExcelWorksheets>

    </x:ExcelWorkbook>

    </xml>

    Inside of this we'll put our references to each worksheet.

    <xml>

    <x:ExcelWorkbook>

    <x:ExcelWorksheets>

    <x:ExcelWorksheet>

     

    </x:ExcelWorksheet>

    </x:ExcelWorksheets>

    </x:ExcelWorkbook>

    </xml>

    Inside of the<x:ExcelWorksheets> tag we'll create a segment to indicate the name and location of each worksheet in our workbook.

    <x:ExcelWorksheet>

    <x:Name>Sheet1</x:Name>

    <x:WorksheetSource HRef="./testBook_files/sheet001.htm"/>

    </x:ExcelWorksheet>

    We'll close up our HTML/XML document with:

    </xml>

    </head>

    </html>

     

    The rest of our files go inside of a special folder one level below our main .xls/.htm file. Excel and Explorer will look for a directory that has the name of the main document with _files tacked on the end. This directory must be placed just below the main document.

    <workbookname>_files\
    filelist.xml
    sheet001.htm
    sheet002.htm

    filelist.xml

    As the name implies this is nothing more than a list of the files we're using to create our workbook.

    The first line is a namespace declaration, followed by Hrefs to each document that makes up the workbook.

    <xml xmlns:o="urn:schemas-microsoft-com:office:office">

    <o:MainFile HRef="../testBook.xls"/>

    <o:File HRef="sheet001.htm"/>

    <o:File HRef="sheet002.htm"/>

    <o:File HRef="sheet003.htm"/>

    <o:File HRef="filelist.xml"/>

    </xml>

    sheet001.htm

    We'll make one of these for each worksheet. They are similar in format to our main .xls/.htm file

    1. Namespace declaration
    2. Document type meta tags
    3. HTML segment
    Namespace declaration

    <htmlxmlns:o="urn:schemas-microsoft-com:office:office"

    xmlns:x="urn:schemas-microsoft-com:office:excel"

    xmlns="http://www.w3.org/TR/REC-html40">

    Document type meta tags

    Inside the <head> tags we declare the content type and a reference to our main.xls file.

    <head>

    <metahttp-equiv=Content-Typecontent="text/html; charset=windows-1252">

    <metaname=ProgIdcontent=Excel.Sheet>

    <metaname=Generatorcontent="Microsoft Excel 9">

    <linkid=Main-Filerel=Main-Filehref="../testBook.xls">

    </head>

    Html segment

    This part is just ordinary html with a few exceptions.

    <bodylink=bluevlink=purple>

    <tablex:strborder=0cellpadding=0cellspacing=0width=192style='border-collapse:

    collapse;table-layout:fixed;width:144pt'>

    <colwidth=64span=3style='width:48pt'>

    <trheight=17style='height:12.75pt'>

    <tdheight=17width=64style='height:12.75pt;width:48pt'>1a</td>

    <tdwidth=64style='width:48pt'>1b</td>

    <tdwidth=64style='width:48pt'>1c</td>

    </tr>

    <trheight=17style='height:12.75pt'>

    <tdheight=17style='height:12.75pt'>2a</td>

    <td>2b</td>

    <td>2c</td>

    </tr>

    <trheight=17style='height:12.75pt'>

    Here we've used an xml tag called x:num to format our field as a number.

    <tdheight=17align=rightstyle='height:12.75pt'x:num>1</td>

    <tdalign=rightx:num>2</td>

    We use the tag x:fla to include a formula in the cell.

    <tdalign=rightx:numx:fmla="=A3+B3">3</td>

    </tr>

    </table>

    </body>

    </html>

    There are other x: tags we could use for formatting, for example:

    x:str to format as string
    x:bool to indicate true/false

    These files are pretty much all we need to create a workbook. All we need to do is create a class to write these files, insert data where we need it, and manage delivery of the final report. We could also create a stylesheet.css file and a tabstrip.htm file. These would be used to render the workbook in a browser without Excel on the local machine. To see how these work, save an Excel workbook as html and examine the resulting files. I'm not using them in this solution for the sake of simplicity.

    Creating Our Class

    Our class is going to be fairly simple. This class could easily be extended to include formatting and insertion of formulas in our cells.

    For our purposes we'll need to refer to the following namespaces:

    Imports System.IO
    Imports System.Web
    Imports System.Text

    We'll be using System.IO for reading and writing to file, System.web for building html elements, and System.Text for working with strings.

    Next we'll declare a namespace called Reports as well as a class declaration we'll name WorkBook.

    Namespace Reports

    PublicClass WorkBook

     

    EndClass

    EndNamespace

    Now we'll need some properties. This one will be used to reference our data source for the workbook object.

    'Property for holding dataset

    Private workbook_ds As DataSet

    Property datasource() As DataSet

    Get

    datasource = workbook_ds

    EndGet

    Set(ByVal Value As DataSet)

    workbook_ds = Value

    EndSet

    EndProperty

    Let's tell our object where to put our data.

    'Set the workpath

    Private strPath AsString

    Property workpath() AsString

    Get

    workpath = strPath

    EndGet

    Set(ByVal Value AsString)

    strPath = Value

    EndSet

    EndProperty

    SubNew()

    'Entry point

    EndSub

    Now we'll develop our main method. This is the one we'll call when we need to create a workbook. This method will accept a dataset and convert each table in the dataset to a corresponding worksheet.

     

    PublicSub renderWorkbook(ByVal ds As DataSet)

    'Send Workbook to browser

    createworksheets(workbook_ds)

    EndSub

     

    PrivateSub createworksheets(ByVal ds As DataSet)

    Dim tbl As DataTable

    Dim colSheets AsNew Collection()

     

    'Create directory that holds worksheets and support files

    'First we'll create a directory based on the path variable set

    'through the workpath property

    Directory.CreateDirectory(strPath)

     

    'Now we'll create the directory to hold supporting files.

    'We'll append “_files” to our dataset name.

    Directory.CreateDirectory(strPath & "\" & ds.DataSetName & "_files")

     

    'Walk through each table in dataset and create worksheet

    ForEach tbl In ds.Tables

    'We'll need this collection later to create our multiple worksheets

    colSheets.Add(tbl.TableName.ToString)

     

    'Pass data table to be exported to a sub that will create the worksheet file.

    createWorksheetFile(tbl, ds.DataSetName.ToString)

    Next

     

    'Call our sub that creates the support files

    createSupportFiles(colSheets, ds.DataSetName.ToString)

     

    'This sub will create the main workbook.xls

    createXls(colSheets, ds.DataSetName.ToString)

     

    EndSub

    In this method we make use of three sub routines:

    1. createWorksheetFile
    2. createsupportFiles
    3. createxls

    PrivateSub createWorksheetFile(ByVal tbl As DataTable, ByVal strName AsString)

    'Files to create

    'bookname_files/sheet001.htm

    'bookname_files/sheet002.htm

    'bookname_files/sheet(etc.).htm

    Dim myFileName, myFileNamePath AsString

    'We'll use stringBuilder to create our documents.

    'StringBuilder is faster than using append to create string-based data

    Dim strHTML AsNew StringBuilder()

    Dim rw As DataRow

     

    'Take the name of the table to create our variables for the file name and path.

    myFileName = tbl.TableName.ToString

    myFileNamePath = strPath & "\" & strName & "_files\" & myFileName & ".htm"

     

    'Create header – This will go at the top of each of our worksheet files, so we build

    'it up here for later use in our For Next statement

    strHTML.Append(createSheetHeader(strName & ".xls"))

    strHTML.Append("<body>" & vbCrLf)

    strHTML.Append("<table x:str border=1 cellpadding=0 cellspacing=0 width=192

    style='border-collapse:" & vbCrLf)

    strHTML.Append(" collapse;table-layout:fixed;width:144pt'" & vbCrLf)

    Dim intCount AsInteger = tbl.Columns.Count

    Dim I AsInteger

     

    'Create sheet headings

    'This is where could add additional formating for the headings.

    strHTML.Append("<tr>" & vbCrLf)

    For I = 0 To intCount - 1

    strHTML.Append("<td><b>" & tbl.Columns(I).ColumnName.ToString &

    "</b></td>" & vbCrLf)

    Next

    strHTML.Append("</tr>" & vbCrLf)

     

    'Turn each data row into <tr>

    Dim strItem AsString

    'Walk through each row of our table and convert it to <tr>

    ForEach rw In tbl.Rows

    strHTML.Append("<tr>" & vbCrLf)

    'Walk through each item in our datarow and

    'convert each item into <td>

    For I = 0 To intCount - 1

    strItem = rw.Table.Columns(I).ColumnName.ToString

    strHTML.Append("<td>" & fixNull((rw.Item(I))) & "</td>" & vbCrLf)

    Next

    strHTML.Append("</tr>" & vbCrLf)

    Next

    strHTML.Append("</table>" & vbCrLf)

    strHTML.Append("</html>" & vbCrLf)

    writeToFile(strHTML.ToString, myFileNamePath)

    EndSub

    We'll use the same kinds of methods to create the remaining files. See the source code for all the gory details.

    Putting the Class to Use

    Here is an example of how we could put our new class to use:

    Imports Reports.Workbook

    PublicFunction createSheet()

    Dim ds AsNew DataSet()

    'This would be a function used to get a dataset.

    ds = getData()

     

    Dim strPath AsString = Server.MapPath(ds.DataSetName.ToString)

     

    'Create a workbook object from my class

    Dim myBook AsNew Reports.WorkBook()

     

    'Set path and data source properites

    With myBook

    .workpath = strPath

    .datasource = ds

    EndWith

     

    'Call method to create workbook

    myBook.renderWorkbook(ds)

    EndFunction

    Enhancements

    This example doesn't contain error checking, which is something you would definitely want prior to deploying. Other enhancements would include adding properties for setting the formats of Worksheets and Cells. I've also not included the tabstrip.htm and stylesheet.css documents for rendering in a browser if the user doesn't have Excel.

    Conclusion

    By combining MS Office HTML/XML with stringBuilder, we've been able to create a pretty cool solution. We're letting Excel handle the conversion to native Excel file type when the user selects "file/save_as" in the browser. We could have delivered the final report as a link in an email or on a Web page.

    References

    199841 - HOWTO Display ASP Results Using Excel in IE with MIME Types
    301044 - HOW TO Create an Office Document in an ASP Application
    260239 - HOWTO Format Cell Data When You Are Creating an Excel File With an Active Server Pages Page
    Excel Technical Articles
    271572 - HOWTO Format an Excel Workbook While Streaming MIME Content
    Microsoft Office HTML and XML Reference

    About the Author

    Bob McLeod is a Systems Developer for an energy aggregator in Portland Maine. He teaches PERL/CGI as an adjunct faculty for Central Maine Technical College and has developed several web-based data delivery systems. Bob can be reached at dvegan@hotmail.com

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    Proposion N2N
    Proposion N2N connects Microsoft .NET applications to Lotus Notes and Lotus Domino databases. This ADO.NET managed data provider allows you to perform blindingly fast queries and updates of Notes data from ASP.NET pages, .NET web services, Windows, or Mobile applications. An innovative SQL-like query language leverages the unique features of Notes and makes collaborative software accessible to relational database programmers.
    [Top]
    Other Articles
    Sep 15, 2005 - Building an Image Keyword System
    Unlike text-based file formats image files aren't made up of words, which makes searching for an image file by keyword difficult. Instead of being able to simply open the file to see what it contains, we're stuck looking at the text around it and other metadata to determine the image's meaning. In this article, Ziran Sun shows you how to build a simple database-based image keyword system that allows you to associate keywords with images and use these keywords to make finding images easier.
    [Read This Article]  [Top]
    Apr 7, 2005 - A Step-by-Step Guide To Using MySQL with ASP.NET - Part 2
    In the second part of of his article on using MySQL with ASP.NET, Ziran Sun covers how to add a new MySQL user to the database server, assign the user the appropriate permissions, connect to the database, and build a simple ASP.NET page to perform a query.
    [Read This Article]  [Top]
    Feb 10, 2005 - A Step-by-Step Guide To Using MySQL with ASP.NET - Part 1
    Back in the days of classic ASP, if you were building a database-driven web site, your choice was either to invest a lot of money to get a copy of Microsoft SQL Server (or some other enterprise-ready database) or invest a lot of time finding a way to deal with the performance and scalability limitations of Microsoft Access. Luckily these days there's another viable alternative: MySQL.
    [Read This Article]  [Top]
    Jan 27, 2005 - Moving a Database from SQL Server 7.0 to SQL Server 2000
    Moving or copying a SQL Server database from one machine to another requires a lot of preparation in order to ensure a smooth transfer. In this article, Dina Fleet Berry examines the different methods and highlights the different issues associated with each of them.
    [Read This Article]  [Top]
    Jan 6, 2005 - Debugging a SQL Stored Procedure from inside SQL Server 2000 Query Analyzer
    There are many times when using SQL Server 2000 Query Analyzer to debug SQL statements is a better choice than debugging in Visual Studio .NET. In this article, Dina Fleet Berry explains why and walks you through the debugging process step-by step.
    [Read This Article]  [Top]
    Nov 24, 2004 - Persisting .NET Objects to SQL Server Using SQLXML and Serialization
    As a follow up to his article on retrieving objects from SQL Server using SQLXML and serialization, Gianluca Nuzzo discusses saving objects back to SQL Server using a schema definition file and updategrams.
    [Read This Article]  [Top]
    Sep 14, 2004 - Transaction Processing in ADO.NET 2.0
    One area that stands out when comparing ADO.NET 1.x to ADO.NET 2.0 is transaction processing. Bill Ryan shows just how easy transaction processing has become with the TransactionScope object in ADO.NET 2.0.
    [Read This Article]  [Top]
    Sep 8, 2004 - Custom Object Data Binding with .NET
    Developers often use brute force coding to marshal data between the GUI and application objects. In this article, Luther Stanton explains how to use .NET's out-of-the box data-binding functionality to make this job much easier.
    [Read This Article]  [Top]
    Sep 2, 2004 - Queue MSMQ Messages from SQL Server
    Learn how to create a console application to queue a message in Microsoft Message Queuing (MSMQ) and then use an extended stored procedure to call the console application from a SQL Server trigger.
    [Read This Article]  [Top]
    Aug 30, 2004 - Tuning Up ADO.NET Connection Pooling in ASP.NET Applications
    Connection pooling increases the performance of Web applications by reusing active database connections instead of creating a new connection with every request. This article shows how to monitor the connection pool, diagnose a potential problem, and apply the appropriate fix.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry

    internet.commediabistro.comJusttechjobs.comGraphics.com

    Search:

    WebMediaBrands Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Shopping | E-mail Offers | Freelance Jobs