quarta-feira, 2 de junho de 2010

Criar planilha do Excel em C#

Para criar uma planilha do Excel em C# podemos proceder de duas maneiras.

A primeira seria utilizar um StreamWriter comum para criar um arquivo com extensão ".xls" e utilizar tabulação (\t) e nova linha (\n) para separar colunas e linhas.
Eu fiz isso a princípio, e até consegui abrir o arquivo no Excel, mas há uma forma mais apropriada para fazê-lo. E foi a forma que eu preferi adotar.

No Visual Studio 2008:

Primeiro será preciso adicionar uma referência ao projeto. No "Solution Explorer" clicar em "References" com o botão direito do mouse e clicar em "Add Reference".



Nas abas superiores da janela que abriu vá para a aba "COM".
Na lista de componentes da aba COM procure em "Component Name" por:

     Microsoft Excel 12.0 Object Library

OBS.: O '12.0' é referente à versão, então pode não ser esse o número.

Selecione o componente e clique OK.



Próximo passo...

No início da classe em que pretende fazer os métodos do Excel adicionar o namespace Microsoft.Office.Interop.Excel, utilizando a instrução 'using', da seguinte maneira. Exemplo:

using System;
using System.Collection.Generics;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;


A partir disso pode-se criar os objetos necessários dentro da classe.

public class Nome_da_classe
{
    private Excel.Application excelApp;/*É uma aplicação do Excel, ou seja, inicia o Excel.*/
    private Excel.Workbook arq_de_tab;/*É o arquivo de trabalho, que pode conter várias planilhas.*/
    private Excel.Worksheet planilha;/*É a planilha de trabalho.*/
    private Excel.Range celulas;/*É um objeto utilizado para selecionar uma ou mais células da planilha e trabalhar com elas.*/


.
.
.
}


O construtor da classe que fiz inicia o Excel e cria um arquivo de trabalho.

public class Nome_da_classe
{
    private Excel.Application excelApp;
    private Excel.Workbook arq_de_trab;
    private Excel.Worksheet planilha;
    private Excel.Range celulas;

    private bool addplanilha = false;/*Atributo que identifica se deve adicionar planilhas ou, caso falso, inserir a primeira.*/

    public Nome_da_classe()
    {
        try
        {
            excelApp = new Excel.Application();//Inicia o Excel.
            arq_de_trab = (Excel.Workbook)excelApp.Workbooks.Add(1);/*Cria um arquivo de trabalho (ainda sem planilhas).*/
        }
        catch (Exception) { }
    }
.
.
.
}

A partir daí é preciso colocar planilhas no arquivo...
eu criei um método para isso, mas precisei de um atributo que me identificasse se eu estou criando a primeira planilha do arquivo ou adicionando planilhas.

public class Nome_da_classe
{
.
.
.
    public void NovaPlanilha()
    {
        try
        {
             if (addplanilha)
                 planilha = (Excel.Worksheet)this.excelApp.Worksheets.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing);
             /*Na primeira execução do método ele entrará no 'else', para criar uma nova planilha, a partir da segunda vez ele irá entrar no 'if', para adicionar planilhas.*/
            else
            {
                planilha = (Excel.Worksheet)arq_de_trab.Sheets[1];
                addplanilha = true;
            }
        }
        catch (Exception) { }
    }
.
.
.
}


A partir daí pode-se utilizar a planilha para jogar valores.
Criei um método de escrita.

public class Nome_da_classe
{
.
.
.
    public void Escrever(int linha, int coluna, string texto)
    {/*Aqui temos índices para a linha e para a coluna, que começam em '1', diferente de vetores e coleções, onde os índices em geral começam em '0'.*/
        try
        {
            planilha.Cells[linha, coluna] = texto;
        }
        catch (Exception) { }
    }
.
.
.
}


Houve um caso em que eu precisava remover uma linha.
Fiz da seguinte forma.

public class Nome_da_classe
{
.
.
.
    public void RemoverLinha(string linha)
    {/*Nota: Aqui a linha não pode ser passada por um índice. É preciso passar alguma célula que esteja na linha que se deseja deletar.
     Por exemplo: passando a string "A1" por parâmetro no método irá deletar a primeira linha, e "A2" irá deletar a segunda linha.*/
        celulas = planilha.get_Range(linha, Type.Missing).EntireRow;
        celulas.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
    }
.
.
.
}


Por fim métodos para salvar e sair.

public class Nome_da_classe
{
.
.
.
    public void Salvar(string filename)
    {
        try
        {
            arq_de_trab.SaveAs(filename, Excel.XlFileFormat.xlExcel7, Type.Missing,
                Type.Missing, false, false, Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing);
        }
        catch (Exception) { }
    }


    public void Sair()
    {
        try
        {
            arq_de_trab.Close(false, "", false);
            excelApp.Quit();
        }
        catch (Exception e) { }
    }
.
.
.
}


OBS.: É importante executar o 'excelApp.Quit()', pois caso contrário o Excel continuará em execução mesmo que o programa no qual ele foi iniciado seja finalizado.

Quando o método 'Sair()' for utilizado e a instrução 'arq_de_trab.Close(false, "", false)' for executada, o excel irá exibir uma mensagem perguntando se você quer salvar o arquivo (caso você não tenha salvo ele...).
Esse é um problema que pode ser resolvido desativando os alertas. Eu fiz isso no construtor, da seguinte forma.


