Kimi zaman geliştirdiğimiz uygulamalarda raporlara ihtiyaç duyuyoruz. Bu raporları oluşturmak için çeşitli araçlar kullanıyoruz. Bu araçlardan birisi de PDF(Portable Document Format - Taşınabilir Belge Biçimi) dosyalarıdır. Bu yazımızda, basit anlamda hazırlayacağımız raporları PDF olarak görüntülemek amacıyla kullanabileceğimiz iTextsharp adında bir kütüphaneden bahsetmek istiyorum.
iTextsharp dll dosyasını http://sourceforge.net/projects/itextsharp adresinden ücretsiz olarak indirebilirsiniz. Ayrıca yapacağımız uygulamada veritabanı olarak AdventureWorks veritabanını kullandım. http://msftdbprodsamples.codeplex.com/ adresinden Microsoft Sql Server için hazırlanmış örnek veritabanlarını indirip bilgisayarınıza kurabilirsiniz.
Uygulama için gerekli olan veritabanı ve dll’i hazırladıktan sonra artık işin kod kısmına geçebiliriz.
NOT: Örneği VS 2010 Express sürümünü üzerinde bir WPF uygulaması kullanarak hazırlayacağım.
Uygulamamıza öncelikle Solution Explorer->References->Add Reference diyerek iTextsharp dll’ini ekliyoruz. Daha sonra veritabanına işlemleri için Add->NewItem->Ado.Net Entity Data Model diyerek .edm dosyasını ekliyoruz. .edm dosyasının adını verdikten sonra çıkan Entity Data Model Wizard ekranında Generate From Database’i seçip ilerliyoruz. Bir sonraki ekranda New Connection deyip açılan ekranda Data Source olarak Microsoft SQL Server Database File; Data Provider olarak da .Net Framework Data Provider for SQL Server’ı seçiyoruz. Continue dediğimizde çıkan ekranda browse diyerek AdventureWorks veritabanını seçiyoruz. Dilerseniz Test Connection diyerek bağlantınızın çalışıp çalışmadığını kontrol edebilirsiniz. Bu işlem tamamlandıktan sonra tekrar bağlantı oluşturduğumuz ekrana dönüyoruz ve ilerliyoruz.
Eğer AdventureWorks veritabanı projeyi oluşturduğunuz klasör içinde değilse karşınıza aşağıdaki gibi bir ekran çıkacak ve veritabanının proje klasörüne kopyalayıp bağlantı cümlesini yeniden yapılandırmak isteyip istemediğinizi soracak. Burada seçim size kalmış dilerseniz veritabanını proje klasörüne kopyalayabilirsiniz yalnız bu durumda AdventureWorks MDF dosyasının 175 mb. civarı bir boyutu olduğunu unutmayın ry tablolarını seçip EDM’ye dahil ediyoruz ve işlemi tamamlıyoruz.
Şimdi sıra geldi raporumuzu oluşturacak metodumuzu yazmaya. Kod kalabalığını ortadan kaldırmak amacıyla metodu 3 ayrı alt metoda bölmeyi tercih ettim :
Açtığımız Window’un .cs uzantılı kod kısmına geçiyoruz. Öncelikle gerekli olan global değişkenleri tanımlıyoruz.
BaseFont _bFont;
Phrase _emptyString;
int _normalFontSize = 7;
AdventureWorks_DataEntities _dt = new AdventureWorks_DataEntities();
Daha sonra Constructor’da gerekli nesneleri örnekliyoruz.
_bFont = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\tahoma.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //Türkçe karakter sorununu çözmek amacıyla dökümanımızda kullanmak üzere bilgisayarımızda bulunan fontlardan birini seçiyoruz ve base font olarak olarak tanımlıyoruz. Daha sonra oluşturacağımız tüm fontları bu base fonttan kalıtıyoruz.
_emptyString = new Phrase("x", new Font(_bFont, _normalFontSize, Font.NORMAL, BaseColor.WHITE));// İçinde veri olmayan bir hücre oluşturmamız gerektiğinde hücrenin değerine bu şekilde bir metin vermemiz gerekiyor. Aksi taktirde boş hücre yok sayılıp bir sonraki hücre onun yerine ekleniyor. Bu da tablomuzu oluştururken hata oluşmasına dolayısıyla tablonun oluşturulamamasına ya da yanlış bir tablo oluşmasına neden oluyor.
Burada BaseFont tipinden bir nesne tanımlayıp örneklememizin ve bundan sonra oluşturacak olduğumuz Font tipinden nesneleri bu base font’tan kalıtacak olmamızın sebebi Türkçe karakter sorununun önüne geçmek.
public void GetHeaderTable(Document document) {
#region Fields
BaseColor _headerColor = new BaseColor(000, 019, 127);
Font _headerFont = new Font(_bFont, 14, Font.BOLD, _headerColor);
#endregion
#region Başlık
PdfPTable header = new PdfPTable(new float[] { 500 });// hücrelerin boyutlarını tabloyu oluştururken verebileceğimiz gibi daha sonra SetWidths metodunu kullanarak da verebiliriz.
PdfPCell cell = new PdfPCell(new Phrase(string.Format("ÜRÜN LİSTESİ"), _headerFont));// Phrase nesnesini oluşturarak hücreye yazılacak metni oluşturuyoruz.
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.Border = iTextSharp.text.Rectangle.NO_BORDER;
header.AddCell(cell);
#region Boşluk
cell = new PdfPCell(_emptyString);
document.Add(header);
}
Tablo içindeki hücrelerin genişliklerini tabloyu örneklerken verebileceğimiz gibi daha sora SetWidths metodunu kullanarak da verebiliriz.
PdfPTable header = new PdfPTable(new float[] { 500 });
Ya da
PdfPTable header = new PdfPTable(1); //buradaki 1 değeri tablonun kaç hücreli olduğunu gösterir.
header.SetWidths(new float[] { 500 });
public void GetColumnTable(Document document) {
BaseColor _columnHeaderColor = new BaseColor(000, 019, 127);
Font _columnHeaderFont = new Font(_bFont, _normalFontSize, Font.BOLD, _columnHeaderColor);
#region MainTable
PdfPTable mainTable = new PdfPTable(new float[] { 50, 150, 300 });
mainTable.DefaultCell.BorderWidth = 1;
mainTable.DefaultCell.BorderColor = BaseColor.BLACK;
#region CellProperties
PdfPCell cell = new PdfPCell();
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
#region Order
cell.Phrase = new Phrase("SIRA NO", _columnHeaderFont);
mainTable.AddCell(cell);
#region SubCategory
cell.Phrase = new Phrase("ALT KATEGORİ", _columnHeaderFont);
#region productTable
#region productTable Details
PdfPTable productTable = new PdfPTable(new float[] { 200, 100 });
#region Product
cell.Phrase = new Phrase("ÜRÜN", _columnHeaderFont);
cell.Colspan = 2;//iki hücreyi birleştirmek amacıyla Colspan property'sini kullanabilirsiniz.
productTable.AddCell(cell);
cell.Colspan = 1;// her seferinde yeni bir cell nesnesi örneklemediğimiz için Colspan property'sini 1 yapıyoruz.
#region ProductName
cell.Phrase = new Phrase("ADI", _columnHeaderFont);
cell.Rotation=90; //metnin hücre içinde dikey görünmesini istersek öncelikle Rotation property'sini 270 olarak set ediyoruz.
cell.Rotate(); // Rotate metodunu çağırarak hücreyi döndürüyoruz.
#region ListPrice
cell.Phrase = new Phrase("LİSTE FİYATI", _columnHeaderFont);
mainTable.AddCell(productTable);
document.Add(mainTable);}
Tablo oluştururken bazen resimdeki gibi, bir hücreyi iki veya daha fazla satır ya da kolona bölmemiz gerekebilir.
Bu durumda şöyle bir yol izliyoruz: Öncelikle yeni bir tablo oluşturup (#region productTable ile başlayan alt tablomuz) bu tablonun değerlerini set ediyoruz.Daha sonrasında yine ana tablomuzda hücreleri oluşturduğumuz yolla bu tablo için de gerekli hücreleri oluşturup tabloya ekliyoruz.
Bir hücreyi birleştirmek amacıyla HTML tablolarında kullandığımız Colspan attribute’u gibi burada da Colspan property’si mevcut.
cell.Colspan = 2;
şeklinde bir atama ile bu işlemi gerçekleştirebilirsiniz.
Ayrıca tablo içindeki bir hücrenin metninin dikey olmasını da sağlayabilirsiniz, bunun için yapmanız gereken cell.Rotation = 90; şeklinde bir atama işlemi ile Rotation property’sinin değerini set etmek ve ardından cell.Rotate(); metodunu çağırmak.
NOT: Rotation property’sine sadece 90 ve katları değerler verebilirsiniz aksi taktirde uygulama exception fırlatır.
Alt tabloyla ilgili işlemleri tamamladıktan sonra ana tablomuza bu tabloyu ekliyoruz.
Son olarak da tablomuza değerlerin eklendiği metodun içeriğini inceliyoruz.
public void GetValueTable(Document document)
{
int order = 1;
Font _nFont = new Font(_bFont, _normalFontSize);
#region MainTable Details
foreach (var subCategory in _dt.ProductSubcategory)
cell.Phrase = new Phrase(order.ToString(), _nFont);
#region SubCategoryName
cell.Phrase = new Phrase(subCategory.Name, _nFont);
foreach (var product in subCategory.Product)
cell.Phrase = new Phrase(product.Name, _nFont);
cell.Phrase = new Phrase(product.ListPrice.ToString("F2"), _nFont);
order++;
document.Add(mainTable);
Burada yukarıda örneklediğimiz _dt (AdventureWorks_DataEntities) nesnesinin ProdcuctSubcategory tablosu içinde foreach döngüsüyle gezerek her bir alt kategoriye bağlı bulunan ürünlerin adını ve liste fiyatını tablomuza ekliyoruz. Ürünlerin adını ve liste fiyatını tabloya eklerken yine productTable adında bir alt tablo oluşturarak mainTable tablomuzun ilgili hücresine her döngüde bu alt tabloyu ekliyoruz.
Son olarak da tüm bu metotları toplayıp PDF dökümanımızı oluşturan metodumuz;
public string GetTable()
Document document = new Document(PageSize.A4);//Sayfa boyutunu belirtiyoruz. Dilersek Rotate() metodunu kullanarak sayfayı yatay olarak kullanabiliriz.
string _fileName = string.Format(@"{0}Ürün Listesi.pdf", AppDomain.CurrentDomain.BaseDirectory);
try
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(_fileName, FileMode.Create));
document.Open();
GetHeaderTable(document);
GetColumnTable(document);
GetValueTable(document);
return _fileName;
catch (DocumentException docEx)
MessageBox.Show(string.Format("Hata:{0}", docEx.Message), "Hata", MessageBoxButton.OK, MessageBoxImage.Error);
return string.Empty;
finally
document.Close();
Burada öncelikle Document nesnesi aracılığıyla PDF dökümanımızı oluşturuyoruz. Document nesnemizi örneklerken PageSize enum’ını kullanarak hangi tip sayfada göstereceğinizi belirtebilirsiniz.
Document document = new Document(PageSize.A4);
Document nesnesinin PageSize property’si readonly only olduğu için sayfa tipini daha sonradan verme şansımız yok. Ayrıca burada aşağıdaki gibi bir kullanımla tabloyu sayfaya yatay olarak da yerleştirebilirsiniz.
Document document = new Document(PageSize.A4.Rotate());
PDF dosyasını istediğiniz bir yere kaydedip buradan gösterebilirsiniz. Ben uygulamanın olduğu Debug klasörünün içine kaydettim.
NOT: Klasik Windows uygulamalarından hatırlayacaksınız bir uygulamanın bulunduğu yolu almak için Application.StartupPath property’sini kullanıyorduk; ancak WPF uygulamalarında durum biraz değişmiş. WPF’ de yeni olduğum için ben de bu uygulama sayesinde bunu öğrenmiş oldumUygulamanın bulunduğu yolu AppDomain.CurrentDomain.BaseDirectory property’sinden elde ediyoruz.
Uygulamayı kaydetmek amacıyla aşağıdaki gibi bir PdfPWriter nesnesi örnekliyoruz.
Document nesnemizi açıp tabloları oluşturmak için yazdığımız 3 metodu çağırıyoruz ve document nesnesiyle işimiz bittiğinde kapatıyoruz.
Bu kadar koddan sonra şimdi sıra geldi oluşturduğumuz PDF dökümanını görüntülemeye. Bunun için Designer kısmına geçip ekrana bir Button bir de WebBrowser nesnesi ekliyoruz. Daha sonra butonun Click eventinde aşağıdaki kodu yazıyoruz.
string path = GetTable();
wbReport.Navigate(new Uri(path));
NOT: WebBrowser kontrolünün Navigate metoduna parametre olarak path nesnesini verdiğinizde aşağıdaki gibi bir hata ile karşılaşıyorsunuz. Bu nedenle bir Uri nesnesi örnekleyip path değişkenini bu uri’ye parametre olarak verip oluşturduğunuz bu Uri nesnesini de WebBrowser kontrolünün Navigate metoduna parametre olarak vermeniz gerekiyor.
Tüm bu işlemlerin sonunda aşağıdaki gibi bir ekran görüntüsü elde ediyoruz.
Görüldüğü gibi Foxit Reader isimli programdan oluşturduğumuz PDF dokümanını sorunsuz şekilde görebiliyoruz.
Böylece yazımızın sonuna geldik. Bu yazımda iTextSharp.dll dosyasını kullanarak raporlarımızın bir PDF dosyası olarak nasıl oluşturulacağını inceledik. Anlatımdan kaynaklanan eksikliklerden dolayı şimdiden affınıza sığınıyorum. Bir sonraki yazıda görüşmek dileğiyle.
Canan Gümrükçüoğlu http://canangumrukcuoglu.wordpress.com