🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Tworzenie pliku Excel w C# (Open XML SDK)

Pokażę teraz, jak skonstruować plik Excel w C# — bez instalowania samego Excela na serwerze. Użyjemy biblioteki Open XML SDK (DocumentFormat.OpenXml), oficjalnego pakietu Microsoftu do czytania i zapisu formatów Office (.xlsx, .docx, .pptx) na czystym XML-u, bez zależności od zainstalowanego Office.

To trzyczęściowa seria: tutaj tworzymy podstawowy skoroszyt z pojedynczą wartością, w części 2 dodajemy stylowanie komórek (czcionki, kolory, obramowania, szerokości kolumn), a w części 3 generujemy wykres słupkowy na podstawie danych w arkuszu.

Open XML SDK vs biblioteki wyższego poziomu

Zanim zaczniemy — warto wiedzieć, że Open XML SDK jest celowo niskopoziomowe. Odzwierciedla dokładną strukturę wewnętrzną pliku .xlsx (który jest w rzeczywistości archiwum ZIP z plikami XML w środku), więc nawet zapisanie jednej komórki wymaga kilku kroków: dokument → część skoroszytu → część arkusza → dane arkusza → wiersz → komórka. Biblioteki takie jak ClosedXML czy EPPlus owijają tę złożoność w znacznie prostsze API (worksheet.Cell("A1").Value = "test";). Open XML SDK ma jednak przewagę tam, gdzie liczy się wydajność przy generowaniu bardzo dużych plików albo brak zależności licencyjnych — EPPlus w wersjach komercyjnych wymaga płatnej licencji, Open XML SDK jest w pełni darmowe i oficjalnie wspierane przez Microsoft.

Krok po kroku

  1. Przechodzimy do Visual Studio i tworzymy nową aplikację konsolową C#.
  2. Dodajemy odniesienie do pakietu NuGet: DocumentFormat.OpenXml
  3. Dodajemy nową klasę i nazywamy ją np. Test
  4. Tworzymy publiczną metodę o nazwie CreateExcelDoc. W tej metodzie utworzymy i zapiszemy nasz plik Excel.
  5. Importujemy odpowiednie przestrzenie nazw.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace Pdf
{
    public class Test
    {
        public void CreateExcelDoc(string fileName)
        {
        }
    }
}

6. Tworzymy nowy dokument arkusza kalkulacyjnego i przekazujemy nazwę pliku oraz typ dokumentu jako parametry.

using (SpreadsheetDocument document = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook))
{  }

7. SpreadsheetDocument musi mieć WorkbookPart i WorksheetPart. Dodamy następujący kod w bloku using.

using (SpreadsheetDocument document = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook))
{
      WorkbookPart workbookPart = document.AddWorkbookPart();
      workbookPart.Workbook = new Workbook();

      WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
      worksheetPart.Worksheet = new Worksheet(new SheetData());
}

8. Dołączamy arkusze do skoroszytu. Skoroszyt będzie zawierać jeden lub wiele arkuszy.

Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());

9. Następnie dodajemy jeden lub wiele arkuszy.

Sheet sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Arkusz 1" };
sheets.Append(sheet);

10. Na koniec zapis skoroszytu.

workbookPart.Workbook.Save();

11. W metodzie Main klasy Program tworzymy obiekt naszej klasy Test i wywołujemy metodę CreateExcelDoc, przekazując ścieżkę do pliku.

static void Main(string[] args)
{
     Test test = new Test();
     test.CreateExcelDoc(@"C:\Excel\test.xlsx");
}

12. Jeśli chcemy coś zapisać, dodajemy SheetData do arkusza. SheetData działa jak kontener, do którego trafiają wszystkie wiersze i kolumny.

SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());

13. Następnie dodajemy wiersz Row. Klasa Row reprezentuje wiersz w arkuszu kalkulacyjnym. Każdy wiersz może zawierać jedną lub więcej komórek Cell. Każda komórka ma CellValue, która zawiera rzeczywistą wartość, oraz DataType, który mówi Excelowi, jak tę wartość interpretować.

Row row = new Row();
Cell cell = new Cell() { CellValue = new CellValue("test"), DataType = CellValues.String };
row.Append(cell);
sheetData.AppendChild(row);
worksheetPart.Worksheet.Save();

Pułapka: komórki liczbowe zapisane jako String

Częsty błąd początkujących z Open XML SDK: zapisanie liczby jako DataType = CellValues.String zamiast CellValues.Number. Plik otworzy się bez błędu, ale w Excelu taka “liczba” będzie wyrównana do lewej (jak tekst), nie zsumuje się w formule SUM(), i Excel nie pozwoli jej posortować numerycznie:

