Kategorie szkoleń | Egzaminy | Kontakt
  • 2
  • 3
  • 553

W jaki sposób najlepiej wygenerować dokument w formacie Excel w aplikacji .NET? Czy są jakieś lekkie biblioteki, czy muszę posiadać pełną instalację Excel? Próbowałem się połączyć do bibliotek udostępnianych przez aplikację Excel jako komponenty COM. I to działa, ale wymusza użycie takiej wersji Office jak na komputerze deweloperskim - ani nowszej, ani starszej.

Uczestnik szkolenia
  • Zapytał
  • @ Uczestnik szkolenia | 28.06.2014
Zaloguj się aby zadać pytanie
Pokrewne

Odpowiedzi (2)

  • 5

Lekkiej biblioteki do obsługi Excela w wersji standalone nie ma w podstawowych bibliotekach .NET. Jeśli jest to raport można się zastanowić nad wykorzystaniem kontrolki ReportViewer, która umożliwia eksport danych z wyświetlanego raportu do formatu Excel. Czasem tez wystarczy wygenerować plik csv, jeśli nie zależy nam na formatowaniu, lub html - jeśli chcemy mieć formatowanie. Formaty te zwykle poprawnie są importowane do Excela.

Jeśli chcemy mieć większą kontrolę, musimy użyć bibliotek COM dostarczanych z Excelem. Niestety jeśli dodamy referencje do tych bibliotek w naszym assembly, to skazujemy naszą aplikację na działanie tylko z tą konkretną wersją Excela. Z nowszymi też nie będzie działać. Dlatego do bibliotek tych najlepiej podłączyć się wykorzystując mechanizm late binding dostępny w Visual Basic.NET  i C#. Polega on na tym, że weryfikacja istnienia funkcji w danym obiekcie następuje w momencie wywołania kodu, a nie kompilacji.W Visual Basic osiągamy to, inicjując instancję klasy Excel.Aplication przez funkcję CreateObject i podstawiając ją pod zmienną typu Object. W C# korzystamy z klasy Activator i typu "dynamic".

Można też skorzystać z mechanizmu refleksji, co jest rozwiązaniem bardziej wydajnym, dającym większą kontrolę nad kodem, ale i bardziej pracochłonną. Jeśli jednak opakujemy sobie Excela własnymi bibliotekami, możemy to znacznie uprościć. Poniżej fragment kodu, który definiuje taką prostą bibliotekę owijającą Excela i generuje za jej pomocą plik z danymi popranymi z bazy danych:

    public static class ExcellUtils
    {
        public static object ShowExcell()
        {
            Type t = Type.GetTypeFromProgID("Excel.Application");
            object a = Activator.CreateInstance(t);
            InvokeSetProperty("Visible", a, true);
            return a;
        }
        public static object InvokeMethod(string name, object target, params object[] p )
        {
            Type t = target.GetType();
            return t.InvokeMember(name,
                BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance
                , null, target, p);
        }
        public static object InvokeGetProperty(string name, object target)
        {
            Type t = target.GetType();
            return t.InvokeMember(name,
                BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance
                , null,target, null);
        }
        public static object InvokeGetProperty(string name, object target,params object[] p)
        {
            Type t = target.GetType();
            return t.InvokeMember(name,
                BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance
                , null, target, p);
        }
        public static void InvokeSetProperty(string name, object target, object val)
        {
            Type t = target.GetType();
            t.InvokeMember(name,
                BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance
                , null, target, new object[]{val});
        }

        public static void ExportToExcell(IDataReader dr)
        {
            int fieldcount = dr.FieldCount;
            var excell = ShowExcell();
            var workbooks = InvokeGetProperty("Workbooks", excell);
            object workbook =InvokeMethod("Add",workbooks, Type.Missing);
            object s =  InvokeGetProperty("ActiveSheet",excell);
            Type sheetType = s.GetType();

            object cells = InvokeGetProperty("Cells", s);
            for (int i = 0; i < fieldcount; i++)
            {
                               
                object item = InvokeGetProperty("Item",cells, 1, i + 1 );
                InvokeSetProperty("Value",item, dr.GetName(i));
            }

            int row = 2;
            while (dr.Read())
            {
                for (int i = 0; i < fieldcount; i++)
                {
                    object item = InvokeGetProperty("Item", cells, row, i + 1);
                    InvokeSetProperty("Value", item, dr[i]);
                }
                row++;
            }
            InvokeMethod("SaveAs",workbook,
                    @"dane.xls", Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, 0,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing
                );
            InvokeMethod("Quit", excell, null);
        }

  • Odpowiedział
  • @ | 28.06.2014
  • TRENER ALTKOM AKADEMII
  • 5

Rozwiazanie zapronowane przez Mirka z uzyciem typow dynamicznych, jest bardzo skomplikowane.

Po pierwsze przy generowaniu zlozonych zestawien, ktore wystepuja w praktyce, zamiast skupic sie na logice musimy walczyc z wywolaniami. Po drugie takie rozwiazanie wymaga instalacji Excela, co przy rozwiazaniach serwerowych jest czesto nie do przyjecia ze wzgledow licencyjnych.

ReportViewer jest faktycznie pewnym rozwiazaniem, ale wymaga od programisty nauki dodatkowego narzedzia. Poza tym rosnie zlozonosc projektu.

Dlatego proponuje zamiast powyzszych rozwiazan skorzystac ze swietnej biblioteki EPPlus. Biblioteka ta, umozliwia generowanie arkusza w intuicyjny sposob, bez potrzeby instalacji Excela. Jak to jest mozliwe? Otoz biblioteka EEPlus generuje arkusze w formacie XLSX, zgodne z otwartym standardem OOXML http://pl.wikipedia.org/wiki/Office_Open_XML Jest to po prostu dokument XML, ktory mozna teoretycznie stworzyc w dowolnym narzedzi, nawet w notatniku jesli ktos mialby czas :)

Oczywiscie EEPlus zrobi to za nas. Utworzenie arkusza jest bardzo proste.

 Tworzymy nowy arkusz:

 FileInfo newFile = new FileInfo(outputDir.FullName + @"\sample6.xlsx");

            ExcelPackage pck = new ExcelPackage(newFile);
            //Add the Content sheet
            var ws = pck.Workbook.Worksheets.Add("Content");
            ws.View.ShowGridLines = false;

 Nastepnie wypelniamy arkusz naglowkami i danymi:

ws.Cells["B1"].Value = "Name";
            ws.Cells["C1"].Value = "Size";
            ws.Cells["D1"].Value = "Created";
            ws.Cells["E1"].Value = "Last modified";
            ws.Cells["B1:E1"].Style.Font.Bold = true;

Mozliwosci jest duzo wiecej. Oprocz wstawiania wartosci do komorek i ich formatowania mozna umieszczac wykresy, itp.  Co wiecej mozemy skorzystac z jezyka Linq przy generowaniu danych.

Wiecej przykladow mozna znalezc na stronie http://epplus.codeplex.com

Biblioteka jest dostepna w postaci paczki NuGet, ktora mozna pobrac za pomoca consoli PowerShell:

Install-Package EPPlus

Korzystam z niej w wielu komercyjnych projektach i goraco polecam.

 

  • Odpowiedział
  • @ | 01.07.2014
  • TRENER ALTKOM AKADEMII
Komentarze