Makale Özeti

Bu makalede çoklu bir combobox kontrolünü nasıl yazabileceğimizi inceleyeceğiz. Çoklu combobox kontrlünden kastım alt alta iki tane ComboBox olduğunu ve bunların ilişkili olduğunu düşünün Örnek Olarak İl-İlçe Veya Bir Programın Menüsünün tutulduğu database tablolarını gösterebiliriz.

Makale

         Merhabalar,

         Bu makalede çoklu bir combobox kontrolünü nasıl yazabileceğimizi inceleyeceğiz. Çoklu combobox kontrlünden kastım alt alta iki tane ComboBox olduğunu ve bunların ilişkili olduğunu düşünün Örnek Olarak İl-İlçe Veya Bir Programın Menüsünün tutulduğu database tablolarını gösterebiliriz. Örnekleri çoğaltmak mümkündür. İlk önce Örneğimizde kullanacağımız Veritabanı Tablolarımızı Yaratalım.

   

         Yapacağımız ComboBox kontrolü bu iki tabloya benzer kendi içinde ilişkili tablolara ait DataSource kaynaklarıyla kullanılabilecektir. İsterseniz artık kodumuzu yazmaya başlayalım.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Data;

namespace CokluComboBoxTest
{
      public class CokluComboBox:Control
      {
            ComboBox cb1;
            ComboBox cb2;

         Gördüğünüz gibi kontrlümüzün temel yapısı hiçbir kontrolle benzerlik göstemediği için kontrlümüzü Control classından türetiyoruz. Bundan hemen sonra ise class level olarak iki tane ComboBox tanımlıyoruz. Bu ComboBox'lar bizim ilişkisel verileri tutacağımız kontroller olacaktır.

            public CokluComboBox()
            {
                  cb1 = new ComboBox();
                  cb1.SelectedIndexChanged += new EventHandler(cb1_SelectedIndexChanged);
                  cb1.Name = "cb1";
                  this.Dock = DockStyle.Top;
                  this.Controls.Add(cb1);
 
                  cb2 = new ComboBox();
                  cb2.SelectedIndexChanged += new EventHandler(cb2_SelectedIndexChanged);
                  cb2.Name = "cb2";
                  cb2.Top = cb1.Top + cb1.Height + 1;
                  this.Dock = DockStyle.Fill;
                  this.Controls.Add(cb2);
            }

          Kontrolümüzün yapıcı metodunda ise daha önceden referansını oluşturduğumuz iki adet ComboBox kontrollerine ComboBox nesnelerini oluşturuyoruz ve ilk ComboBox'ımızın seçili nesnesi değiştiğinde ikinci combobox'ımız değişeceğinden ilk combobox'ımız için SelectedIndexChanged event'ını tanımlıyoruz, bu eventin içinde diğer combobox'ı nasıl doldurduğumuzu bu makalenin sonunda göreceğiz.. Yine kontrolümüze yazacağımız eventları kullanabilmek için iki combobox içinde SelectedIndexChanged eventını yazmış olmamız gerekmektedir.Birinci comobox'ımızın Control container'ımızda en üstte bulunması için Dock özelliğini Top veriyoruz ve mevcut kontrlümüzün Controls collection'una ekliyoruz. Diğer Combobox içinde aaynı işlemleri yapıyoruz ancak buradaki tek farklılık bu combobox için Dock özelliğinin Fill olarak ayarlanmış olmasıdır.
 

            private string mDisplayMember="";
            public string DisplayMember
            {
                  get
                  {
                        return mDisplayMember;
                  }
                  set
                  {
                        mDisplayMember = value;
                        cb1.DisplayMember = value;
                        cb2.DisplayMember = value;
                  }
            }

          Artık property'lerimizi yazmaya başlayabiliriz. mDisplayMember değişkeninin encapsule edildiği property olan DisplayMember propertysinin Get metodunda bu değişkenin değerini döndürürken Set metodunda bu değişkene değer atamanın yanı sıra kontrolümüz içinde bulunan ComboBox'ların DisplayMember propertylerini set ediyoruz.
 

            private string mValueMember="";
            public string ValueMember
            {
                  get
                  {
                        return mValueMember;
                  }
                  set
                  {
                        mValueMember = value;
                        cb1.ValueMember = value;
                        cb2.ValueMember = value;
                  }
            }

          mValueMember değişkeninin encapsule edildiği property olan ValueMember propertysi yapı olarak DisplayMember propertysiyle aynıdır ve kontrolümüz içinde bulunan ComboBox'ların ValueMember değerlerini set etmek temel görevidir.
 

            private string mMasterChildRelationField;
            public string MasterChildRelationField
            {
                  get
                  {
                        return mMasterChildRelationField;
                  }
                  set
                  {
                        mMasterChildRelationField = value;
                  }
            }

          MasterChildRelationField property'sinde ise DataSource olarak vereceğimiz kendi içinde ilişkili tablonun hangi alanının ilişkili verileri taşıdığı kriteridir. Bu propertyde aynen DisplayMember veya ValueMember propertyleri gibi DataSource'da bulunan kolon isimini taşımalıdır.
 

