Cuma, Aralık 02, 2005

Derinlemesine TField kullanımı

Veri ile çalışan Delphi uygulamalarında sıkça kullandığımız TBDEDataset, TADODataset, TIBDataset gibi bileşenlerin tümü TDataset sınıfından türetilmiştir. Gerçekte TDataset sınıfı satırlar ve sütunlardan oluşan her tür veriyle çalışabilecek özelliklerle donatılmıştır. Programlarımız çeşitli veritabanı sistemleriyle çalıştığı için Delphi içinde TDataset sınıfından türetilmiş birçok özel bileşen bulunmaktadır. TDataset sınıfının özelliklerini bildiğimiz zaman diğer TDataset türevlerini de aynı şekilde kullanabiliriz. Mesela SQL Server kullanacaksak TADODataset bileşenini, Interbase kullanacaksak TIBDataset bileşenini kullanırız. Delphi ile gelen TDataset bileşenleri haricinde üçüncü parti birçok özel TDataset bileşeni de internette bulunabilmektedir. Örneğin sadece hafızada bir tablo oluşturup kullanabileceğimiz TMemoryDataset bileşeni veya Outlook programına bağlanarak Outlook adres defterini bir tablo gibi kullanmamızı sağlayan TOutlookDataset bileşeni gibi özel TDataset bileşenleri de vardır. Çok özel durumlar için biz de TDataset sınıfını özelleştirerek özel TDataset bileşenleri yazabiliriz ancak bu konu ayrı bir yazı konusu olabilecek kadar geniş bir konudur.

Bir veritabanı sistemine bağlı çalışan TDataset bileşeni veritabanına gerekli bağlantıyı kurabilmek için o veritabanı için özel yazılmış TConnection bileşeninden yardım alır. Mesela bir TADODataset bileşeni kullanıyorsak veritabanına bağlanabilmek için ADO için yazılmış TADOConnection, eğer interbase’e bağlanacaksak bu sefer TIBConnection bileşeni kullanırız. Veritabanlarına bağlı çalışan TDataset’ler tutmasını istediğimiz veriyi veritabanından çekme işini de yaparlar. Bu veri bir tablo olabileceği gibi birkaç tablonun birleşiminden oluşmuş da olabilir. TTable tek bir veritabanı tablosuyla çalışmak üzere tasarlanmış özel bir TDataset türevidir. Bu bileşen gerekli tabloyu veritabanından çekmek için sadece o tablonun adına ihtiyaç duyar. Bu tablo adını bileşenin TableName özelliği ile tanımlarız. TQuery ise SQL cümlesi kullanarak bir veya birçok tablonun birleştirilmesinden oluşan bir verikümesi üzerinde çalışan bir bileşendir. Bu nedenle TQuery TableName özelliği içermez. Onun yerine SQL özelliği içerir. Kısacası TDataset bileşenleri çalışacakları verikümesini veritabanından nasıl çekeceği ile ilgili bilgiye ihtiyaç duyar. Bu bilgiyi o bileşene özgü özellikler ile tanımlayabiliriz.

İlgili tanımları yaptıktan sonra TDataset bileşenimiz ihtiyacımız olan verikümesini veritabanından nasıl çekeceğini ve yaptığımız değişiklikleri nasıl geri göndereceğini öğrenir. Bu tanımlamaları nasıl yapabileceğimize dair ADO ile ilgili bir örneği bir önceki yazımda anlatmıştım. Bu işlemler genellikle her TDataset türü için ilgili veritabanının özelliklerine göre farklı şekillerde yapılır.

Bir TDataset’i Active özelliğini True yaparak veya kod ile Open metodunu çağırarak açarız. TDataset direk veritabanındaki tablolar üzerinden çalışmaz. Onun yerine tablonun bir eşini bilgisayarın hafızasında oluşturur. Bu hafızadaki tablo üzerinde yaptığımız değişiklikler daha sonradan veritabanına yansıtılır. Bu özellikle büyük boyutlu tabloları açarken dikkat etmemiz gereken bir konudur. Tüm tablo olduğu gibi veritabanından bilgisayarın hafızasına çekileceği için yavaş çalışma veya bilgisayarın hafızasının tükenmesi gibi sorunlar ortaya çıkabilir. Genellikle kullanılan yöntem tablonun üzerinde çalışacağımız kadarını kullanmaktır. Örneğin bir müşterinin siparişlerini göstermek için tüm siparişleri çekmenin bir anlamı yoktur. Sadece ilgili müşterinin sipariş kayıtlarını çekmek yeterli olacaktır. Kimi veritabanı sistemleri tablonun tümünü çekmeden sadece gerektiği kadar bilginin çekilmesi özelliğini destekler. Mesela bir TDBGrid ile listelediğimiz kayıtlar listeyi aşağıya kaydırdıkça ekranda göründüğü kadar satır veritabanından çekilecektir. Ancak bu özellik kullanıcı sayısı çok olan sistemlerde veritabanı sunucusunun yükünü normalden daha fazla arttıracağı için performans düşüklüğüne neden olabilir. Özel durumlar haricinde bu tür özellikler kullanılırken dikkat edilmelidir.

TDataset ile ilgili anlaşılması gereken önemli konulardan biri dataset’in aktif kayıt özelliğidir. Bir TDataset’e ilk veri çektiğimizde gelen listenin ilk kaydı dataset’ in aktif kaydı olur. TDataset’in First, Next, Prior, Last metodlarını kullanarak aktif kaydı değiştirebiliriz. TDataset’ ten veri okuduğumuzda aktif kayıtla ilgili bilgileri okuruz. TDataset’ in Fields koleksiyonu tablonun alanlarıyla ilgili TField nesnelerini içerir. Eğer tablomuzda 3 sütun varsa Fields koleksiyonunda her bir sütuna karşılık gelen 3 adet TField nesnesi bulunmaktadır. Herhangi bir koleksiyonda olduğu gibi Fields[0] birinci TField nesnesine, Fields[1] ikinci TField nesnesine ulaşmamızı sağlar. TField nesnesi TDataset’ in aktif kaydındaki herhangi bir değeri okuyabileceğimiz bir Value özelliği içerir. Örneğin:

