Makale Özeti

Bu makalemde sizlere çoklukla kullandığımız bir bileşen olan DataTable'in nasıl genişletilebileceği ve yeni özelliklerin nasıl katılabileceğini inceleyeceğiz.Standart DataTable metodlarını değiştirmeden katacağımız özellikler standart metodların ve özelliklerin yanısıra geliştirdiğimiz uygulamalarda birçok yerde kullanabileceğimiz ve kodlama maliyetini düşürmekle kalmayıp aynı zamanda kullanım kolaylığı sağlayan özellikler olacaktır. Bu özellikler arasında DataTable'dan direkt olarak html tablosu üretme, Excel'e export, Word'e export ve DataAdapter ile entegre bir biçimde kolay çalışabilme olacaktır.

Makale

         Merhabalar,         

         Bu makalemde sizlere çoklukla kullandığımız bir bileşen olan DataTable'in nasıl genişletilebileceği ve yeni özelliklerin nasıl katılabileceğini inceleyeceğiz.Standart DataTable metodlarını değiştirmeden katacağımız özellikler standart metodların ve özelliklerin yanısıra geliştirdiğimiz uygulamalarda birçok yerde kullanabileceğimiz ve kodlama maliyetini düşürmekle kalmayıp aynı zamanda kullanım kolaylığı sağlayan özellikler olacaktır. Bu özellikler arasında DataTable'dan direkt olarak html tablosu üretme, Excel'e export, Word'e export ve DataAdapter ile entegre bir biçimde kolay çalışabilme olacaktır.

         Öncelikle DataTable yapımızın nasıl oluşturulduğuna bakalım.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Collections;
using System.IO;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Data.Common;
 
 
namespace ExtendedControls
{
    public class ExtendedDataTable : DataTable
    {
 
        public ExtendedDataTable()
            : base()
        {
 
        }
         Gördüğünüz gibi kontrolümüzü DataTable class'ından inherit ederek kodlamaya başlıyoruz ve Constructor olarak base'ini çağıracak şekilde parametre almayan tek bir tanımlama yapıyoruz.
 
        public void ImportDataTable(DataTable dt)
        {
            this.Columns.Clear();
            this.Rows.Clear();
            foreach (DataColumn dc in dt.Columns)
            {
                this.Columns.Add(dc.ColumnName, dc.DataType);
            }
            foreach (DataRow dr in dt.Rows)
            {
                this.Rows.Add(dr.ItemArray);
            }
        }
          Bu kısımda yazmış olduğumuz ImportDataTable metodunun amacı normal bir DataTable'i kendisinden daha büyük bir yapı olan ExtendedDataTable'a cast edemediğimiz için yazılmıştır. Bu sayede elimizde bulunan bir datatable i tanımlamış olduğumuz ExtendedDataTable'a import edebilir ve yazdığımız gelişmiş özellikleri kullanabiliriz. Metodun çalışma mantığını inceleyecek olursak parametre olarak gelen DataTable'ın kolonlarının ExtendedDataTable içerisinde oluşturulması ve Row'ların tekrardan eklenmesiyle oluşturulduğunu görebilirsiniz.
 
        private string _TableStyle;
        private string _HeaderStyle = "background-color:#075b98;color:#FFFFFF;padding-left:10px;font-weight:bold;height:25px;";
        private string _RowStyle = "background-color:#e1edfb;padding-left:10px;height:25px;";
        private string _AlternatingRowStyle = "background-color: #FFFFFF;padding-left:10px;height:25px;";
        private Hashtable _ColumnNames = new Hashtable();
 
        public string TableStyle
        {
            get { return _TableStyle; }
            set { _TableStyle = value; }
        }
        public string HeaderStyle
        {
            get { return _HeaderStyle; }
            set { _HeaderStyle = value; }
        }
        public string RowStyle
        {
            get { return _RowStyle; }
            set { _RowStyle = value; }
        }
        public string AlternatingRowStyle
        {
            get { return _AlternatingRowStyle; }
            set { _AlternatingRowStyle = value; }
        }
        public Hashtable ColumnNames
        {
            get { return _ColumnNames; }
        }
         Yukarıda görmüş olduğunuz member değişkenler ve propertyler Html'e export edilen DataTable'ın görünüşünün belirlendiği özelliklerdir. İsimlerinden de anlaşılabileceği gibi Tablo, Başlık, Satır, Alternatif Satır stilleri bu özellikler sayesinde belirlenebilmektedir. ColumnNames Hashtable'i ise Hangi kolonun başlığının oluşturulan HTML tablosunda nasıl gözükeceğine ait bilginin tutulduğu propertydir.
 
        public string ToHTML()
        {
            try
            {
                int columnCount = 0;
                StringBuilder sb = new StringBuilder();
                sb.Append(@"<table style=""");
                sb.Append(TableStyle);
                sb.Append(@""">");
                sb.Append(@"<tr style=""");
                sb.Append(HeaderStyle);
                sb.Append(@""">");
                foreach (DataColumn dc in this.Columns)
                {
                    if (ColumnNames.Keys.Count == 0 || ColumnNames.ContainsKey(dc.ColumnName))
                    {
                        sb.Append("<td>");
                        if (ColumnNames.Keys.Count == 0)
                        {
                            sb.Append(dc.ToString());
                        }
                        else if (ColumnNames.ContainsKey(dc.ColumnName))
                        {
                            sb.Append(ColumnNames[dc.ColumnName].ToString());
                        }
                        sb.Append("</td>");
                        columnCount++;
                    }
 
                }
                sb.Append("</tr>");
                if (this.Rows.Count > 0)
                {
                    for (int i = 0; i < this.Rows.Count; i++)
                    {
                        sb.Append(@"<tr style=""");
                        if (i % 2 == 0)
                        {
                            sb.Append(RowStyle);
                        }
                        else
                        {
                            sb.Append(AlternatingRowStyle);
                        }
                        sb.Append(@""">");
                        foreach (DataColumn dc in this.Columns)
                        {
                            if (ColumnNames.Keys.Count == 0 || ColumnNames.ContainsKey(dc.ColumnName))
                            {
                                sb.Append("<td>" + this.Rows[i][dc].ToString() + "</td>");
                            }
                        }
                        sb.Append("</tr>");
                    }
                }
                else
                {
                    sb.Append("<tr><td colspan=" + columnCount.ToString() + ">Kayıt Bulunamadı</td></tr>");
                }
 
                sb.Append("</table>");
 
                return sb.ToString();
            }
            catch (Exception ex)
            {
 
            }
            return "Tablo Oluşturulurken Hata";
 
        }
         DataTable'ı html'e export etmeye yarayan ToHTML metodunun içinde tanımlamış olduğumuz StringBuilder nesnesi ile html kodunu string olarak oluşturuyor olacağız.Bunun için öncelikle table tagını oluşturduktan sonra tablomuzun kolon başlıklarını oluşturmaya başlıyoruz. Burda dikkat ettiğimiz kural eğer ColumnNames Hashtable'ında bir tanımlama mevcutsa tanımladığımız kolonlar için tanımladığımız metninlerin Başlık olarak gelecek olması ve tanımlanmamış kolonları hiç üretilmeyeceğidir. Eğer ColumnNames için bir tanımlama yapmadıysak kolonlar datatabledaki başlıklarıyla oluşturulacaktır. Daha sonrasında ise tek ve çift numaralı satırları farklı stiller alacak şekilde oluşturuyoruz ve Kayıt bulunamaması durumunda kaç kolon oluşturulmuşsa o kadar kolonu colspanlar tek bir satırda birleştirip Kayıt Bulunamadı yazısını yazdırıyoruz.
 
        public void ToExcel(string fileName)
        {
            try
            {
                Microsoft.Office.Interop.Excel.Application insApplication = new Microsoft.Office.Interop.Excel.Application();
 
                System.Globalization.CultureInfo oldCI = System.Threading.Thread.CurrentThread.CurrentCulture;
                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
 
                Microsoft.Office.Interop.Excel.Workbook insWorkBook;
                insApplication.Visible = false;
 
                insWorkBook = insApplication.Workbooks.Add(Microsoft.Office.Interop.Excel.XlWBATemplate.xlWBATWorksheet);
                Microsoft.Office.Interop.Excel.Worksheet insWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)insWorkBook.Worksheets[1];
                insWorkSheet.Rows.Delete(false);
                insWorkSheet.Columns.Delete(false);
 
                for (int i = 0; i < this.Columns.Count; i++)
                {
                    insWorkSheet.Cells[1, i + 1] = this.Columns[i].ColumnName;
                }
                for (int i = 0; i < this.Rows.Count; i++)
                {
                    for (int j = 0; j < this.Columns.Count; j++)
                    {
                        insWorkSheet.Cells[i + 2, j + 1] = this.Rows[i][j].ToString();
                    }
                }
                insWorkSheet.Columns.AutoFit();
                insWorkBook.SaveAs(fileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal, null, null, null, null, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, null, null, null, null, null);
 
                insApplication.Quit();
                System.Threading.Thread.CurrentThread.CurrentCulture = oldCI;
            }
            catch (Exception ex)
            {
                StreamWriter sw = new StreamWriter(fileName);
                sw.Write(this.ToHTML());
                sw.Close();
            }
 
        }
         ToExcel metodunda ise Office Interop kullanarak bir excel dökümanı oluşturacağız, ancak bu dökümanın oluşturulması sırasında bir hata meydana gelirse oluşturduğumuz HTML kodunu bir dosyaya uzantısı xls olacak şekilde yazacağız ve Office'in web desteğinden faydalanacağız. Öncelikle Excel işlemlerini yapabilmek için bir tane Excel Uygulaması oluşturuyoruz ve WorkBookun hatasız bir şekilde eklenebilmesi için mevcut Culture bilgimizi değiştiriyoruz. Ancak işlem bittiğinde eski halin e geri döndüreceğiz. Daha Sonrasında Bu Excel Uygulamasının içinde bir Excel WorkBook'u ve onunda içinde bir Excel Sheet oluşturuyoruz. Oluşturduğumuz ExcelSheet'de bulunan tüm kolonları ve satırları sildikten sonra aynı ToHtml Metodunda olduğu gibi döngü içerisinde WorkSheet'in hücrelerine ilk önce başlıkları daha sonra ise verileri yazıyoruz. Excelde bulunan kolonların genişliklerini otomatik ayarlattıktan sonra dosyamızı parametre olarak gelen dosya ismine kaydediyor ve Excel Uygulamasını kapatıyoruz.
 
        public void ToWord(string fileName)
        {
            Microsoft.Office.Interop.Word.Application insApplication = new Microsoft.Office.Interop.Word.Application();
 
            System.Globalization.CultureInfo oldCI = System.Threading.Thread.CurrentThread.CurrentCulture;
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
 
            Microsoft.Office.Interop.Word.Document insDocument;
            insApplication.Visible = false;
 
            object Template = "normal.dot";
            object newTemplate = false;
            object DocumentType = Microsoft.Office.Interop.Word.WdDocumentType.wdTypeDocument;
            object Visible = false;
 
            insDocument = insApplication.Documents.Add(ref Template, ref newTemplate, ref DocumentType, ref Visible);
 
            Microsoft.Office.Interop.Word.Range r;
            object start = 0;
            object end = 0;
            r = insDocument.Range(ref start,ref end);
 
            object Autofit = Microsoft.Office.Interop.Word.WdAutoFitBehavior.wdAutoFitContent;
            object TableBehaviour = Microsoft.Office.Interop.Word.WdDefaultTableBehavior.wdWord9TableBehavior;
            Microsoft.Office.Interop.Word.Table insTable= insDocument.Tables.Add(r, this.Rows.Count + 1, this.Columns.Count, ref TableBehaviour, ref Autofit);
 
            for (int i = 0; i < this.Columns.Count; i++)
            {
                insTable.Cell(1, i + 1).Range.Text = this.Columns[i].ColumnName;
            }
            for (int i = 0; i < this.Rows.Count; i++)
            {
                for (int j = 0; j < this.Columns.Count; j++)
                {
                    insTable.Cell(i + 2, j + 1).Range.Text = this.Rows[i][j].ToString();
                }
            }
            object missing  = System.Reflection.Missing.Value;
            object FileName = fileName;
 
            insDocument.SaveAs(ref FileName,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing,ref missing);
 
            object SaveAs = false;
            insApplication.Quit(ref SaveAs,ref missing,ref missing);
            System.Threading.Thread.CurrentThread.CurrentCulture = oldCI;
 
 
        }
         ToWord Metodunda ise aynı ToExcel metodundaki yolu izleyeceğiz. İlk Önce Word Uygulamamızı açıyoruz daha sonrasında bunun içinde "normal.dot" templatinden bir tane work dökümanı oluşturuyoruz ve Bu dökümanın hemen başlangıçında bir tablo oluşturuyoruz. Tablonun içeriğini yine aynı şekilde doldurduktan sonra Dökümanımızı parametre olarak gelen lokasyona kaydediyor ve Word Uygulamasını sonlandırıyoruz.

         Şimdi Export metodlarının yanısıra geliştireceğimiz veritabanından data çekmek ve çekilden bu datada yapılan değişikliklerin veritabanınına yansıtılması sırasında kullanacağımız metodlar için yazılmış propertyleri inceleyelim.
 
        private SqlDataAdapter _Adapter = new SqlDataAdapter();
        public SqlDataAdapter Adapter
        {
            get { return _Adapter; }
        }
 
        private SqlConnection _Connection = new SqlConnection();
        public SqlConnection Connection
        {
            get { return _Connection; }
        }
 
        private SqlCommand _Command = new SqlCommand();
        public SqlCommand Command
        {
            get { return _Command; }
        }
         Yukarıda görmüş olduğunuz property'ler bir datatable'ın DataAdapter tarafından doldurulması için gerekli tüm nesnelerin taşındığı özelliklerdir.
 
        public void Fill()
        {
            Command.Connection = Connection;
            Adapter.SelectCommand = Command;
            Adapter.UpdateCommand = new SqlCommandBuilder(Adapter).GetUpdateCommand();
            Adapter.DeleteCommand = new SqlCommandBuilder(Adapter).GetDeleteCommand();
            Adapter.InsertCommand = new SqlCommandBuilder(Adapter).GetInsertCommand();
            Adapter.Fill(this);
        }
         Geliştirdiğimiz DataTable'ımıza yazmış olduğumuz Fill metodu bir önceki kısımda bahsettiğimiz propertylerde bulunan nesneleri kullanarak mevcut DataTable'ımıza kullanıcının belirttiği sorgu sonucunda datayı doldurmakta ve SqlDataAdapter nesnesinin Update, Delete ve Insert Command'larınıda oluşturmaktadır. Bu SqlDataAdapter nesnemiz ExtendedDataTable'ımız yaşadığı sürece yaşayacağından bu komutları daha sonra kullanabileceğiz.
 
        public void Update()
        {
            Adapter.Update(this);
        }
    }
}
         ExtendedDataTable içine yazdığımız bir diğer metod olan Update() metodu sayesinde daha önceden oluşturmuş olduğumuz SqlDataAdapter üzerinde Update işlemlerini yapabiliriz. Şimdi Bu İki metodun kullanımı ile ilgili ufak bir örnek yapalım.
 
        private void Form1_Load(object sender, EventArgs e)
        {
                ExtendedControls.ExtendedDataTable a = new ExtendedControls.ExtendedDataTable();
                a.Connection.ConnectionString = "data source=.;initial catalog=Sample;integrated security=SSPI";
                a.Command.CommandText = "Select EmployeeId,EmployeeFirstName,EmployeeLastName FROM Employees";
                a.Fill();
                dataGridView1.DataSource = a;
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            ((ExtendedControls.ExtendedDataTable)dataGridView1.DataSource).Update();
        }
         Yukarıdaki örnekte formun Load Eventinde yeni bir Extended DataTable oluşturuyorum. Daha Sonrasında Bu ExtendedDataTable'ın Connection propertysinin ConnectionString'ini ve Command propertysinin CommandText özelliğini dolduruyorum. Bu işlemlerden sonra tek yapmam gereken ExtendedDataTable nesnemde bulunan Fill metodunu çalıştırmamdır. DataGrid'e bind ettiğim bu datada değişiklik yaptıktan sonra değişiklikleri veritabanına kaydetmek için ise ExtendedDataTable'ın Update metodunu çağırmam yeterli olacaktır.

         Bu makalede sıklıkla kullandığımız DataTable nesnesini nasıl kişiselleştirebileceğimizi inceledik. Örnekler ve kodlar makaleyi tamamlayıcı özellik teşkil ettiğinden hata yakalama metodları yazılmamıştır. Umarım faydalı olmuştur.

         oztamer@hotmail.com
         tamer.oz@yazgelistir.com
         oztamer@hotmail.com
Ornek Kodlar