            private DataTable mDataSource;
            public DataTable DataSource
            {
                  get
                  {
                        return mDataSource;
                  }
                  set
                  {
                        mDataSource = value;
                        if (value != null)
                        {
                              DataView insDataView = new DataView(value);
                              insDataView.RowFilter = mMasterChildRelationField + " IS NULL";                                                           
                              cb1.DataSource = insDataView; 
                        }
                  }
            }

          DataSource propertymiz kontrolümüze vereceğimiz DataTable'ı taşımakta ve mDataSource değişkenini encapsule etmektedir. Bu property'mizin Get metodunda mDataSource değişkeninin değerini döndürürken Set metodunda bu değişkene değer atamanın yanı sıra ilk ComboBox doldurulmaktadır. ComboBox'ı dolduruken verilen DataSource'dan bir DataView yaratıyoruz ve ana kayıtlar gösterleceği için DataView'in RowFilter propertysine ana kayıdı olmayanları filter edebilmesi için ve ilişkisel verileri tuttuğumuz kolon adımız mMasterChildRelationField değişkeninde saklandığı için mMasterChildRelationField + " IS NULL" gibi bir sınama kriteri veriyoruz ve daha sonra bu DataView'i ComboBox'ımıza DataSource olarak atıyoruz. İkinci ComboBox'ın doldurulması ilkindeki seçili item'in değişeceği zaman gerçekleşeceği için ikinci ComboBox'a DataSource atama işlemini daha sonra inceleyeceğiz.
 

            public object cb1SelectedValue
            {
                  get{return cb1.SelectedValue;}
                  set{cb1.SelectedValue=value;}
            }
            public object cb2SelectedValue
            {
                  get{return cb2.SelectedValue;}
                  set{cb2.SelectedValue = value;}
            }

          Şimdi ise her bir programlarken ihtiyacımız olabilecek bazı propertyleri her bir ComboBox için yazalım. Bu propertyler genelde ComboBox'ın propertylerine erişmemizi sağlayan propertyler olacaktır. İlk propertymiz cb1SelectedValue ismindedir ve cb1 ismiyle tanımladığımız ComboBox'ımzıın SelectedValue propertysine değer atamakta veya değerini okumaktadır. Bu kod diğer ComboBox için aynı olduğundan burada açıklanmamıştır..
 

            public int cb1SelectedIndex
            {
                  get { return cb1.SelectedIndex;}
                  set { cb1.SelectedIndex = value; }
            }
            public int cb2SelectedIndex
            {
                  get { return cb2.SelectedIndex; }
                  set { cb2.SelectedIndex = value; }
            }

          Yine ComboBoxlarımızın SelectedIndex propertylerini okuyabilmek veya yazailmek için kullanacağımız propertylerimizi cb1SelectedIndex ve cb2SelectedIndex isimleriyle ait oldukları ComboBox'ların propertylerini çağıracakları şekilde tanımlıyoruz.

            public object cb1SelectedItem
            {
                  get { return cb1.SelectedItem; }
                  set { cb1.SelectedItem = value; }
            }
            public object cb2SelectedItem
            {
                  get { return cb2.SelectedItem; }
                  set { cb2.SelectedItem = value; }
            }

          Biraz önce bahsedilen mantıkla SelectedItem propertysini kapsayan property her bir combobox için yukarıdaki gibi yazılmıştır.
 

            public string cb1SelectedText
            {
                  get { return cb1.SelectedText; }
                  set { cb1.SelectedText = value; }
            }
            public string cb2SelectedText
            {
                  get { return cb2.SelectedText; }
                  set { cb2.SelectedText = value; }
            }

          Bu sefer ise her bir ComboBox için SelectedText propertysini kullanmamıza olanak veren propertyleri mantık aynı olacak şekilde yazıyoruz.
 

            private event EventHandler cb1SelectedIndexChanged;
            private event EventHandler cb2SelectedIndexChanged;

          Şimdi ise kontrolümüzün event'larını yazmaya başlayabiliriz. Burada kontrolümüzde bulunan her iki ComboBox'ında seçili nesnelerinin değiştiğinde bir event fırlatması ve bunu bizim yakalayıp kullanmamız gerekliliği ortadadır. Bunun için isimleri cb1SelectedIndexChanged ve cb2SelectedIndexChanged olan iki adet prıvate event tanımlıyoruz.
 

            public event EventHandler Cb1SelectedIndexChanged
            {
                  add { cb1SelectedIndexChanged += value; }
                  remove { cb1SelectedIndexChanged -= value; }
            }
            public event EventHandler Cb2SelectedIndexChanged
            {
                  add { cb2SelectedIndexChanged += value; }
                  remove { cb2SelectedIndexChanged -= value; }
            }

          Biraz önce tanımladığımız eventlara sonsuz sayıda handler atayabilmemiz için yeni tanımladığımız EventHandler'ın add ve remove metodlarını kullanıyoruz. Burda tanımlanan handlerlar publıc olduğundan bundan sonra eventlarımızı Cb1SelectedIndexChanged ve Cb2SelectedIndexChanged şeklinde çağıracağız.
 