textbox1.text := MyDataset.Fields[0].Value;

dediğimizde datasetin aktif kaydının birinci sütunundaki değeri textbox’a aktarmış oluruz. Veritabanında tabloyu oluştururken birinci sütuna herhangi bir veritipini tanımlayabiliriz. Yani birinci sütun bir string, integer, double, vb.. tipinde değer içerebilir. Bu nedenle Value özelliği herhangi bir tipin yerine geçen Variant tipindedir. Variant tipler herhangi bir tip barındırabildiği için biraz karmaşık yapıdadırlar ve yavaş çalışırlar. Eğer tablonun birinci sütununun hangi tipte veri içerdiğini biliyorsak TField’in örneğin string için AsString, integer için AsInteger özelliklerini kullanabiliriz. TField hemen hemen her tür veri için AsXXX özelliği içerir. Yani bir değer okurken sütunun içerdiği veri tipine karşılık gelen AsXXX özelliğini kullanmak faydalı olacaktır. Yukarıdaki örneğimizi:

textbox1.Text := MyDataset.Fields[0].AsString;
dateTimePicker.Date := MyDataset.Fields[1].AsDateTime;

olarak yazabiliriz. Dikkat edilirse dateTimePicker bileşeninin Date özelliği TDateTime tipinde olduğu için TField’in AsDateTime metodu ile veriyi TDateTime tipinde okuduk. Eğer ilgili tarihsel değeri bir TextBox ile göstermek istersek TextBox’ın tipi string olduğu için AsDateTime yerine AsString kullanarak tarihsel değeri otomatik olarak TextBox ile kullanabileceğimiz string tipine çevirmiş oluruz.

İlgili sütuna referans eden TField nesnesine ulaşmak için kullanabileceğimiz ikinci yöntem TDataset’in FieldByName fonksiyonudur. FieldByName fonksiyonu ile TField’in FieldName özelliğine göre arama yapabiliriz. TField’in FieldName özelliği tablomuzu oluştururken ilgili sütuna verdiğimiz addır. Yani “Adres” adında bir sütun tanımladıysak dataset açılırken otomatik olarak “Adres” sütununa karşılık gelen bir TField nesnesi oluşturulur ve dataset’in fields koleksiyonuna eklenir. Bu TField nesnesinin FieldName özelliği “Adres” olacaktır. Bu TField nesnesini:

Var MyAdresField: TField;
MyAdresField := MyDataset.FieldByName(“Adres”);
textBox1.Text := MyAdresField.AsString;


veya

textBox1.Text := MyDataset.FieldByName(“Adres”).AsString;

gibi kullanabiliriz.

Normalde TDataset’in Fields koleksiyonu için endişelenmeyiz çünkü bir veritabanına bağlı dataseti açtığımızda delphi otomatik olarak tablomuzdaki alanlara karşılık gelen TField nesnelerini oluşturacaktır. Bildiğimiz gibi TDataset türevi bileşenlerin üzerine çift tıkladığımızda Fields koleksiyonuna ekleme-çıkarma yapabileceğimiz bir liste ekrana gelir. Bu listeye eğer ekleme yapmışsak bu bilgiler kullanılır ve TField listesi tekrardan oluşturulmaz. Tıpkı Ado ile çalışırken TDataset’in TAdoDataset türevi ile çalıştığımız gibi TDateTime tipindeki alanlar için TDateTimeField, string tipindeki alanlar için TStringField gibi TField türevleri ile çalışırız. Hemen her veritipine karşılık gelen bir TField türevi vardır ve o alanın türüne göre bazı ek özellikler içerirler. Mesela TDateTimeField nesnesi tarihin gösterilme şeklini belirleyebileceğimiz bir DisplayFormat özelliği içerir veya bir TBooleanField nesnesi boolean değeri gösterirken True, False değerleri yerine Evet, Hayır veya başka herhangi bir bilgi gösterebileceğimiz bir DisplayValues özelliğine sahiptir. Ancak TDataset’in Fields koleksiyonu üzerinden bu alanlara erişirsek bu alanlar bize TField tipinde görüneceği için TField türevine ait özellikleri göremeyiz. Eğer TDataset listesine alanları elle eklersek o zaman alanın tipine karşılık gelen TField türevi yaratılacağı için bu özelliklere erişebilme şansımız olur. Field listesine “Add All Fields” dediğimizde gelen alanlara tıkladığımızda object inspectordan bakarsak oluşan TField türünün ne olduğunu görebiliriz. Add all fields dediğimizde normalde program çalışırken otomatik olarak oluşacak olan alanları baştan oluşturmuş olacağız. Eğer kullanmayacağımız alanları listeden çıkarırsak bu alanlar bir daha oluşmayacağı için hafızadan yer kazanmış oluruz.

TDataset’in fields listesinden ulaşabileceğimiz alanların veri türüne özel özelliklerine ulaşabilmek için bir tip zorlaması yapmamız gerekecektir. Eğer ilk sütunumuz Date türündeyse ve biz kod içerisinde bu alanın DisplayFormat özelliğini değiştirmek istersek:

MyDataset.FieldByName(‘Tarih’).DisplayFormat = ‘dd.mm.yy’;

yazamayız çünkü FieldByName fonksiyonu Tarih alanını TDateTimeField tipinde değil bir üst tip olan TField tipinde döndürür. Halbuki DisplayFormat TDateTimeField türünün bir özellğidir. O zaman yazmamız gereken:

(MyDataset.FieldByName(‘Tarih’) as TDateTimeField).DisplayFormat = ‘dd.mm.yy’;

olacaktır. Bu sayede elle alan eklemesekte türe özel özelliklere erişebilmemiz mümkün olur. Delphi yardım dosyaları TField türevleri ve bunların spesifik özellikleri ile ilgili geniş bilgi içermektedir.

TField nesnesinin FieldKind özelliği bu nesnenin davranış şekli ile ilgili önemli bir özelliktir. Normalde Fields listesindeki her TField nesnesinin tablodaki bir alana karşılık geldiğinden bahsetmiştim. Bu alanların tümünün FieldKind özelliği fkData olarak gelir. Tablodaki bir alana karşılık gelmeyen TField nesneleri de kullanabiliriz. Bunlar lookup ve calculated alanlardır ve tablodaki bir alana referans etmek yerine ek bilgiler vermek için kullanılabilir. Bir lookup TField alanını içerdiği değeri başka bir datasetteki değerle ilişkilendiren değerler için kullanırız. Örneğin müşteriler ve şehirler gibi iki tablomuz olsun. Müşteri tablomuzda şehir alanı için sayısal bir değer kullanırız ve şehirler tablosuna referans ederiz.

Şehir No Şehir Adı
1Ankara
2İstanbul
3İzmir

Müşteri NoMüşteri AdıMüşteri Adresi Şehir No
1Emre Kızılay 1
2HasanTaksim2
3GülayUlus2
4GülerKonak3
5GülsümUlus1


Yukarıda görüldüğü gibi müşteriler tablosunda şehir değeri için şehirler tablosundaki ŞehirNo değerini kullandık. Normalde Ankara değeri yerine 1 yazdık. Bunu neden yaptık? Normalde sayı tipi 8 harflik bir alan harcarlar. Halbuki şehir adları 8 harften fazla olabileceği için biz şehir adı alanı için 15 harf gibi bir alan ayırırız. Bu şekilde referans alanı kullandığımız için müşteriler tablosunun Şehir adı için kullanacağımız yer daha az olacaktır. Referans alanının bir faydası da birçok alandan oluşan bilgilere bir sayı ile erişebilmemizdir. Bir sipariş tablomuz olduğunu düşünelim

Şipariş No Müşteri No Stok Adı Miktar
11Etek1
21Ceket2
32Etek1
45Yelek3
54Yelek1

Bu tablodan görüleceği üzere siparişin hangi müşteriye ait olduğunu müşteri no alanını kullanarak bulabiliyoruz. Müşteri No değeri Müşteriler tablosundaki Müşteri No alanına referans eder.

TBLMusteri ve TBLSehirler adında iki tane TDataset’imiz olsun. Bu bileşenlerin üzerine çift tıklayarak gelen listeden “Add All Fields” diyerek listelere tüm alanları ekleyelim. TBLMusteri bileşeninin alan listesine “New Field” diyerek yeni bir alan ekleyelim ve Name kısmına uygun bir değer yazalım. Type kısmından string seçelim ve FieldType seçeneklerinden Lookup’ı işaretleyelim. Lookup Definition kısmı etkinleşir. Dataset listesinden TBLSehirler’i seçelim. Key Fields TBLMusteri tablosundaki SehirNo alanı, Lookup Keys ise TBLSehirler tablosundaki SehirNo alanı olacak. Result Field ise Şehir isimlerini görmek istediğimiz için TBLSehirler tablosundaki SehirAdi alanı olacaktır. Bu tanımlamayı yapıp ilgili tabloyu bir DBGrid’e bağlarsak eklediğimiz lookup alanında Şehir numarası yerine Şehir adını görebiliriz. Aslında bu lookup alanı eklerken Delphi nin yardımcı alan ekleme ekranını kullandık. Eğer alan listesine geri döner ve bu yeni eklediğimiz alana tıklarsak Object Inspector da kullandığımız değerlerin tümünü görebiliriz. Yine buradan görebildiğimiz gibi FieldKind özelliği fkLookup olmuştur.

FieldKind özelliğinin bahsetmek istediğim üçüncü değeri fkCalculated’ dir. Calculated alanlar anlık hesaplar yapabileceğimiz ve bunları gösterebileceğimiz alanlardır. Calculated alanları lookup alan gibi ekleriz ancak bu sefer Calculated seçeneğini seçeriz. Örneğin bir sipariş tablomuz olduğunu ve tablomuzda sipariş edilen ürünün fiyatının ve sipariş edilen miktarın yazılı olduğunu düşünelim. Normalde bu bilgileri gösterirken miktar ve fiyat çarpımını da göstermemiz gerekir. Eklediğimiz calculated alana ToplamFiyat adını verdiğimizi düşünürsek datasetin OnCalcFields olayına (event) gideriz ve içine

MyDataset.FieldByName(‘ToplamFiyat’).Value :=
MyDataset.FieldByName(‘Fiyat’).Value * MyDataset.FieldByName(‘Miktar’).Value;


yazarız. Bu sayede ToplamFiyat alanı hesaplanır ve kullanıcıya gösterilir. Normal alanlardaki gibi DisplayFormat özelliklerini kullanarak hesaplanan alanların da görünüm şekillerini değiştirmek mümkündür. Hesaplanan alanlar tablodaki bir sütuna referans etmezler ama diğer sütunları kullanarak hesaplar yapmamızı ve bunları kullanıcıya göstermemizi sağlarlar. Tablodaki bir sütuna referans etme şartı sadece FieldKind özelliği fkData olan alanlar için geçerlidir.

TField nesnesinin OnGetText ve OnSetText adında iki önemli olayı (event) vardır. Bu olaylar kullanıcıya görünen değerlerde değişiklikler yapabilmemiz için çok faydalıdırlar. Tıpkı biribirlerine referans eden tablolarda olduğu gibi kullanıcıya string olarak görünen değerleri tablomuzda sayısal değer olarak tuttuğumuzu düşünelim. Mesela müşteri tablomuzda Müşteri Tipi için bir alanımız olduğunu ve Müşteri Tipi “Normal Müşteri” olanlar için 1 değerini, “Özel Müşteri” olanlar için 2 değerini tuttuğumuzu düşünelim. Yani özel müşteriler için tablomuzda 2 değerini tutacağız ancak bunu kullanıcıya “Özel Müşteri” olarak göstereceğiz. Bu basit bir eşleme olduğu için bu bilgileri ayrı bir tabloda tutmak fazla masraflı olacaktır. Bu isteğimizi basitçe gerçekleştirmek için OnGetText olayına gideriz. Olayın parametre listesine bakarsak “var Text:String“ şeklinde bir parametre olduğunu görebiliriz. Bu Text değişkenine atadığımız değerin kullanıcıya görünecek değer olduğunu belirtir. Sender değişkeni de TField tipindedir ve ilgili alana işaret eder. Örneğin:

if sender.Value = 1 then Text := “Normal Müşteri”;
if sender.Value = 2 then Text := “Özel Müşteri”;


yazarak tablodan okuduğumuz değerleri kullanıcıya farklı göstermek mümkün olacaktır.

Aynı şekilde OnSetText olayını kullanarak kullanıcının alana girdiği değerleri veritabanına farklı değerler olarak kaydedebilmek mümkündür.

Cuma, Kasım 18, 2005

Delphi ile Veritabanı Programlama Hakkında Önbilgi

Günümüz programlarında bilgiler veritabanı sistemleri üzerinde tutulurlar. Veritabanları tablolardan, tablolar ise satır ve sütunlardan oluşan yapılardır. Delphi içinde genellikle tablolar table, sütunlar field, satırlar ise record olarak adlandırılır. Bir delphi programıyla veritabanı arasında bilgi alışverişi yapabilmek için öncelikle veritabanında bir tablo açılmalıdır. Sonra o tabloda kullanmak istenilen sütunları tanımlamalıyız. Bunları veritabanı ile gelen programlar ile yapabiliriz. SQL Server, Oracle, Access, MySQL, Interbase yaygın kullanılan veritabanı sistemleridir.

Delphi projelerinde veritabanı sunucusuna bağlantıyı TConnection bileşeni sağlar. Veritabanından çektiğimiz bilgileri ve bu bilgiler üzerinde yaptığımız değişiklikleride TDataset bileşeni (component) tutar. Bir TDataset bileşeni veritabanına bağlanabilmek için TConnection bileşenine ihtiyaç duyar. Birçok durumda bir program için bir TConnection bileşeni yeterlidir. Projemize ekleyeceğimiz her dataset aynı connection bileşenini kullanabilir. Teoride delphi tüm verikaynakları ile bu bileşenler üzerinden çalışabilir ancak verikaynağının türüne göre özel türleri vardır. Mesela TADODataset ve TADOConnection Ado desteği olan veritabanlarına bağlanmak için kullanılan özel veri bileşenleridir. Veritabanı üzerinde bulunan bilgiler TConnection bileşeni ile çekilerek TDataset bileşenine aktarılır. TDataset üzerinde yapılan değişiklikler de aynı yoldan tekrar veritabanına yazılır.

Access kullanıyorsanız programı çalıştırarak yeni bir tablo açın adına MUSTERILER deyin ve tabloya MUSTERIADI ve MUSTERIADRESI olarak iki tane sütun (field) tanımlayın.

İlgili tabloyu tanımladıktan sonra delphi projemize bir TAdoConnection ekleyerek üzerine çift tıklıyoruz ve gelen ekrandaki bilgileri doldurarak TAdoConnection bileşenimizin veritabanına nasıl bağlanacağını ilgili parametrelerden tanımlıyoruz. Use connection string'i seçip build diyoruz ve gelen ekrandan provider sayfasına tıklayıp Microsoft JET 4.0 OLEDB Provider'i seçiyoruz. Connection sayfasına geçip "Select or Enter a Database Name" (1. seçenek) editinin yanındaki ... düğmesine tıklayarak mdb dosyamızı buluyoruz ve OK diyerek işimizi bitiriyoruz.

Şimdi Delphi projemize geçiyoruz ve yeni bir proje başlatıyoruz. Ana Formun üzerine veritabanında açtığımız tabloya karşılık gelecek bir TAdoDataset bileşeni ekliyoruz. AdoDatasetin connection özelliğini AdoConnection1 olarak belirliyoruz. Bu AdoConnection1 biraz önce eklediğimiz TConnection bileşenidir. Sonra datasetimizin CommandText özelliğine çift tıklayarak önce Tables listesine gelen tablolardan kullanmak istediğimiz tablo adına çift tıklıyoruz. İkinci adımda datasetimizde kullanmak istediğimiz sütun (field) isimlerine çift tıklıyoruz. Eğer tüm sütunları kullanmak istiyorsak sadece * 'a çift tıklıyoruz. Eğer yanlış birşey yaparsak Cancel diyerek tekrar yapabiliriz. Aslında bunu yaparken basit bir SQL cümlesi yazmış olduk. Tablomuzun adının MUSTERILER olduğunu düşünürsek sağ tarafta

SELECT * FROM MUSTERILER

gibi bir cümle oluşması lazım. Bu oluşan cümle veritabanları ile programlar arasındaki konuşma dili olan SQL'dir. Buradaki cümle "MUSTERILER Tablosundaki tüm bilgileri getir" anlamına gelir. İstersek bu cümleyi direk CommandText özelliğine yazarak da aynı sonucu elde edebiliriz. OK diyerek pencereyi kapatıyouz ve TAdoDataset'in Active özelliğini True yaparak tabloyu aktif hale getiriyoruz. Eğer Active özelliği hiç hata vermeden true oluyorsa programımız veritabanına başarıyla bağlanmış ve ilgili tabloyu kullanabilir hale gelmiş demektir. Genellikle gerçek programlarımızda datasetleri active konumunda bırakmak iyi bir fikir değildir. Bilgisayar hafızasını ve veritabanın kaynaklarını verimli kullanabilmek için datasetleri ihtiyacımız olduğunda kodla AdoDataset1.Open diyerek dataseti Active konumuna getirmek ve işimiz bittiğinde de AdoDataset1.Close diyerek kapatmak gerekir. Şimdilik deneme yaptığımız için Active özelliğini true olarak bırakalım.

