Here's a slightly (well...much) more complex way of generating an Excel document using Visualforce, but it's much easier to get a good looking result that's an actual xls file, including native Excel conditional formatting, frozen header rows, formulas and named ranges! All credit goes to Caspar Harmer. I'm only adding some explanation to his code, which you can find on GitHub here:
Rapsacnz ExcelExport
First, the Apex controller:
public ExcelExportController() {
xmlheader ='<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?>';
endfile = '</Workbook>';
opportunities = [SELECT Id, Name, StageName, Amount FROM Opportunity LIMIT 10];
oppSize = opportunities.size();
}
The xmlheader and endfile values will be added to the Visualforce page using apex:outputText tags, since Visualforce doesn't play nice with these tags on its own!
And now the beginnings of the Visualforce page:
<apex:page id="pg" standardStylesheets="false" controller="ExcelExportController" contenttype="application/vnd.ms-excel#TestExport_{!TODAY()}.xls">
<apex:outputText value="{!xmlheader}" escape="false"/>
<!-- your excel output goes here -->
<apex:outputText value="{!endfile}" escape="false"/>
</apex:page>
Now for the interesting part: We're going to create our spreadsheet layout directly in Excel, instead of trying to do all the formatting in Visualforce.
- Open Excel and create a new spreadsheet
- Add columns for Opportunity Name, Stage, Amount, and a few more for fun.
- Give it a highlighted frozen column header
- Put in some data rows
- Add a SUM formula at the bottom of the Amount column
- Define named ranges for each of the columns to make them easier to work with in Visualforce.
If you want to get really nutty with it, add a % of total sales column, and use a formula to calculate the percentage based on the total of the Amount column divided by the Amount value for each row. Add a SUM formula to the bottom of that column to be sure it totals 100%
Save the file as an "Excel 2004 XML Spreadsheet (.xml)". Then open the file in a text editor, and paste the contents into the Visualforce page below the "your excel output goes here" comment.
Edit the named ranges to make it simpler to refer to elsewhere in your code. (I'm not sure how to explain that better. It makes sense as you start to read through it...I hope.)
<Names>
<NamedRange ss:Name="Amount_Column" ss:RefersTo="=Opportunities!R2C3:R{!oppSize + 1}C3"/>
<NamedRange ss:Name="Name_Column" ss:RefersTo="=Opportunities!R2C1:R{!oppSize + 1}C1"/>
<NamedRange ss:Name="Percentage_of_Total_Column" ss:RefersTo="=Opportunities!R2C4:R{!oppSize + 1}C4"/>
<NamedRange ss:Name="Stage_Column" ss:RefersTo="=Opportunities!R2C2:R{!oppSize + 1}C2"/>
<NamedRange ss:Name="Total_Amount_Cell" ss:RefersTo="=Opportunities!R{!oppSize + 2}C3"/>
</Names>
Caspar has used Visualforce to modify the height of each named range, based on the total number of opportunities (oppSize).
Use one of the rows from the spreadsheet's code as a template to build your table inside an apex:repeat block.
<apex:repeat value="{!opportunities}" var="opp">
<Row>
<Cell>
<Data ss:Type="String">{!opp.Name}</Data>
<NamedCell ss:Name="Name_Column"/>
</Cell>
<Cell >
<Data ss:Type="String">{!opp.StageName}</Data>
<NamedCell ss:Name="Stage_Column"/>
</Cell>
<Cell >
<Data ss:Type="Number">{!opp.Amount}</Data>
<NamedCell ss:Name="Amount_Column"/>
</Cell>
<Cell ss:StyleID="s75" ss:Formula="=IFERROR(RC[-1]/Total_Amount_Cell,0)">
<Data ss:Type="Number"></Data>
<NamedCell ss:Name="Percentage_of_Total_Column"/>
</Cell>
</Row>
</apex:repeat>
Add the "totals" row that includes your SUM formulas to the bottom of the table (below the apex:repeat block).
<Row ss:Height="19">
<Cell ss:StyleID="s67"/>
<Cell ss:StyleID="s67"><Data ss:Type="String">Totals</Data></Cell>
<Cell ss:StyleID="s67" ss:Formula="=SUM(Amount_Column)"><Data ss:Type="Number"></Data></Cell>
<Cell ss:StyleID="s70" ss:Formula="=SUM(Percentage_of_Total_Column)">
<Data ss:Type="Number"></Data>
</Cell>
</Row>
...and there you have it: One incredibly complicated but perfectly formatted Excel compatible spreadsheet, generated dynamically from data in your org. (Phew!)
My recommendation: Go with the simple version: Is there a way to dynamically fill excel sheets from apex?
or invest in a 3rd party solution like Drawloop or Conga Composer.