            protected void onCb1SelectedIndexChanged(EventArgs e)
            {
                  if (cb1SelectedIndexChanged != null)
                        cb1SelectedIndexChanged(this, e);
            }    
            protected void onCb2SelectedIndexChanged(EventArgs e)
            {
                  if (cb2SelectedIndexChanged != null)
                        cb2SelectedIndexChanged(this, e);
            }

          Tanımladığımız event'larımızda mevcut br handler varsa yani değerleri null değilse bu metodlardan eventlarımızı fırlatabiliyoruz. Bu metodları kodumuzun içinde eventlarımızı fırlatmak istediğimiz noktalarda kullanacağız.
 

            void cb1_SelectedIndexChanged(object sender, EventArgs e)
            {
                  DataView insDataView = new DataView(mDataSource);
                  insDataView.RowFilter = mMasterChildRelationField + "=" + ((ComboBox)this.Controls.Find("cb1", true)[0]).SelectedValue.ToString();
                  ((ComboBox)this.Controls.Find("cb2", true)[0]).DataSource = insDataView;
                  onCb1SelectedIndexChanged(e);
            }
            void cb2_SelectedIndexChanged(object sender, EventArgs e)
            {
                  onCb2SelectedIndexChanged(e);
            }    
      }
}

          cb1_SelectedIndexChanged metodu yapıcı metodumuzda tanımladığımız gibi birinci ComboBox'ımızın seçili item'ı değiştiğinde devreye girecektir. Dolayısıyla 2. ComboBox'ı doldurmamız gereken yer burasıdır. Bu işlem için ise yine DataSource'ımızı kapsayacak şekilde bir DataView oluşturuyoruz bu sefer arama kriterimiz ilişkisel verilerin tutulduğu kolonun isminin değerini taşıyan değişken olan mMasterChildRelationField'ı kullanarak ilişkisel kolonumuzdaki verilerin 1. ComboBox'ın seçilideğerine eşit olduğu değerleri getirecek şekilde yazılmıştır. Bundan sonra ise yazmış olduğumuz kontrolde tanımladığımız Eventımızı fırlatıyoruz. Diğer ComboBox'ın seçili nesnesi değiştiği zaman yapacağı spesifik bir görev olmadığından Sadece kontrolümüzde tanımladığımız event'ı fırlatmamı yeterli olacaktır.

         Şimdi ise yazmış olduğumuz bu kontrolün bir uygulamada nasıl kullanılacağını inceleyelim bunun için Bir tane Windows Application yaratıp Formun üzerine kontrolümüzü ToolBox'tan sürükleyip bırakıyoruz. Bundan sonrası kontrolümüze veriyi doldurmak ve ilgili propertyleri atamaktır. Bunun ile ilgili bir örnek aşağıda verilmiştir.
 

            private void Form1_Load(object sender, EventArgs e)
            {
                  DbProviderFactory ProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
                  DbConnection Connection = ProviderFactory.CreateConnection();
                  Connection.ConnectionString = "data source=.;initial catalog=Ornek;integrated security=SSPI";
                  DbCommand cmd = ProviderFactory.CreateCommand();
                  cmd.CommandText = "Select * FROM MenuItems";
                  cmd.Connection = Connection;
                  DbDataAdapter da = ProviderFactory.CreateDataAdapter();
                  da.SelectCommand = cmd;
                  DataTable dt = new DataTable();
                  da.Fill(dt);
                  
                  cokluComboBox1.DisplayMember = "Text";
                  cokluComboBox1.MasterChildRelationField = "ParentMenuItemId";
                  cokluComboBox1.ValueMember = "MenuItemId";
                  cokluComboBox1.DataSource = dt; 
            }

          Formumuzun load olayında DataBAseden veriyi çekip DataTable nesnesine aktardıktan sonra kontrolümüzün DiplayMember property'sine kontrolümüz içinde bulunan ComboBox'larda gözükmesi istediğimiz verileri taşıyan kolon adını, ValueMember property'sine ise ComboBox'larda arka planda değerlerinin tutulmasını istediğimiz kolon adını, MasterChildRelationField property'sine ise verilerin ilişkilendirildiği kolonumuzun adını atadıktan sonra DataSource property'sine oluşturduğumuz ve verileri taşıyan DataTable nesnemizi atamak suretiyle kontrolümüzü çalışır hale getiriyoruz.



         Bu makalede ve bulunan uygulamada hep beraber kendimize ait bir kontrol yazark işlerimizi nasıl kolaylaştırabileceğimizi inceledik ve bilgi sahibi olduk. Sizlerde bu şekilde kontrollerinizi yazarak işlerinizi kolaylaştırıp bazı noktalarda kod yükünden kurtulabilirsiniz. Bu uygulamada hata yakalama fonksiyonlar ve detaylı testler uygulama öğrenme ve makaleyi tamamlayıcı amaçlı olduğundan yazılmamıştır.

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