Şu ana kadar veritabanından bilgi çekmekle ilgili tanımlamaları yapmış olduk. Şimdi sıra bunu kullanıcıya göstermeye geldi. Veritabanlarına bağlı çalışan programlar için TDataset bileşenleri ile çalışabilen birçok kontrol Delphi içinde standart olarak gelir. Edit, ListBox, ListView gibi forma direk yerleşebilen elemanlara kontrol diyoruz. TEdit içine yazı yazabildiğimiz bir kontoldür ve bunun veritabanları ile otomatik olarak çalışabilen türevi TDBEdit'dir. Adı TDB ile başlayan kontrollerin birçoğu veritabanından bilgi okuyabilir. Kimi kontroller tek bir tablo satırı (record, row, kayıt) ile çalışabildiği gibi kimi kontroller birden çok satırla (tablonun kendisi ile) çalışabilir. Bu kontroller ile dataset arasındaki bağlantıyı TDataSource bileşeni sağlar. Formumuza bir TDataSource bileşeni ekliyoruz ve bileşenin Dataset özelliğini AdoDataset1 olarak seçiyoruz. AdoDataset1 demin eklediğimiz TAdoDataset'tir. Artık formumuza veri kontrolleri eklemeye hazır hale geldik. Şimdi MUSTERILER tablomuzda MUSTERIADI ve MUSTERIADRESI olarak iki sütun eklemiş olduğumuzu düşünelim. Bu sütunları veritabanı programının kendisini kullanarak eklemiştik. (access kullanıyorsak access'in kendisi ile, SQL Server kullanıyorsak Enterprise Manager ile) Şimdi bu iki sütun için iki tane TDBEdit bileşeni ekleyelim ve DataSource özelliklerini Datasource1 olarak seçelim. Datasource1 demin eklediğimiz TDataSource'dir. Bu kontrollerin hangi sütunlarla çalışacağını ilgili kontrolün DataField özelliğini kullanarak belirliyoruz. Eğer bir hata yapmadıysak DataField özelliğine tıkladığımızda ilgili sütunların isimlerinin gelmesi lazım. Şu ana kadar bilgi girişi sağlayabileceğimiz kadar tanımlama yapmış olduk.

Programı kullanırken dataset'e kayıt ekle, sil, kaydet gibi komutlar vermemiz lazım. Bunlar normalde kodlar kullanarak yaparız ama şimdilik sonucu hemen görebilmemiz için forma bir
tane de TDBNavigator ekleyelim. Bunun da DataSource özelliğini datasource1 olarak seçelim. Bu bileşen dataset'e sonraki kayda git, ilk kayda git, son kayda git, kayıt ekle gibi komutlar vermemizi sağlar. Şimdi programı çalıştıralım. İlk olarak tablomuzda kayıt olmayacağı için
editlerimiz boş görünecek. Editlere birşeyler yazabilmek için önce tabloya boş bir satır eklememiz lazım. Bunu navigatordan + ya tıklayarak yapıyoruz. Sonra editlerin içine birşeyler yazalım ve tick işaretine tıklayalım. Tick işareti (check işareti) kaydet anlamı taşır. Bir kere daha + ya tıklayalım ve boş bir satır daha ekleyelim. Editler otomatik olarak boşalacak ve yeni kayıt girmek için hazır hale gelecek. Tekrar yeni bilgiler girelim ve tekrar tick işaretine (kaydet)tıklayalım. Şu anda iki satır bilgi girdik. İleri ve geri düğmelerine tıklayarak kayıtlar arasında dolanabiliriz. Bir kaydı değiştirmek için yukarı ok düğmesine tıklayarak satırı düzenleme moduna getiririz ve yaptığımız değişikliği kaydetmek için tekrar tick işaretine tıklarız. Değişikliği kaydetmek istemiyorsak çarpı düğmesine tıklayarak kaydı eski haline alırız. Şimdi birden çok kaydı düzenleyebilmek için kullanabileceğimiz TDBGrid kontrolünü deneyelim. Formdaki edit kontrollerini silerek bir TDBGrid ekleyelim ve gridin DataSource özelliğini DataSource1 olarak seçelim. Programı çalıştırdığınızda biraz önce girdiğimiz 2 kaydı görebilmeniz lazım. Navigator'u kullanarak bir üçüncü kaydı ekleyelim ve tick'e tıklayarak kaydedelim.

Bu işleri navigator kullanmadan kendimiz butonlar ekleyerek de yapabiliriz. Her düğmenin kod karşığı şunlar dır.

Navigatordaki dizilime göre:

AdoDataset1.First; İlk satıra git
AdoDataset1.Prior; Önceki satıra git
AdoDataset1.Next; Sonraki satıra git
AdoDataset1.Last; Son satıra git
AdoDataset1.Append; Satır ekle
AdoDataset1.Delete; Satır sil
AdoDataset1.Edit; Düzenleme moduna geç
AdaDataset1.Post; Kaydet
AdoDataset1.Cancel; İptal et
AdoDataset1.Refresh; Yenile (veritabanından tekrar oku)

Bir satırda değişiklik yapmak için önce dataseti edit moduna geçirmemiz gerekir. Yeni kayıt eklediğinizde dataset otomatik olarak edit moduna geçecektir. Kaydı silmek için edit moduna geçmeye gerek yoktur. Post ve Cancel komutlarından sonra edit modundan otomatik olarak çıkılır. Edit moduna geçirmek çok kullanıcılı sistemlerde ilgili satırı kilitleyecek ve başka bir kullanıcının kayıtta değişiklik yapmasını engelleyecektir. Refresh komutu ise yine çok kullanıcılı sistemlerde başka kullanıcıların yaptığı değişiklikleri görmemizi sağlayacaktır.

Şimdi forma iki tane TButton ekleyin ve butonlara çift tıklayarak birinin altına AdoDataset1.Next; öbürüne de AdoDataset1.Prior yazın. Denediğinizde navigatorla aynı şekilde çalıştığını göreceksiniz. Bu şekilde navigator olmadan da ilgili dataset komutlarını çalıştırabiliriz. Şimdi her komut için bir buton ekleyin ve butonların altına ilgili komutları yazın. Hangi butonun ne iş yaptığını anlamak içinde butonların caption özelliklerine gerekli tanımları yazın.

Gridde o an üzerinde bulunduğunuz kayıt dataset'in aktif satırıdır. Daha doğrusu aktif satır giridin değil datasetin bir özelliğidir. Datasette aktif satır neyse gridde o satırı işaretli olarak görürüz. Gridde başka bir satıra tıklarsak aslında dataset'in aktif satırını değiştirmiş
oluyoruz. Bu nedenle AdoDataset1.Delete komutu "Aktif satırı sil" anlamına gelecektir. Satır silindikten sonra aktif satır bir sonraki satır olacaktır. Eğer grid yerine edit kullandığımız bir önceki durumu düşünürsek datasette aktif satır neyse editlerde o satıra ait bilgiler
görünecektir. Datasetteki aktif satırı next, prior komutlarıyla değiştirebildiğimiz gibi Locate komutunu da kullanabiliriz.

AdoDataset1.Locate('MUSTERIADI','EMRE',[]); komutu aktif satırı muşteri adı Emre olan satıra getirecektir. Eğer boş köşeli parantezi [loPartialKey] şeklinde yazarsak o zaman EMRE ile başlayan ilk kayda gideriz. Locate komutu birden fazla sütunda da da çalışacak özelliktedir. Detaylı bilgi için help'e bakınız.

Dataset'in EOF ve BOF özellikleri de ilk veya son kayıtta olup olmadığımızı anlamak için kullandığımız özelliklerdir. Eğer ilk kayıtta isek AdoDataset1.BOF true döndürür. Eğer tablonun sonuna geldiysek EOF true olacaktır. Diğer durumlarda her ikisi de false döndürür.

Dikkat ederseniz navigatorda bazı durumlarda bazı düğmeler pasif duruma geçmekte bazı durumlarda da aktifleşmektedir. Bu şu anlama gelir. Dataset'e post diyebilmek için (tick'e tıklayabilmek için) tabloya bir kayıt eklemek veya bir kaydı edit moduna getirmek gerekmektedir. Eklediğiniz butonlardan da deneyebileceğiniz gibi bu iki durumdan biri
olmadan kendi eklediğiniz post düğmesine tıklarsanız hata alırsınız. Tıpkı ilk kayıtta olduğunuz halde prior, edit modunda olmadığınız halde cancel dediğinizde alacağınız gibi. Navigator bileşeni datasetin o anki duruma göre veremeyeceğiniz komutları takip ederek ilgili butonları pasifleştirir ancak bu özellik sizin eklediğiniz butonlarda olmayacaktır. Bu özelliği programlayabilmek için edit butonuna tıklandığında edit butonun enabled özelliğini false yapmalı, post veya cancel komutu verildiğinde de edit butonunun enabled özelliğini tekrar true yapmalısınız. Bunun bir yolu da ilgili komutu vermeden önce dataset'in state özelliğini kontrol etmektir. State özelliği datasetin o anki durumu ile ilgili bilgi almamızı sağlar. Örneğin dataseti edit moduna geçirdiğimizde AdoDataset1.State ifadesi bize dsEdit olarak dönecektir.

Projemize kodlara karşılık eklediğimiz butonlardan post butonundaki AdoDataset1.post; komutunu

if AdoDataset1.state = dsEdit then AdoDataset1.post;

olarak değiştirirseniz o zaman dataset edit modunda olmadığında post denirse hata vermesini engellemiş olursunuz. Çünkü post demeden önce datasetin dsEdit modunda olup olmadığını test etmiş oluruz. Bu kod eğer dataseti edit moduna geçirdiysek çalışacaktır ancak dataset'e yeni bir kayıt eklersek bu sefer yukarıda yazdığımız kodla kayıt yapamayacağımızı görürüz. Çünkü dataset'e bir kayıt eklediğimizde dataset dsEdit değil dsInsert moduna girer. Bu nedenle post butonun altındaki kodu

if AdoDataset1.state = dsEdit then AdoDataset1.post;
if AdoDataset1.state = dsInsert then AdoDataset1.post;

gibi iki durumu da kontrol edecek şekilde değiştirebiliriz. Bunun daha kısa yazılımı

if (AdoDataset1.state = dsEdit) or (AdoDataset1.state = dsInsert) then AdoDataset1.post;

olabilir. State özelliğini help'ten inceleyerek hangi değerler alabildiğine bakabilirsiniz. AdoDataset1.edit dediğimizde dataset'in state'i dsEdit olur. AdoDataset1.Append dediğimizde de state dsInsert olur. Diğer türlü de dsBrowse olacaktır.

Kodla veri okumak da datasetin FieldByName özelliği kullanılarak yapılır. bir buton ekleyelim ve altına

Form1.caption := AdoDataset1.FieldByName('MUSTERIADI').value;

yazalım. Bu butona tıkladığımızda formun captionunda datasetin aktif satırının MUSTERIADI sütununda yazan bilgiyi görüntüleriz. forma bir listBox ekleyelim ve ekleyeceğimiz diğer bir butonun altına şunu yazalım.

AdoDataset1.First; //ilk kayda git
ListBox1.items.clear; //listbox'u temizle

// son kayda gelene kadar çalıştır. Son kayda gelene kadar EOF özelliği false olacaktır.

while not ADODataset1.Eof do
begin
// MUSTERIADI sütununda yazan bilgiyi listbox'a ekle.
listbox1.items.add(AdoDataset1.FieldByName('MUSTERIADI').value);

// sonraki kayda git;
AdoDataset1.Next;
end;