// ❌ Wygląda jak liczba, ale Excel traktuje ją jak tekst
var zlyTyp = new Cell() { CellValue = new CellValue("42"), DataType = CellValues.String };

// ✅ Poprawnie — Excel rozpozna to jako liczbę
var dobryTyp = new Cell() { CellValue = new CellValue(42), DataType = CellValues.Number };

Ten sam mechanizm zobaczysz w części 3, gdzie wartości liczbowe dla wykresu muszą być poprawnie otypowane, żeby Excel w ogóle mógł je narysować.

Cały kod klasy Test

public class Test
{
    public void CreateExcelDoc(string fileName)
    {
        using (SpreadsheetDocument document = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook))
        {
            WorkbookPart workbookPart = document.AddWorkbookPart();
            workbookPart.Workbook = new Workbook();

            WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
            worksheetPart.Worksheet = new Worksheet();

            Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());

            Sheet sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Arkusz 1" };
            sheets.Append(sheet);

            workbookPart.Workbook.Save();

            SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
            Row row = new Row();
            Cell cell = new Cell() { CellValue = new CellValue("napis test "), DataType = CellValues.String };
            row.Append(cell);

            sheetData.AppendChild(row);
            worksheetPart.Worksheet.Save();
        }
    }
}

Uruchom projekt i sprawdź wygenerowany plik Excel. Cały kod źródłowy serii dostępny jest na GitHubie.

FAQ

Czy blok using wystarczy, żeby plik na pewno się zapisał?
using gwarantuje wywołanie Dispose(), ale to jawne wywołania Save() na poszczególnych częściach (Workbook.Save()Worksheet.Save()) faktycznie zapisują dane do wewnętrznego archiwum — pomiń jedno z nich, a odpowiadająca mu część pliku pozostanie pusta lub nieaktualna.

Czy mogę tym samym mechanizmem czytać istniejące pliki Excel, nie tylko je tworzyć?
Tak — SpreadsheetDocument.Open(fileName, isEditable: false) otwiera istniejący plik do odczytu tą samą biblioteką, choć nawigacja po już istniejącej strukturze XML bywa równie żmudna jak jej tworzenie.

https://github.com/mariuszjurczenko/ExcelOpenXml

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

53 comments

  1. I in addition to my buddies were studying the great helpful tips found on the blog while then came up with an awful feeling I never thanked the web site owner for those tips. My guys became for this reason excited to see all of them and now have pretty much been tapping into these things. Many thanks for genuinely indeed thoughtful as well as for making a decision on this kind of helpful resources millions of individuals are really needing to be aware of. My personal honest regret for not expressing appreciation to earlier. Malynda Timothee Ximenez

  2. Its like you read my mind! You seem to know so much about this, like you wrote the book in it or something. I think that you can do with some pics to drive the message home a little bit, but other than that, this is magnificent blog. A great read. I will definitely be back. Tami Lyle Farrica

  3. Having read this I believed it was rather informative. I appreciate you finding the time and energy to put this information together. I once again find myself spending a lot of time both reading and posting comments. But so what, it was still worth it! Mari Derron Chadburn

  4. Hiya, I am really glad I have found this information. Today bloggers publish only about gossip and web stuff and this is really annoying. A good website with interesting content, this is what I need. Thanks for making this site, and I will be visiting again. Do you do newsletters by email? Allina Ralph Charline

  5. I enjoy you because of all of your effort on this web site. My mother enjoys making time for investigation and it is simple to grasp why. We know all of the powerful medium you provide effective tips on the blog and as well foster contribution from other people on this concern then our favorite child has always been becoming educated a great deal. Take advantage of the rest of the year. Your carrying out a stunning job. Ashli Kermy Simdars

  6. I feel genuinely thrilled to have seen your current webpage and appearance forward to be able to so many more interesting times reading here. Cheers yet again for all typically the details. Beilul Hersh Jollenta

  7. I blog quite often and I truly appreciate your information. Your article has truly peaked my interest. I will book mark your site and keep checking for new information about once per week. I opted in for your Feed too. Renate Byram Braynard

  8. Po przejrzeniu kilku postów na blogu w Twojej witrynie, szczerze doceniam Twój sposób pisania bloga. Dokonałem rezerwacji i oznaczyłem to na mojej liście zakładek i wrócę w najbliższej przyszłości.

  9. I’ve been browsing online greater than 3 hours as of late, but I by no means discovered any attention-grabbing article like yours.
    It is lovely price enough for me. Personally, if all web owners and bloggers made excellent content as you probably did, the net can be
    much more helpful than ever before.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →