Şu ana kadar VSTO'nun temel prensiplerini gördük, ve siz de aldığınız c# molasından sonra buraya gelmiş olmalısınız.
Daha önce belirttiğimiz gibi, VSTO ile çalışırken Excel'in kendi API'si ile çalışabileceğimiz gibi, bazı işlemlerin sadeleştirildiği ve yeni özelliklerin eklendiği 3rd party kütüphanelerle çalışabileceğimizi söylemiştik. Bu bölümde bunlardan ilki olan ClosedXML'i göreceğiz. Ama öncesinde bir 3rd party paketi kurmak için Nuget Manager nasıl kullanılıyor ona bakacağız.
Öncelikle ClosedXML'in nasıl kullanıldığını, ne tür fonksiyonalitesi olduğunu görmek için bu paketin github repo'suna gidelim. Bu sayfada çok basit bir örnek vermişler. Önce bunu inceleyelim, akabinde daha fazla bilgi için bizleri yönlendirdikleri wiki sayfasına bakacağız. Bu kütüphanede işin özü, dosya kapalıyken onda işlem yapmaktır.
using (var workbook = new XLWorkbook())
{
var worksheet = workbook.Worksheets.Add("Sample Sheet");
worksheet.Cell("A1").Value = "Hello World!";
worksheet.Cell("A2").FormulaA1 = "=MID(A1, 7, 5)";
workbook.SaveAs("HelloWorld.xlsx");
}
Buradaki using bloğunun ne olduğunu bildiğinizi varsayıyorum(c# molası verdiğinizde bunu da öğrenmiş olmanız lazım). Özetle, çeşitli object yaratma işlemlerinden sonra sonlandırma işlemini de (unutulma durumuna karşın) otomatik olarak yaparlar. Burda bir Worobook nesnesi (bellekte) yaratılıyor. Sonlandırma işlemi de bellekte olmak durumunda, o yüzden using bloğuna alınıyor.
İlk satırı anlatmaya devam edelim. Workbook nesnesi yaratılıyor ama burada gerek Interop gerek klasik Excel kullanımında aşina olduğumuzun aksine, sayfasız bir workbook oluşuyor. Garip, değil mi?
Peki, sonrasında bir worksheet nesnesi yaratılıyor ve yaratım anında sayfaya isim de verebiliyoruz.
Kodun kalanı oldukça basit ve anlaşılır, farklı bir Cell kullanımı dışında interop ile de aynı.
Burda farkettiyseniz, hiç Globals sınıfından veya Application nesnesinden eser yok. Zira bu aslında bir VSTO Add-in uygulamasına ait bir kod değil, herhangi bir c# projesinde kullanılabilecek bir koddur. Projemizi başlatırken "Excel VSTO Add-In" veya "Excel VSTO Workbook" seçmediysek bunlar VSTO değildir. Ancak Excel ile çalışmak için illa VSTO add-in yaratmak zorunda değiliz. Hatta Interop API'sini bile VSTO dışı projelerde kullanabiliriz. Yani şu kombinasyonlar olasıdır:
- Interop API'sini kullandığımız bir VSTO add-in projesi
- Interop API'sini kullandığımız VSTO olmayan bir proje
- 3rd Party Excel API'si ile VSTO projesi
- 3rd Party Excel API'si kullanarak VSTO olmayan bir proje
- Her iki API'yi kullandığımız bir VSTO projesi
- Her iki API'yi kullandığımız VSTO olmayan bir proje
Proje tipine VSTO dediğimizde, VS bizim için arka planda bir sürü ayarlama yapar, tek olayı bu. Hatta istersek(neden isteyelim ki) genel bir c# projesi açıp bunu da VSTO'ya dönüştürebiliriz. Ve bunu istersek(yine neden isteyelim ki) notepad'de bile yapabiliriz.
Bence Excel API'sini 3rd party paketlerden ayıran en önemli özelliği, kodun çalıştığı PC'de Excel'in kurulu olması gerektiği ve bir workbookla çalışırken onun açık olması gerektiğidir. 3rd party paketlerde ise işlemler genelde bellekte, yani dosyalar kapalıyken, yapılır. Hatta bu sayfada tanıyacağımız ClosedXML'in adından bile bu anlaşılıyor. Bununla beraber dosya açıkken de işlem yapılabilir, ki bizim durumumuzda her iki seçenek te olacak; bazen aktif workbook üzerinde işlem yapacağız, bazen de başka (kapalı) bir dosyaya yazma işlemi yapacağız. Ancak bu paketlerin nimetlerinden faydalanmak için dosya açık olsa bile önce onu kapatıp bellekte işlerimizi yapıp, en son dosyayı tekrar açabiliriz. Tabi açmak için Interop'a başvurmamız gerkeiyor.
Şimdi ClosedXML'i daha detaylıca görmeden önce, yukarıdaki kodu Interop API ile nasıl yapardık ona bakalım.
Excel.Workbook wb = app.Workwb.Worksheets[1].Name = "Sample Sheet"; //sayfa yaratmaay gerek yok, zaten default bir sayfamız var, biz bunun (1'den fazla olsa da ilkini) adını değiştiriyoruz
app.Range["A1"].Value = "Hello World"; //bu ve alttakinde ise worksheet nesnesi üzerinden değil app nesnesi üzerinden erişiyoruzden erişiyoruz
app.Range["A2"].Formula = "=MID(A1,7,5)";
wb.SaveAs("HelloWorld_Interop.xlsx");
Ben iki kodu da ribbonda iki butona atadım. ClosedXML yoluyla yapınca görünürde hiçbirşey olmadı(beklediğim üzere). Dosya arka planda oluştu. Interop ile yapınca, dosyayı bellekte değil direkt olarak o an açık olan Excel oturumu içinde yarattı, ve SaveAs yaptıktan sonra da açık olarak kaldı. Aynı etkiyi ScreenUpdating=False diyerek de yapabilirdik, ama maksat hızlı çalışmaksa o zaman Interop yerine diğer kütüphaneleri tercih etmek daha doğru olacaktır.
Temel işlemlerin bir kısmı
Birkaç koddan sonra göreceksiniz ki, bu kütüphane kullanımını büyük ölçüde VBA syntaxına benzetmeye çalışmışlar, o açıdan güzel olmuş.
Burda dikkat edilmesi gereken husus şu. Kütüphaneler zaman içinde evrimleşebiliyor. Verdikleri örneklerin bir kısmı ise güncelleme yapmadıkları için geçersiz olabiliyor. Bunları GitHub kullanmayı biliyorsanız Github üzerinden kütüphaneyi yaratanlara bildirebiliyorsunuz. Forumlarda aradığınızda bu problemin nasıl giderilleceğine dair bilgiler bulunabileceği gibi, "What Is New?" veya "Changes" gibi alanlarda da duyurusunu görebilirsiniz. Sorunu kendiniz de çözmeye çalışabilirsiniz tabi. Mesela aşağıdaki kodların bir kısmında sorun vardı, bunları yorum olarak ekledim.
//Yeni dosya yaratma dosya yaratma
var wb = new XLWorkbook();//Sayfası olmayan bir dosyayı bellekte yaratır
//var olan dosyayı açma(bellekte)
var mevcut = new XLWorkbook("MevcutDosya.xlsx");
//Bir dosyaya sayfa ekleme
var ws = wb.Worksheets.Add("Yenisayfa1");
var ws2 = wb.AddWorksheet("Yenisayfa2");
//Range işlemleri:
ws.Cell("A1").Value = "selam"; //[] değil () kullanıldığına dikkat, zira bunu bir indexli proerty olarak dğeil metod gibi ele almışlar
ws.Cell("A2").Value = new DateTime(1919, 1, 21);
var alan = ws.Range("B1:D20");
var ozel = alan.FirstCell(); // FirstCellUsed, FirstRow, LastColumn gibi çeşitli türevleri de var
var usedrange = ws.RangeUsed();
//format işlemleri
alan.Style.NumberFormat.NumberFormatId = 15; //Bu kütüphanenin dayandığı OpenXML'in öncenden tanımlı formatlarından
alan.Style.NumberFormat.Format = "$ #,##0";
//Zincirleme formatlama
alan.FirstCell().Style
.Font.SetBold()
.Fill.SetBackgroundColor(XLColor.CornflowerBlue)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
//Table işlemleri
var excelTable = alan.CreateTable();
//Lambda Expressions ile koşullu işlemler yapma
var rows = alan.Rows(r => r.Cell(3).GetString() == "E"); // 3.kolondaki değeri E olanlar
foreach (var row in rows) //wiki'deki örnekte doğrudan ForEach metodu kullanılmıştı ama bu metod List sınıfının bir metodudur, doğrudan kullanılamaz. Biz bunu şimdi klasik for ile yapalım, bir alttaki örnekte List'e çevirip öyle yapalım
row.Delete();
var hucreler = alan.Cells(c => c.DataType == XLDataType.Text); // XLDataType, daha önce XLCellValues idi, yeni versiyonda değişmiş
hucreler.ToList().ForEach(c => c.Style.Fill.BackgroundColor = XLColor.LightGray); //List'e çevirerek ForEach kullanmak da bir diğer yöntem
Büyük ve/veya çok sayıda dosya ile çalışıyorsanız wiki'deki performans yönetimi ile ilgili notları da mutlaka okuyun.
Tabi hepsi bu kadar değil. Ben kendimce önemli gördüklerimi buraya aldım, başlangıç için bunlar yeterli olacaktır. Diğer bütün işlem tipleri için ihtiyaç duydukça wiki'ye başvurabilirsiniz.
Açık dosyalarla çalışmak
ClosedXML ile çalışırken o anda Excel'de açık olan bir dosya ile çalışmak istersek, aşağıdaki gibi bir kod ile açık dosyayı elde eden bir kod yazarız. Akabinde bunu ana kodumuza dahil ederiz. Ben bu ve bunun gibi sık kullanılma ihitmali olan fonskyionları bir Utiliy paketi içine koydum(VolkansUtility), siz de bunu kullanabilirsiniz, aslında kullanmanızı şiddetle tavsiye ederim, çünkü oldukça faydalı kodlar var içinde.
Normalde activeworkbook'u bu kadar dolambaçlı bir şekilde elde etmeye gerek yok tabi. Bunun için hiç de Utility'deki fonksiyona gerek duymadan doğrudan Globals.ThisAddin.Application.ActiveWorkbook diyerek de alabilirdik ancak, hem Utility içindeki kodu kullanmak daha kısa, hem de bu kodu VSTO dışındaki başka bir projede de kullanabilirsiniz. Üstelik, Utility içinde başka işimize yarayacak birçok hazır fonksiyon olacak. O yüzden şimdi Utility ile ilerleyeceğiz.
public static Excel.Workbook GetActiveWorkbook()
{eWorkbook()
{
Excel.Application app = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
app.Visible = true;
return app.ActiveWorkbook;
}
Elimizde bu kod olduğuna göre şimdi ClosedXML kodu ile birleştirebiliriz.
private void button4_Click(object sender, RibbonControlEventArgs e)
{
//Önce Interop ile başlıyoruz, zira açık olan bi dosyada işlem yapacağız
Excel.Workbook wb = ExcelRW.GetActiveWorkbook();//VolkansUtility içinde
string filepath = wb.FullName;
wb.Save(); wb.Close(); //ClosedXML ile işlem yapabilmek için dosyayı geçici olarak kaydedip kapatıyoruz
//Şimdi ClosedXML zamanı
var cxwb = new XLWorkbook(filepath);
var ws = cxwb.Worksheet(1);
var alan = ws.Range("B1:D20");
var hucreler = alan.Cells(c => c.DataType == XLDataType.Text);
hucreler.ToList().ForEach(c => c.Style.Fill.BackgroundColor = XLColor.LightGray);
cxwb.Save();
//Şimdi tekrar Interop vakti
app.Workbooks.Open(filepath);
}
Bu arada, kapalı bi dosyayı da açmak için yine Interop'dan destek alabiliriz. Mantık yukarıdaki kod ile aynıdır.
Data işlemleri
Burdaki detayları anlamak için c#'taki DataTable yapısı başta olmak üzere veri yapılarını iyi bilmeniz gerekiyor.
Bir veri yapısındaki bilgileri Excel'e nasıl alabileceğiniz
Inserting Data başlığı altında açıkça anlatılmış durumda. Ben burdaki iki metoda ait küçük bir farktan bahsetmek istiyorum. Aslında wiki'de bu bilgi var ama gözden kaçabilir diye ben de vurgulamak istedim. InsertData kolon başlıklarını eklemez ve Range döndürürken, InsertTable başlıkları koyar ve Table döndürür.
Bunun dışında tersine ihtiyacınız olursa, yani Excel'deki verileri bir veri yapısına veya bir DataTable'a almak istiyorsanız bunun için yine benim Utility paketindeki bir metodu kullanabilirsiniz. Bu paket içinde ClosedXML ile Interop farkını görebileceğiniz iki metod var. WriteDataTableContentToActiveWBWithInterope ve WriteDataTableContentWithClosedXML. İkisinin satır sayısına bakarsanız 3rd party paketlerin nasıl kolaylıklar sağladığını görürsünüz. Tabi yalnız satır sayısı sizi yanıltmasın, Interop ile kod yazmak uzun sürüyor fakat süre olarak bakıldığında Interop daha hızlıdır, en azından bu örnekler için. Kodun içinde süre tutan bir kısım da vardır, siz de deneyebilirsiniz.