Bu kod tablodaki tüm satırları tek tek dolaşarak MUSTERIADI sütunundaki bilgiyi listbox'a ekleyecektir. Ancak listbox datasource'ye bağlı olmadan çalıştığı ve ilgili satırları biz manuel eklediğimiz için listbox'ta yapacağımız değişiklikler dataset'i değiştirmeyecektir. Mesela griddeki gibi satırlara tıklamamız datasetin aktif satırını değiştirmez. Eğer MUSTERIADI sütununda boş kayıtlar varsa boş değeri listbox'a eklerken hata alabiliriz. Bunu engellemek için listbox'a ekleme kodunu

AdoDataset1.FieldByName('MUSTERIADI').AsString

olarak yazabiliriz. AsString ifadesi okunan değer boşsa onu boş String'e çevirecek ve listBox'a eklenebilecek hale çevirecektir. Eğer okumaya çalıştığımız değer integer (tamsayı) ise

AdoDataset1.FieldByName('MUSTERIADI').AsInteger

yazabiliriz. Bu durumda da boş kayıtlar sıfır'a dönecektir. Diğer veritipleri için de AsXXXXX ifadeleri vardır. Delphi yardımında TField özelliklerinden diğer AsXXXXX çevrimlerini görebilirsiniz. Genellikle veritipi neyse ona uygun AsXXXX ifadesini kullanınız.

Son olarak aynı özelliği kullanarak datasetin verilerini de değiştirebiliriz.

AdoDataset1.FieldByName('MUSTERIADI').value = 'EMRE';

ifadesi aktif satırın MUSTERIADI sütunundaki değerin EMRE olmasına neden olacaktır.
Tabiki bunu yapabilmek için öncelikle dataseti edit moduna geçirmemiz veya yeni bir kayıt eklememiz sonra da post komutunu çağırarak satırı kaydetmemiz gerekecektir. Bir satır edit modunda iken her sütunu değiştirdikten sonra kaydetmemize gerek yoktur. Satırda istediğimiz tüm değişiklikleri yaptıktan sonra post diyebiliriz.

AdoDataset1.Insert;
AdoDataset1.FieldByName('MUSTERIADI').value = 'EMRE';
AdoDataset1.FieldByName('MUSTERIADRESI').value = 'ANKARA';
AdoDataset1.Post;

Yeni kayıt eklemekle ilgili kodu bu şekilde yazabilirsiniz. Bu kayıt işleminden sonra aktif satır bu yeni eklediğimiz satır olacaktır.

Cumartesi, Ekim 22, 2005

Bilgisayar programı nedir?

Bilgisayara bir işi yaptırmak için verdiğimiz komutlar bütününe bilgisayar programı dendiğini bilgisayarlarla az çok ilişkisi olan herkes bilmektedir. Bilgisayar programı bir programlama dili kullanarak yazılır. Programı çalıştırdığımızda bilgisayar ilgili komutları okuyarak programda kendisine tarif edilen işi yapar. Aslında günümüzde her türlü elektronik cihaz bu tür programlar barındırır ve bunları işleterek değişik işler yaparlar. Bir çamaşır makinesini düşünürsek çamaşır makinelerinde A,B,C,D... gibi harflerle isimlendirilmiş programlar bulunur. Mesela A programını işletirsek makine önceden belirlenmiş miktarda su çeker sonra onu ısıtır sonra çamaşırları içine koyduğumuz tamburu belli bir hızda birkaç tur sola sonra sağa çevirir sonra belli bir süre bekler, vs.vs bu şekilde devam eder. A programı beyaz çamaşırlar için işletilir. Eğer makineden renki çamaşırlar için kullanılan D programını işletmesini istersek o zaman farklı yıkama, bekletme ve sıkma metodu işleterek çalışmaya başlayacaktır. Temel mantık açısından bilgisayar programları da benzer şekilde çalışır. Tabiiki bir çamaşır makinesine göre daha yetenekli bir cihaz olduğu için onun işleteceği programlar daha fonksiyonel olmak durumundadır.

Bilgisayar programları yazmak için birçok dil vardır. Bir programlama dilini öğrenmek aslında bilgisayarların çalışma mantığını kavrayabilmiş bir kişi için çok kolaydır. Modern program geliştirme araçları dilin yanısıra birçok yardımcı araç içerir. Bir program yazabilmek için dilin yanısıra bu yan bileşenlerin de öğrenilmesi gerekir. Bu yan bileşenlere ve yardımcı araçlara genel olarak programlama arabirimleri denmektedir. Örneğin yazıcı ile ilgili yardımcı araçları kullanmadan çıktı alabilmek mümkündür ama çok zor bir iştir. Ancak yazılım üreticileri yazıcıdan çıktı almakla ilgili yardımcı programlar yazarlar ve bunları işletim sistemine eklerler. Bu sayede programcıya düşen iş birkaç basit komutu işletmektir. Yani yazıcıdan çıktı almak için programlama dilinin yanısıra yazıcı yardımcılarını da öğrenmek gerekir. Yazdığımız programın türüne göre bu arabirimleri kullanmayı öğreniriz. Örneğin veritabanı programı yazanlar veri kaydetmek, silmek, düzenlemek gibi işler için SQL kullanırlar. Oyun programcıları ekrana istedikleri grafikleri çizebilmek için DirectX kullanırlar. Eğer DirectX arabirimi olmasaydı programcı direk ekran kartına ulaşan kodlar yazmak zorunda kalacaktı. Ayrıca yazdığı kodun her ekran kartında çalışacağı da garanti olmayacaktı. DirectX oyun programları ile ekran kartları arasındaki iletişimi sağlayan arabirim olarak görev yapmaktadır. Bu sayede bir oyun çalışırken bilgisayara takılı ekran kartının ne olduğuyla pek ilgilenmez. Bir oyun programcısı SQL ile hiç işi olmayacağı için bu aracı kullanmayı bilmeyebilir. Kısacası bir programlama dili öğrenirken yapmak istediğimiz programın türüne göre yardımcı arabirimleri de öğrenmemiz gerekir ki genellikle bunları öğrenmek dili öğrenmekten daha çok zaman alabilmektedir.

