Com o Visual Studio 2010 aberto, vamos iniciar criando um projeto do tipo ASP.NET MVC 2 Web Application com o nome de ExportacaoExcel. Podemos também criar um DB no SQL Server com o nome de sua preferência, dentro do DB criamos uma tabela de Usuários com a seguinte estrutura:
CREATE TABLE [dbo].[tbUsuarios]
(
[codUsuario] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[nome] [varchar](200) NULL,
[idade] [int] NULL,
[codContato] [int] NULL,
[dataCadastro] [datetime] NULL,
[login] [varchar](100) NULL,
[senha] [varchar](100) NULL,
[ativo] [bit] NULL,
[observacao] [varchar](max) NULL
)
No diretório Model do projeto vamos criar um novo item do tipo LINQ to SQL Classes com o nome de DB.dbml. Com o mesmo aberto, arrastamos a tabela criada anteriormente para dentro do mesmo e a renomeamos para Usuario.
Agora sim, mão a obra
Na Master Page vamos adicionar a referencia ao jQuery conforme o código abaixo:
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
<script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<asp:ContentPlaceHolder ID="HeadContent" runat="server" />
</head>
Agora vamos adicionar um botão na View Index.aspx:
<input type="button" id="btnExportar" value="Exportar Usuários" />
E o evento referente ao click do botão:
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript">
$(document).ready(function () {
$("#btnExportar").click(function () {
var campos = "codUsuario,nome";
window.location = "/Home/GeraExcel/?nomeEntidade=Usuario&campos=" + campos;
});
});
</script>
</asp:Content>
Note que, o parâmetro nomeEntidade é o nome da tabela definida no arquivo DB.dbml que queremos obter, e a propriedade campos é justamente os campos que desejamos exportar da tabela.
Agora no HomeController vamos criar um método do tipo ActionResult chamado GeraExcel que é justamente o método chamado no evento acima, e um outro chamado SelecionaDados, conforme é exibido abaixo:
public ActionResult GeraExcel(string nomeEntidade, string campos)
{
Type tipoEntidade = Type.GetType("ExportacaoExcel.Models." + nomeEntidade);
return this.Excel(new DBDataContext(),
this.SelecionaDados(tipoEntidade),
nomeEntidade + ".xls",
campos.Split(','));
}
public IQueryable SelecionaDados(Type tb)
{
DBDataContext _db = new DBDataContext();
return _db.GetTable(tb).AsQueryable();
}
Observe, o método GeraExcel recebe os parâmetro (nomeEntidade e campos) enviados pela nossa View Index.aspx, a propriedade tipoEntidade como o próprio nome diz, recebe o tipo da entidade enviada como parâmetro. Porém, será exibida uma mensagem de que não existe uma definição para Excel e que não existe nenhum ExtesionMethod.
Para um melhor entendimento sobre ExtensionMethods veja: http://msdn.microsoft.com/en-us/library/bb383977.aspx
Vamos então criar um, na raiz do projeto criaremos um diretório chamado Helpers e dentro do mesmo adicionaremos uma classe chamada ExtensionMethods.cs com o seguinte código:
using System.Web.Mvc;
using System.Data.Linq;
using System.Linq;
using System.Web.UI.WebControls;
namespace ExportacaoExcel.Helper
{
public static class ExcelControllerExtensions
{
public static ActionResult Excel
(
this Controller controller,
DataContext dataContext,
IQueryable rows,
string fileName
)
{
return new ExcelResult(dataContext, rows, fileName, null, null, null, null);
}
public static ActionResult Excel
(
this Controller controller,
DataContext dataContext,
IQueryable rows,
string fileName,
string[] headers
)
{
return new ExcelResult(dataContext, rows, fileName, headers, null, null, null);
}
public static ActionResult Excel
(
this Controller controller,
DataContext dataContext,
IQueryable rows,
string fileName,
string[] headers,
TableStyle tableStyle,
TableItemStyle headerStyle,
TableItemStyle itemStyle
)
{
return new ExcelResult(dataContext, rows, fileName, headers, tableStyle, headerStyle, itemStyle);
}
}
}
Note agora que é apresentado um novo erro, explicando que não existe o tipo ou namespace ExcelResult, portanto vamos cria-lo também.
Dentro do diretório Helpers adicionaremos outra classe, agora chamada ExcelResult.cs, da seguinte forma:
using System;
using System.Data.Linq;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using TesteExcel.Models;
namespace ExportacaoExcel.Helper
{
public class ExcelResult : ActionResult
{
private DBDataContext _db = new DBDataContext();
private DataContext _dataContext;
private string _fileName;
private IQueryable _rows;
private string[] _headers = null;
private System.Web.UI.WebControls.TableStyle _tableStyle;
private TableItemStyle _headerStyle;
private TableItemStyle _itemStyle;
public string FileName
{
get { return _fileName; }
}
public IQueryable Rows
{
get { return _rows; }
}
public ExcelResult(DataContext dataContext, IQueryable rows, string fileName)
: this(dataContext, rows, fileName, null, null, null, null)
{
}
public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers)
: this(dataContext, rows, fileName, headers, null, null, null)
{
}
public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, System.Web.UI.WebControls.TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle)
{
_dataContext = dataContext;
_rows = rows;
_fileName = fileName;
_headers = headers;
_tableStyle = tableStyle;
_headerStyle = headerStyle;
_itemStyle = itemStyle;
if (_tableStyle == null)
{
_tableStyle = new System.Web.UI.WebControls.TableStyle();
}
if (_headerStyle == null)
{
_headerStyle = new TableItemStyle();
_headerStyle.BackColor = Color.LightGray;
}
}
public override void ExecuteResult(ControllerContext context)
{
// Cria um HtmlTextWriter
StringWriter sw = new StringWriter();
HtmlTextWriter tw = new HtmlTextWriter(sw);
if (_tableStyle != null)
_tableStyle.AddAttributesToRender(tw);
tw.RenderBeginTag(HtmlTextWriterTag.Table);
// Gera os headers
if (_headers == null)
{
_headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray();
}
// Cria o header para as Rows
tw.RenderBeginTag(HtmlTextWriterTag.Thead);
foreach (String header in _headers)
{
if (_headerStyle != null)
_headerStyle.AddAttributesToRender(tw);
tw.RenderBeginTag(HtmlTextWriterTag.Th);
tw.Write(header);
tw.RenderEndTag();
}
tw.RenderEndTag();
// Cria Data Rows
tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
foreach (Object row in _rows)
{
tw.RenderBeginTag(HtmlTextWriterTag.Tr);
foreach (string header in _headers)
{
string strValue = "";
if (row.GetType().GetProperty(header).GetValue(row, null) == null)
strValue = "";
else
strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString().Trim();
strValue = ReplaceSpecialCharacters(strValue);
if (_itemStyle != null)
_itemStyle.AddAttributesToRender(tw);
tw.RenderBeginTag(HtmlTextWriterTag.Td);
tw.Write(HttpUtility.HtmlEncode(strValue));
tw.RenderEndTag();
}
tw.RenderEndTag();
}
tw.RenderEndTag();
tw.RenderEndTag();
WriteFile(_fileName, "application/ms-excel", sw.ToString());
}
private static string ReplaceSpecialCharacters(string value)
{
value = value.Replace("’", "'");
value = value.Replace("“", "\"");
value = value.Replace("”", "\"");
value = value.Replace("–", "-");
value = value.Replace("…", "...");
return value;
}
private static void WriteFile(string fileName, string contentType, string content)
{
HttpContext context = HttpContext.Current;
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
context.Response.Charset = "";
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = contentType;
context.Response.Write("<html xmlns:x=\"urn:schemas-microsoft-com:office:excel\">");
context.Response.Write("<head>");
context.Response.Write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1252\">");
context.Response.Write("<!--[if gte mso 9]>");
context.Response.Write("<xml>");
context.Response.Write("<x:ExcelWorkbook>");
context.Response.Write("<x:ExcelWorksheets>");
context.Response.Write("<x:ExcelWorksheet>");
context.Response.Write("<x:Name>" + fileName.Replace(".xls", "").Replace(".xlsx", "") + "</x:Name>");
context.Response.Write("<x:WorksheetOptions>");
context.Response.Write("<x:Panes>");
context.Response.Write("</x:Panes>");
context.Response.Write("</x:WorksheetOptions>");
context.Response.Write("</x:ExcelWorksheet>");
context.Response.Write("</x:ExcelWorksheets>");
context.Response.Write("</x:ExcelWorkbook>");
context.Response.Write("</xml>");
context.Response.Write("<![endif]-->");
context.Response.Write("</head>");
context.Response.Write("<body>");
context.Response.Write(content);
context.Response.Write("</body>");
context.Response.Write("</html>");
context.Response.End();
}
}
}
Pronto, agora em HomeController basta adicionarmos a referência ao diretório Helpers:
using ExportacaoExcel.Helper;
Note que, ao executar a aplicação e clicar no botão de exportação, é disponibilizado o download do arquivo Excel
Em próximos artigos mostrarei como realizar a importação de um arquivo Excel.
Até a próxima !!
Rafael Zaccanini
rafael.zaccanini@gmail.com