public class Nome_da_classe
{
.
.
.
    public Nome_da_classe()
    {
        try
        {
            excelApp = new Excel.Application();
            arq_de_trab = (Excel.Workbook)excelApp.Workbooks.Add(1);
            excelApp.DisplayAlerts = false;/*Desativa a exibição de alertas do Excel na hora de fechar o arquivo.*/
        }
        catch (Exception) { }
    }
.
.
.
}

Uma última coisa interessante:
Da forma como foi demonstrado, você não verá o arquivo enquanto ele está sendo escrito.
No caso eu não tinha interesse em vê-lo, mas se for o caso pode-se utilizar a propriedade 'excelApp.Visible': colocando 'true' nela o arquivo fica visível.

[update]
Estive revisando os posts aqui do blog e percebi que não mostrei uma forma de ler uma informação de uma célula da planilha. Não é muito complicado. Exemplo prático:

public class Nome_da_classe
{
.
.
.
    public string Ler(int linha, int coluna)
    {
        string resposta = "";
       
        try
        {
            resposta = ((Excel.Range)planilha.Cells[linha, coluna]).Value2.ToString();
        }
        catch(Exception e)
        {
            resposta = "Impossível ler. Erro: " + e.Message;
        }

        return resposta;
    }
.
.
.
}

Cuidado ao ler uma célula. Tenha certeza de que a célula que você vai ler não está vazia, ou você pode acabar gerando uma exceção. O ideal, obviamente, é criar um tratamento para essa exceção, caso ela aconteça.
Um site que foi muito útil especificamente nesse caso de leitura do arquivo:
http://www.sharpprogrammer.com/dotnet/how-to-read-excel-file-in-c-net/
[/update]

[update="18/07/2011"]
Se houver interesse, recentemente fiz um outro post sobre Excel e C#, focado em como abrir uma planilha existente e em algumas observações sobre a leitura de arquivos do Excel.
É possível acessá-lo pelo histórico do blog ou ir pelo link para o post Excel e C#: Abrir uma planilha e ler células.
[/update]

Sites que foram de grande utilidade:
http://www.codeproject.com/KB/cs/Excel_and_C_.aspx?display=Print
http://andylopes.wordpress.com/2008/07/04/gerando_planilha_excel_dotnet/
http://www.daniweb.com/forums/thread210020.html
http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-vb/33651/Problem-with-Excel-WorkBook-SaveAs

13 comentários:

Dolce Festas disse...

Cara, muito maneiro este post, foi muito útil, para mim, parabéns pela sua dedicação para o desenvolvimento do conhecimento!
the future is now

_ivan disse...

Obrigado, Santi!

O intuito é esse mesmo: desenvolvimento do conhecimento.
Estou sempre aberto a perguntas e sugestões.

Alexandre Phoenix disse...

Olá

Eu estava mexendo com essas referencias, so que para WORD. e me surgiu um problema... estou usando isso via web. e vi que nao tenho como acessar por la... (teste local funciona pq usa o office instalado na maquina).

O que eu faço, é pegar um modelo do word e com FIND/REPLACE alterar algumas informações... Sabe como poderia fazer isso rodar no servidor (tem como)?

se puder responder por email

alepe86@gmail.com

abrç

_ivan disse...

Alexandre,

te enviei um e-mail, mas acho que não posso te ajudar nesse caso. Até agora praticamente não trabalhei com aplicações web. Normalmente as questões de referências em webapp são um pouco mais complicadas mesmo e não sei se é possível fazer isso que queres no servidor.

João disse...

Parabens cara.
Foi bastante útil pra mim esse tutorial.

Abraços.

_ivan disse...

Muito obrigado, João.

Anônimo disse...

Olá, Ivan. Teria como NÃO colocar o caminho a ser salvo? Tenho que fazer um relatório em excel (lendo os dados do BD), queria que o arquivo fosse aberto para o usuário e que não salvasse o arquivo no servidor.

Sueli

_ivan disse...

Oi Sueli.
Não entendi exatamente qual é a tua dúvida. Se tu não vais salvar tu não precisas do caminho nem do método para salvar. No caso eu estou editando o arquivo sem exibí-lo, mas tu podes mantê-lo visível inclusive enquanto o edita. Seria esse o caso? Se puderes deixar mais claro o que queres torno a responder. Qualquer coisa podes me mandar uma mensagem.

Obs.: Mesmo quando tu não salvas o arquivo ele é aberto em alguma pasta temporária, obviamente.

Henry disse...

Parabéns pela iniciativa,

muito bom o artigo, e o melhor de tudo funciona

_ivan disse...

Valeu, Henry. :)

Wellington Kolenyak disse...

Ótimo post, consegui fazer o que estava mais apanhando que era adicionar mais de uma planilha no mesmo arquivo. Porém a única dúvida que tenho é alterar a ordem dessas planilhas dentro do arquivo, pois a Plan2 está vindo antes da Plan1. Alguma sugestão?

Abs. Wellington

_ivan disse...

mmm

Não cheguei a ter essa necessidade de trabalhar com mais de uma planilha, mas a princípio elas são uma coleção, o que significa que tu pode ordenar elas como tu quiser.
Segundo o que tu falaste deve haver uma lógica em que a última planilha inserida fica na primeira aba da interface visual. Pode ser só uma questão de tu inserir a planilha em um ponto diferente da coleção. Tentaste fazer isso?

Se não for isso avisa aqui no blog que faço uns testes por aqui e aviso o que descobrir.

Unknown disse...

Bom dia!!

Usei esse código no visual studio e nao criou ficheiro Excel nem nada..
Preciso mesmo de um programa q crie, edite, guarde...