Bu arabirimlerin en temeli işletim sistemidir. İşletim sistemleri bütün programların altında çalışan programlardır ve bilgisayar açılırken otomatik olarak yüklenerek bilgisayarın işlevlerini kullanıma açarlar. Günümüzde en yaygın kullanılan işletim sistemleri Windows ve Linux'tur. İşletim sistemleri programları çalıştırır ve onların isteklerine cevap verirler. İşletim sistemini kullanmadan direk harddiske ulaşarak bir dosya kaydetmek temelde çok zor bir iştir. Harddisk içinde fiziksel olarak birkaç tane CD benzeri manyetik disk bulunur ve bilgiler bu manyetik yüzeyler üzerine kaydedilir. Bir dosyayı kaydederken diske kaydetme işini yapan program bu dosyanın hangi diskin hangi yüzüne ve hangi bölümüne yazıldığını bilmelidir ve gerektiğinde bu dosyayı tekrar bulabilmelidir. İşletim sistemi sayesinde bu karmaşık işleri programcı hiç düşünmez çünkü işletim sistemi bu tür diske kaydetme, silme, okuma gibi işleri yapmaktadır. Programcıya düşen işletim sisteminin bu işleri yapmak için programcıların erişimine sunduğu arabirime gerekli komutları vermektir. Aslında işletim sistemlerinin bu arabirimleri de çoğu programcı için karmaşıktır. Çünkü programcının ihtiyacı olabilecek her türlü işlemi yapabilmesi için bu yardımcılar birçok komut sunarlar. Delphi, .Net, Java gibi modern programlama araçları, programcıları işletim sistemlerinin bu karmaşık yapılarıyla uğraştırmak yerine kendi içlerinde ikinci bir programlama arabirimi içerir. Bir dosyanın diskte bulunup bulunmadığını kontrol edeceksek ve Delphi kullanıyorsak bunu iki şekilde yapabiliriz:

1. Windows API (Uygulama Programlama Arabirimi) kullanarak direk windows'a sorarız.
2. Delphi'nin FileExists fonksiyonunu kullanarak Delphi'ye sorarız. Delphi de Windows'a sorar ve bize cevap verir.

Windows işletim sistemi harddiske dosya kaydetmek gibi başka birçok önemli hizmet sunar. Ekrana pencereler çizmeye, içine yazı yazılabilecek alanlar eklemeye, ses ve görüntü hizmetlerine erişmeye, network sistemleri oluşturmaya, internete bağlanmaya, internetten dosya indirmeye, yazıcı, tarayıcı, webcam, mp3 player, fotoğraf makinesi gibi bilgisayara bağlanabilen donanımların sunduğu hizmetlere erişmeye ve bunlara benzer birçok işi yapmaya yarayan servislerle donatılmıştır. Bu servisleri kullanırken bu servislerin sağladığı imkanlarla sınırlıyız. Örneğin ekrana bir pencere çıkarmak programcı için çok basit bir iştir. Ancak çıkardığımız pencerenin başlığının rengini değiştirmek çok zor bir iştir. Çünkü windows pencere başlıklarının renginin değiştirilmesi ile ilgili direk bir yöntem sunmaz. Kullanıcı isterse tüm pencere başlıklarının rengini windows'un kendi ayarlarından değiştirebilir ve tüm pencere başlıkları aynı renkte görüntülenir.

Bazı programcılar programlama ortamlarının sunduğu fonksiyonları kullanmayı pek sevmezler ve direk işletim sisteminin komutlarını kullanma gayreti içindedirler. Çok hız gerektiren bazı hassas uygulamalar için bu gerekli olabilir ancak genelde işletim sistemi API'si ile uğraşmak çok da büyük bir kazanç sağlamaz. Delphi içinde gelen bu arabirim komple programımız içine ekleneceğinden yazdığımız programın boyu direk windows API ile çalışan programa göre daha büyük olacaktır. Günümüzde harddisklerin ve hafızların boyu düşünüldüğünde delphinin programa ekleyeceği birkaç yüz KB'lik yük tamamen gözardı edebilebilir. Boyu küçük program yazmaya gayret etmek yerine iyi işleyen bir program yazmayı becermek çok daha faydalı olacaktır.

Delphi işletim sistemi ile kontak kurulması gerektiğinde windows API'si üzerinden çalıştığı için Delphi ile yazdığımız programlar Linux, BSD, Unix gibi başka işletim sistemlerinde çalışmaz. Bu işletim sistemlerinde çalışacak programlar yazmak için o işletim sistemine uyumlu bir programlama aracı kullanmak gerekir. Java ile yazılan programlar hem Windows'da hem de Linux'da çalışabilmektedir. Ancak Java ile yazılmış olsa dahi işletim sisteminin sunduğu özellikleri direk olarak kullanamazsınız. Çünkü bu durumda windows için yazdığınız kodlar Linux için anlamsız olacaktır.

Delphi geliştirme ortamının programlama dili Object Pascal'dır. Delphi nin altyapısında kullandığı ve direk işletim sistemi ile iletişime geçen arabirimi RTL'dir. Görsel özellikleri içeren arabirim ise VCL'dir. Bunlar Delphi'nin temel arabirimleridir. Program yazarken bilerek ya da bilmeyerek bu arabirimlerden faydalanırız. Bu arabirimlerin sunduğu özellikler ek programlar yüklenerek genişletilebilir. Mesela Delphi standart olarak veri şifrelemekle ilgili bir özellik içermez. Bu özellikler sonradan ek araçlar yüklenerek programlama diline eklenebilir. Bunlara genel olarak 3. Parti bileşenler denmektedir. Delphi üzerine en çok bileşen yazılan ortamlardan biridir ve bu ek programların da büyük çoğunluğu internetten ücretsiz olarak edinilebilir. Delphi belkide dünyada bu konuda en çok desteklenen programlama aracıdır. Programlama araçlarının iyiliği veya kötülüğü tartışılırken dille ilgili özelliklerin yanısıra bu arabirimler ile sunduğu hizmetler ve 3. parti desteği de büyük önem taşımaktadır.