You are on page 1of 278

KAYNAKLARIN BOŞALTILMASI ve IDisposable ARAYÜZÜ

{10.11.2007 Cumartesi}
Nesne yönelimli programlamada çeşitli işler, sınıflarla temsil edilip
basit bir biçim de kullanıma sunulmaktadır. Sınıfların ayrıntılarının
gizlenerek onların public fonksiyonlarla kullanılması işlemine kapsülleme
denilmektedir.

Sınıflar çeşitli kaynakları kullanıyor olabilir. Örneğin; kaynaklar sınıfın


başlangıç fonksiyonlarında tahsis ediliyor olabilir.

Başlangıç fonksiyonunda tahsis edilen kaynaklar nerede


boşaltılmalıdır? Bu konuda ilk akla gelen yöntem finalize
fonksiyonundan faydalanmak olabilir. Finalize fonksiyonu çöp toplayıcı
sistem tarafından, boşaltım işleminden hemen önce çağrılmaktadır. C# ta
finalize fonksiyonu doğrudan tanımlanamaz. Bunun yerine bitiş
fonksiyonu destructor fonksiyonu tanımlanır. Derleyici, bitiş fonksiyonunu
finalize fonksiyonu olarak ara koda yazar. Bitiş fonksiyonu başına erişim
belirleyici getirilmez. Örneğin:

~A()
{
//...
}

Fakat maalesef boşaltım işlemlerinin bitiş fonksiyonunda yapılmasının iki


sakıncası vardır:

1- Bitiş fonksiyonunun, nesne seçilebilir duruma geldikten sonra tam


olarak ne zaman çöp toplayıcı tarafından çağrılacağı belli değildir.
Hatta çöp toplayıcının program sonlanmadan bitiş fonksiyonun
çağırması garanti de değildir.
2- Bir sınıfın başka bir sınıf türünden veri elemanına sahip olması
durumunda, elemana sahip sınıf seçilebilir hale geldiğinde,
muhtemelen elemana ilişkin nesne de seçilebilir duruma gelecektir.
İşte bu durumda çöp toplayıcı nesneleri hangi sırada sileceğinin bir
garantisi yoktur. Şüphesiz programcı önce elemana sahip sınıf
nesnesinin, sonra elemana ait sınıf nesnesinin silinmesini tercih eder.
Bu nedenle elemana sahip sınıfın, bitiş fonksiyonu içerisinde
referans türünden elemanların kullanılmaması gerekir. Bu durum ise
bitiş fonksiyonlarının hepten kullanışsız hale getirmektedir. Örneğin:

class Sample
{
private FileStream m_fs;

public Sample()
{
m_fs = new FileStream(...);

1
//...
}
~Sample()
{
m_fsa.Close  Dikkat soruna yol açabilir.
}
//...
}

Kaynak bırakma işleminin determinist bir biçimde yapılması için,


programcının bu işi bir fonksiyona yaptırması uygun olur. İşte bunun için
herkes tarafından anlaşılabilecek isimde bir fonksiyon önerilmektedir. Bu
fonksiyon Dispose fonksiyonudur. Dispose fonksiyonu IDisposable
arayüzü ile ifade edilmiştir.

interface IDisposable
{
void Dispose;
}

Yazdığımız sınıf, bir takım kaynakları kullanıyor ise ve bu kaynakların


bırakılması bizim için önemli ise önerilen yöntem, o sınıf ya da yapının
IDisposable arayüzünü desteklemesi ve bu işlemin Dispose fonksiyonuna
yaptırılmasıdır. Boşaltma işleminin Dispose fonksiyonu ile yapılmasının bir
faydası da, nesnenin türünü bilmeden boşaltma yapılmasının mümkün hale
getirmesidir. Örneğin:

ArrayList al = new ArrayList();

//...

foreach (IDisposable id in al)


id.Dispose();

HOCANIN ÖRNEĞİ
using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
ArrayList al = new ArrayList();

//...
//...

2
foreach (IDisposable id in al)
id.Dispose();
}
}

class A : IDisposable
{
public void Dispose()
{
Console.WriteLine("A.Dispose");
}
//...
}

class B : IDisposable
{
public void Dispose()
{
Console.WriteLine("B.Dispose");
}
//...
}

class C : IDisposable
{
public void Dispose()
{
Console.WriteLine("C.Dispose");
}
//...
}
}

O halde programcı bir sınıfın ya da yapının IDisposable arayüzünü


desteklediğini gördüğünde, bu sınıf ya da yapının bir takım kaynaklar
tahsis ettiğini, dolayısıyla işlem bittiğinde Dispose fonksiyonu ile bu
kaynakların bırakılması gerektiğini düşünmesi gerekir.

Bir sınıfın IDisposable arayüzünü desteklediğini düşünelim. Sınıfı


kullanan kişinin, kullanımı bittikten sonra Dispose fonksiyonunu bilinçli
olarak çağırması tavsiye edilir. Programcı bunu unutursa? Bir kaynağın
boşaltılmaması her zaman soruna yol açacağı anlamına gelmemektedir. Bir
kaynağı hiç boşaltmamaktansa, sınıfın bitiş fonksiyonunda boşaltmak
tercih edilebilir. Hiç olmazsa çöp toplayıcı boşaltma işlemini yapacaktır.
Bir sınıfın kullandığı kaynaklar “Yönetilebilen” ve “Yönetilemeyen” olmak
üzere ikiye ayrılmaktadır. Yönetilebilen kaynak, sınıf nesnesi yoluyla dolaylı
kullanılan kaynaktır. Hâlbuki yönetilemeyen kaynak, doğrudan işletim
sistemi düzeyinde tahsis edilen kaynaktır. Örneğin; biz işletim sisteminin
CreateFile API fonksiyonunu C# dan çağırarak bir dosya açtığımız
zaman, bu kaynak yönetilemez bir kaynaktır. Yönetilemez kaynak tahsis

3
eden tarafından boşaltılmalıdır. CLR sistemi bu kaynağın tahsis edilmiş
olduğunu anlayamaz. Hâlbuki biz dosyayı, FileStream sınıfı ile açarsak,
bizim için FileStream nesnesi yönetilebilen bir kaynaktır. Çünkü biz bu
dosyayı kapatmasak bile çöp toplayıcı, bu nesneyi silerken dosyanın
kapatılması olasılığı vardır.

Özetle programcının kaynak boşaltımını hem Dispose fonksiyonunda


hem de bitiş fonksiyonunda yapması iyi bir tekniktir. Fakat Dispose
fonksiyonunda, hem yönetilen hem de yönetilemeyen kaynakların
boşaltılması gerekirken, bitiş fonksiyonunda yalnızca yönetilemeyen
kaynaklar boşaltılmalıdır. Çünkü yönetilebilen kaynaklar her sınıfın kendi
bitiş fonksiyonunda nasıl olsa boşaltılacaktır.

Bütün bu anlatımlar sonucu olarak kaynak boşaltımı tipik bir biçimde


şöyle yapılmalıdır.

1- Boşaltım hem Dispose fonksiyonu hem de bitiş fonksiyonunda


yapılmalıdır. Dispose fonksiyonunda hem yönetilen hem de
yönetilemeyen kaynaklar, bitiş fonksiyonunda ise yalnızca
yönetilemeyen kaynaklar boşaltılmalıdır.
2- Kod tekrarının engellemek için ortak bir fonksiyonun çağrılması
uygun olur. Kullanılan tipik kalıp şöyledir:

class Sample : IDisposable


{
//...
private void Dispose (bool disposing)
{
if (disposing)
{
// yönetilen kaynakları boşalt

GC.SuppressFinalize(this);
}
// yönetilemeyen kaynakları boşalt
}
public void Dispose() //Arayüz fonksiyonu
{
Dispose(true);
}
~Sampe()
{
Dispose(false);
}
}

Burada SuppressFinalize fonksiyonu, çöp toplayıcının ilgili nesne için


bitiş fonksiyonunu çağırmasını engellemektedir. Yani Dispose fonksiyonu

4
programcı tarafından çağrılırsa, çöp toplayıcı tarafından artık bitiş
fonksiyonu çağrılmamaktadır. Aksi takdirde yönetilemeyen kaynaklar iki
kez boşaltılacaktır.

public static void SuppressFinalize(Object obj)

.NET KÜTÜPHANESİNDEKİ COLLECTİON SINIFLARI

Amacı birden fazla nesneyi tutmak olan sınıflara Collection sınıflar


denmektedir. Bu bakımdan dizilerde birer Collection sınıflar olarak
değerlendirilebilir.

Collection sınıflar liste tarzı ve sözlük tarzı olmak üzere ikiye


ayrılmaktadır. IList arayüzünü destekleyen sınıflara liste tarzı collection
sınıflar, IDirectory arayüzünü destekleyen sınıflara sözlük tarzı collection
sınıflar denir. Bu durumda bütün bu sınıflarda da ortak fonksiyonlar vardır.

IList ve IDirectory arayüzlerinin türetme şemaları şöyledir:

IEnumerator

ICollection

IList IDirectory

IEnumerator arayüzünün tek bir fonksiyonu vardır:

interface IEnumerable
{
IEnumerator GetEnumerator
}

ICollection arayüzünün 4 elemanı vardır. En önemli elemanı int türden


Count isimli read only property elemanıdır.

int count (get;)

Bu arayüzün ikinci önemli elemanı ise collection içerisindekilerin bir diziye


kopyalanmasını sağlayan CopyTo fonksiyonudur.

5
void CopyTo (Array array, int index)

IList arayüzü pek çok elemana sahiptir. Bu elemanların hepsi liste


tarzı collection bir sınıfta bulunmak zorundadır. İleriki konularda bir
collection sınıfla karşılaşıldığında, bu sınıfın IList arayüzünü desteklediği
belirtilmişse, o sınıfta aşağıdaki elemanların hepsi bulunmak zorundadır.

IList arayüzünün Add isimli fonksiyonu, listenin sonuna yeni bir


eleman eklemek için kullanılır:

int Add (Object value)

Fonksiyon, eklenecek değeri parametre olarak alır ve eklemenin yapıldığı


index değeri ile geri döner.

IList arayüzünün Insert isimli fonksiyonu belirli bir indekse insert


yapar(araya eleman ekler).

HOCANIN ÖRNEĞİ
using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
ArrayList al = new ArrayList();

for (int i = 0; i < 10; ++i)


al.Add(i);

al.Insert(5, 100);

foreach (int x in al)


Console.WriteLine(x);
}
}
}

RemoveAt isimli fonksiyon belirli bir indeksteki elemanı siler:

void RemoveAt(int index)

IndexOf fonksiyonu, parametresi ile belirtilen elemanı sıralı olarak arar ve


onun bulunduğu indeks değeri ile geri döner.

6
int IndexOf (Object value)

Contains isimli fonksiyon bir değerin listede olup olmadığına bakar.

bool Contains (Object value)

Clear isimli fonksiyon listedeki tüm elemanları siler.

Hocanın Örneği
using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
ArrayList al = new ArrayList();

for (int i = 0; i < 10; ++i)


al.Add(i);

al.Clear();

Console.WriteLine("Eleman Sayısı: {0}", al.Count);


foreach (int x in al)
Console.WriteLine(x);
}
}
}

Hocanın Örneği
using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
ArrayList al = new ArrayList();

for (int i = 0; i < 10; ++i)


al.Add(i);

7
Console.WriteLine("Eleman Sayısı: {0}", al.Count);
foreach (int x in al)
Console.WriteLine(x);

int index = al.IndexOf(5);

Console.WriteLine(index);

}
}
}

IList int parametreli indeksleyici elemanı get/set bir elemandır. Belirli


bir indeksteki elemanı alıp set etmekte kullanılabilir. Örneğin; c liste tarzı
bir collection sınıfı olsun. Aşağıdaki gibi bir işlemi yapabiliriz:

c[index] = val;

DİZİLERİN COLLECTION ÖZELLİKLERİ

Anımsanacağı gibi bütün diziler System.Array sınıfından türetilmiştir.


System.Array sınıfı da IList arayüzünü desteklemektedir. O halde, bir
dizi referansı ile IList arayüzündeki tüm elemanları kullanabiliriz. Yani
dizilerde aslında liste tarzı sınıflardır.

ArrayList SINIFI

ArrayList sınıfı, System.Collection isim alanındadır. Tipik olarak


kendi içerisinde object türünden bir dizi içerir. Bu dizinin tahsis edilmiş
boyuna capacity, dizinin içerisine yerleştirilmiş eleman sayısına count
denir. Eleman ekleyen fonksiyonlar eklemeyi yaparlar ve count değerini bir
arttırırlar. Count değeri, capacity değerine eriştiğinde, sınıfın ilgili
fonksiyonu iki kat capacity genişliğinde yeni bir dizi tahsis eder, eski
değerleri yeni diziye kopyalar ve yeni diziden devam eder. Capacity
artırımı, önceki değerin iki katı artırım şeklinde yapılır. Eleman silme
durumunda capacity değeri azaltılmaz. Capacity kavramı, her ekleme
işleminde tahsisat yapmayı engellemek için düşünülmüştür. İki kat
arttırma gelenekseldir. ArrayList sınıfı C++ taki Vektör sınıfına işlevsel
olarak karşılık gelmektedir.

SÖZLÜK TARZI COLLECTION SINIFLAR


{17.10.2007 Cumartesi}
IDirectory arayüzünü destekleyen sınıflara, sözlük tarzı sınıflar
denir. Sözlük tarzı sınıflar anahtar-değer çiftini tutmaktadır. Böylece

8
anahtar verildiğinde değer alınabilmektedir. Anahtar ve değer çifti, object
olarak saklanıp geri verilmektedir. Dolayısıyla anahtar ve değer herhangi
bir türden olabilir. Örneğin; bir şehrin plaka numarası anahtar, o şehrin
diğer bilgileri değer olabilir. En çok kullanılan sözlük tarzı sınıf Hashtable
sınıfıdır. Bu sınıfta anahtar ve değer, Add fonksiyonu ile girilebilir. Bilginin
geri alınması, indeksleyici ile yapılabilir. İndeksleyicide [] içerisine anahtar
girilmelidir. Örnek1:

using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
Hashtable ht = new Hashtable();

ht.Add(123, "Savaş Gül");


ht.Add(111, "Ali Serçe");
ht.Add(122, "Kaan Aslan");

string name = (string)ht[123];

Console.WriteLine(name);
}
}
}

Örnek2:

using System;
using System.IO;
using System.Collections;

namespace CSD
{
class App
{
public static void Main()
{
Hashtable ht = new Hashtable();

ht.Add("Savaş Gül", 123);


ht.Add("Ali Serçe", 111);
ht.Add("Kaan Aslan", 122);

int no = (int)ht["Savaş Gül"];

9
Console.WriteLine(no);
}
}
}

.NET GUI PROGRAMLAMA MODELİ

Windows uygulamaları, konsol tabanlı ya da GUI tabanlı olmak


üzere ikiye ayrılabilir. Konsol tabanlı uygulamalar, siyah ekranla çalışan
uygulamalardır. Hâlbuki GUI uygulamaları, grafik arayüzünü kullanan
pencereli uygulamalardır.

Windows sistemlerinde, ekranda bağımsız olarak kontrol edilebilen


ekranlara pencere denmektedir.

Doğrudan masaüstüne açılan pencerelere, ana pencere(top level


windows) denir. Bir pencerenin içerisinde bulunan ve o pencerenin dışına
çıkamayan pencerelere, alt pencere(child windows) denilmektedir. Her
pencerenin bir üst penceresi(parent window) vardır. Alt pencerenin de alt
pencereleri olabilir. Bir pencerenin tek bir üst penceresi vardır. Fakat bir alt
pencerenin birçok alt penceresi bulunabilir. Üst pencereleri ortak olan
pencerelere, kardeş pencere(sinling windows) denir. Ana pencereler,
masaüstü penceresinin alt pencereleri durumundadır. Görüldüğü gibi
pencereler hiyerarşik bir yapı içermektedir.

Bir pencerede, görmeye alıştığımız pek çok öğe bulunmak zorunda


değildir. Tipik bir ana pencerenin başlık kısmı vardır. Pencere başlık
kısmında, minimize – maximize gibi tuşlar bulunur. Pencere başlığının
altında kalan, pencere sınırları içerisinde bulunan alana, çalışma
alanı(client area) denir.

Bir pencerenin başlık kısmı ve sınır çizgileri olmak zorunda değildir.


Bu durumda pencere ekranda fark edilmeyebilir.

Pek çok uygulamanın yani prosesin, tek bir ana penceresi vardır.
Fakat bir program, birden fazla ana pencereye sahip olabilir. Görev
çubuğu, ana pencereleri gösterir. Çalışan programlar görev yöneticisinden
(task manager) görüntülenebilir.

Yaratılmış olan bir pencere görülür ya da görünmez duruma


sokulabilir. Visual Studio paketindeki Spy++, programı sistemdeki tüm
pencerelere listelemektedir.

Windows mesaj tabanlı bir programlama modeli kullanmaktadır. GUI


programları bu modele uygun yazılmalıdır. Hâlbuki konsol uygulamaları
klasik uygulamalardır. Mesaj tabanlı değildir. Mesaj tabanlı programlama

10
modelinde, işletim sistemi daha birinci elden bir takım olayları izler ve
bunu programlara kendisi iletir. Örneğin; farenin program penceresinin
sınırları içerisinde bir tuşuna basılması ya da program aktifken klavyeden
bir tuşa basılması birer olaydır. Windows bu olayları sayısal bilgi olarak
paketleyip ilgili programa iletir.

Windows işletim sistemi her bir thread mesaj kuyruğu denilen bir kuyruk
sistemi oluşturur. Windows bir pencere üzerinde olay gerçekleştiğinde, bu
olayı bir mesaj biçiminde ifade ederek, o pencere hangi thread tarafından
yaratılmışsa, o threadın mesaj kuyruğuna iletmektedir. Görüldüğü gibi
Windows yalnızca olayları belirleyip kuyruğa yazmaktadır. Mesajların
kuyruktan alınması ve işlenmesi programın sorumluluğundadır. Bunu
programcı sağlamak zorundadır. O halde GUI programı oluşturan
programcı, sürekli bu kuyruğa bakmalı, kuyrukta sırada bulunan mesajı
almalı ve işlemelidir. Buna mesaj döngüsü(message loop) denmektedir.
Mesaj döngüsünün oluşturulması, mesajların alınarak işlenmesi
Application sınıfının, Run fonksiyonları tarafından yapılmaktadır. O halde
programcının Main fonksiyonunda, bu fonksiyonu çağırarak mesaj
döngüsüne girmelidir. Mesaj döngüsünden çıkıldığında Main sonlanır ve
dolayısıyla program da sonlanır. Mesaj döngüsünden çıkma, ana
pencerenin kapatılması ile olur. Yani kullanıcı programın ana penceresini
kapatır. Application.Run fonksiyonu sonlanır. Böylece Main fonksiyonu da
biter ve program sonlanmış olur.

İSKELET GUI PROGRAMI

Ekrana bir pencere oluşturan temel bir C# GUI programı aşağıdaki


gibi yazılabilir:

using System;
using System.Windows.Forms;

namesapace CSD
{
class App
{
public static void Main()
{
/*MyForm mf = new MyForm
Application.Run(mf);bu şekilde de yapılabilir.*/

Application.Run(new MyForm());
}
}
class MyForm : Form
{
//...

11
}
}

Bu iskelet program için mscorlip.dll dışında System.dll ve


System.Windows.Forms.dll dosyalarına da referans edilmelidir. Ayrıca
System.Drawing.dll dosyası, henüz gerekli olmasa da ilerisi için referans
edilmesi tavsiye edilmektedir. Eğer program Empty Project seçeneği ile
düzenlenirse ayrıca bir konsol ekranı da gözükür. Bunu engellemek için
Project seçeneklerinden Output Type “Window Application” biçiminde
seçilmelidir. Application.Run fonksiyonu, parametre olarak Form
referansı istemektedir. Bu Form referansı, Form sınıfından bir sınıf türetilip,
türemiş sınıf biçiminde verilebilir. Bu işlem genişletilebilirlik için tercih
edilmelidir. Application.Run fonksiyonu mesaj kuyruğundan mesajları
alarak işlem yapar. Programın ana penceresi kapatıldığında bu işlemden
çıkılır. Bu durumda Main fonksiyonu sonlanır ve program biter.

Aslında mesaj döngüsünü oluşturmak için, parametresiz bir Run


fonksiyonu da vardır. Normal olarak ana pencereyi görünür yapmak için
Form sınıfının Visible property elemanına true geçmek gerekir. new
işlemi ile ana pencere yaratılır fakat Visible yapmadıkça görülmez. İşte
parametreli Run fonksiyonu Visible işlemini de kendi içerisinde
yapmaktadır. Eğer parametresiz Run fonksiyonunu kullanacak olsaydık,
Visible işlemini bizim yapmamız gerekirdi. Örneğin:

public static void Main()


{
MyForm mf = new MyForm
Mf.Visible = true;
Application.Run(mf);
}

Parametreli Run fonksiyonunun diğer bir farkı, parametre olarak geçtiğimiz


form kapatılınca mesaj döngüsünü sonlandırmasıdır. Örneğin:

public static void Main()


{
MyForm mf1 = new MyForm();
Mf1.Visible = true;

MyForm mf2 = new MyForm();

Application.Run(mf2);
}

Burada mf2 penceresi kapatıldığında, Run fonksiyonu sonlanacaktır. mf2


penceresi ayrıca Visible yapılmamıştır. mf2 penceresi, Run fonksiyonunda
zaten Visible yapılacaktır. Eğer programda parametresiz Run

12
kullanılsaydı, pencereler kapatılsa bile Run fonksiyonu sonlanmazdı. Bu
durumda program çalışıyor durumda görev yöneticisinde görülmektedir.

Anahtar Notlar

Win32 sistemlerinde her threadın ayrı bir mesaj kuyruğu vardır. Bir
threadın açtığı tüm pencerelere ilişkin mesajlar aynı kuyruğa yazılır.
Application.Run fonksiyonu dolayısıyla tüm pencerelere ilişkin mesajları
işler. Fonksiyona geçtiğimiz parametre yalnızca hangi pencere
kapatıldığında programdan çıkılacağını belirtir.

GUI PROGRAMLAMASINDA KULLANILAN ÖNEMLİ YAPI VE


SINIFLAR

GUI programlama da pek çok yardımcı yapı ve sınıf kullanılmaktadır:

-Point Yapısı
Bu yapı System.Drawing isim alanındadır. Point yapısı, yalnızca bir
noktanın x ve y bileşenlerini tutan ve bazı basit işlemleri yapan yardımcı
bir yapıdır. Yapının iki parametreli başlangıç fonksiyonu, yapının tutacağı
fonksiyonu set eder.

public Point (int x, int y);

Point pt = new Point(10,20);

x ve y elemanları istenildiği zaman X ve Y property elemanları ile set


edilebilir. Örneğin:

Point pt = new Point();

pt.X = 10;
pt.Y = 20;

Yapının == operatör fonksiyonu, != operatör fonksiyonu iki noktayı


karşılaştırmaktadır.

Hocanın Örneği
using System;
using System.Collections;
using System.Drawing;

namespace CSD
{
class App

13
{
public static void Main()
{
Point pt = new Point();

pt.X = 10;
pt.Y = 20;

Point pt2 = new Point(10, 20);

if (pt == pt2)
Console.WriteLine("Evet");
else
Console.WriteLine("Hayır");
}
}
}

Yapının int, int parametreli ya da Point parametreli Offset


fonksiyonları, yapının tuttuğu noktayı verilen miktar kadar öteler. Örneğin:

using System;
using System.Collections;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Point pt = new Point(10, 20);

pt.Offset(1, 1);

Console.WriteLine(pt.ToString()); //x = 11, Y =


21
}
}
}

Yapının ToString fonksiyonu, tutulan noktanın koordinatlarını içeren yazı


döndürür.

-Size Yapısı
Size yapısı da System.Drawing isim alanındadır. Yapının başlangıç
fonksiyonlarıyla tutulacak yükseklik ve genişlik belirtilebilir.

public Size (int width, int height)

14
Yapının Width, Height property elemanları, genişlik ve yükseklik
değerlerini get ve set etmede kullanılabilir. Örneğin:

Size sz = new Size();


sz.Height = 10;
sz.Width = 20;

Yapının == operatör fonksiyonu, != operatör fonksiyonları vardır.

Yapının iki Size değerinin toplayan ve iki Size değerini çıkaran operatör
fonksiyonları da vardır.

Size a = new Size(10, 20);


Size b = new Size(30, 30);
Size c;

c = a + b;

Her ne kadar Point ile Size arasında mantıksal bir ilgi yoksa da, Size
değerini Point değerine dönüştüren, Point değerini de Size değerine
dönüştüren explicit tür dönüştürme operatör fonksiyonları vardır. Örneğin:

Size sz = new Size(10, 20);


Point pt;

pt = (Point)sz;

Size yapısının diğer elemanları MSDN dokümanlarından incelenmelidir.

-Rectangle Yapısı
Bu yapıda System.Drawing isim alanı içerisindedir. Dikdörtgensel bir alanı
tutmak için kullanılmaktadır. Yapının iki başlangıç fonksiyonu vardır.

public Rectangle (int x, int y, int width, int height)

Diğer başlangıç fonksiyonu şöyledir:

public Rectangle (Point location, Size size)

Örneğin:

Rectangle rect = new Rectangle(new Point(10,10) new


Size(100,100));

15
Sol üst köşe ve sağ alt köşe koordinatları ile Rectangle nesnesi
oluşturmak için Rectangle sınıfının static FromLTRB fonksiyonu
kullanılabilir. Örneğin:

Rectangle rect = new Rectangle.FromLTRB(10,10,20,20);

{18.10.2007 Pazar}
Rectangle yapısının da ToString fonksiyonu, dikdörtgenin koordinatlarına
ilişkin bir yazı vermektedir.

using System;
using System.Collections;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Rectangle rect = new Rectangle(new Point(10, 20),
new Size(100, 100));

Console.WriteLine(rect.ToString());
}
}
}

Yapının Top, Left elemanları, sol üst köşe koordinatlarını veren read only
propertylerdir. Fakat X ve Y elemanları, sol üst köşe koordinatlarına ilişkin
read write property elemanlardır. Height ve Width property elemanlarda
read write elemanlardır.

using System;
using System.Collections;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Rectangle rect = new Rectangle(new Point(10, 20),
new Size(100, 100));

rect.Height = 500;

Console.WriteLine(rect.ToString());

16
}
}
}

Location property elemanı, sol üst köşe koordinatını Point olarak temsil
eder.

Rectangle yapısının Offset isimli fonksiyonları, dikdörtgeni x ve y miktarı


kadar bulunduğu yerden itibaren öteler.

Hocanın örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
this.Paint += new
PaintEventHandler(MyForm_Paint);
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

Rectangle rect = new Rectangle(100, 100, 50,


100);

g.DrawRectangle(Pens.Red, rect);

rect.Offset(10, 10);

g.DrawRectangle(Pens.Black, rect);
}
}
}

17
Yapının bir grup Inflate fonksiyonu vardır. Inflate fonksiyonu, yapının
tuttuğu dikdörtgeni sol üst ve sağ alt köşelerden açmak ya da büzmek için
kullanılır.

Rectangle rect = new Rectangle (100,200,50,50);


Rect.Inflate(10,20);

Hocanın Örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
this.Paint += new
PaintEventHandler(MyForm_Paint);
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

Rectangle rect = new Rectangle(100, 100, 100,


100);

g.DrawRectangle(Pens.Red, rect);

for (int i = 0; i < 5; ++i)


{
rect.Inflate(-10, -10);
g.DrawRectangle(Pens.Red, rect);
}
}
}
}

Hocanın Örneği
using System;
using System.Windows.Forms;

18
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
this.Paint += new
PaintEventHandler(MyForm_Paint);
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

Rectangle rect = this.ClientRectangle;

g.DrawRectangle(Pens.Red, rect);

for (int i = 0; i < 15; ++i)


{
rect.Inflate(-10, -10);
g.DrawRectangle(Pens.Red, rect);
}
}
}
}

Hocanın Örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

19
class MyForm : Form
{
public MyForm()
{
this.Paint += new
PaintEventHandler(MyForm_Paint);
this.ResizeRedraw = true;
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

Rectangle rect = this.ClientRectangle;

g.DrawRectangle(Pens.Red, rect);

for (int i = 0; i < 15; ++i)


{
rect.Inflate(-10, -10);
g.DrawRectangle(Pens.Red, rect);
}
}
}
}

Yapının Contains isimli fonksiyonları, bir noktanın ya da başka bir


dikdörtgenin, dikdörtgen içerisinde olup olmadığını test etmek için
kullanılır. Örneğin:

public bool Contains (int x, int y)

Hocanın Örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
Rectangle m_rect;

20
public MyForm()
{
this.Paint += new PaintEventHandler(MyForm_Paint);
this.MouseDown += new
MouseEventHandler(MyForm_MouseDown);
this.ResizeRedraw = true;

m_rect = new Rectangle(100, 100, 100, 100);


}

void MyForm_MouseDown(object sender, MouseEventArgs e)


{
if (e.Button == MouseButtons.Left)
{
if (m_rect.Contains(e.X, e.Y)) 
{
MessageBox.Show("İçeride");
}
}
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

g.DrawRectangle(Pens.Red, m_rect);
}
}
}

Intersect iki dikdörtgenin kesişimi, Union iki dikdörtgenin birleşimine


ilişkin dikdörtgenleri elde etmek için kullanılır.

PointF, SizeF ve RectengleF YAPILARI

Bu yapıların Point, Size, Rectangle yapılarından tek farkları, koordinat


bilgilerini int değil de float tür olarak tutmalarıdır. Dolayısıyla noktalı
koordinatlara izin vermektedir. Örneğin:

PointF ptf = new PointF(10.2F, 3.4F);

Bu yapıların int ve float versiyonları arasında dönüştürmeye izin veren,


tür dönüştürme operatör fonksiyonları bulunmaktadır. Aşağıda şekilsel
olarak hangi dönüştürmelerin mümkün
Implicit olduğu belirtilmektedir.
Point PointF

Explicit

Implicit
Size SizeF

21

Rectangle RectangleF
Color YAPISI

Bilgisayar sisteminde renk kırmızı, yeşil ve mavi renklerinin 1


byte’lık[0-255] tonal bileşenleri ile elde edilir. Böylece 16 milyon değişik
renk elde edilir. .Net’te bir rengi ifade etmek için Color yapısı
kullanılmaktadır. Bu yapı da System.Drawing isim alanındadır. Bir renk
Color yapısının başlangıç fonksiyonu ile değil, static FromArgb
fonksiyonları ile oluşturulur:

public static Color FromArgb (int Red, int green, int blue)

Örneğin:

Color redColor = Color.FromArgb(255,0,0);

Kırmızı, yeşil ve mavi tonal bilgilerin dışında bir de Alpha faktörü de vardır.
Alpha faktörü de 0-255 arasında bir değer alır. Alpha faktörü, arka planın
gözükmesini ayarlamaktadır. Örneğin; Alpha faktörü 255 ise saydamsız, 0
ise tam saydamdır. Böylece aslında bir renk saydamlık derecesi ile birlikte
4 byte ile ifade edilir.

Hocanın Örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form

22
{
Rectangle m_rect;

public MyForm()
{
this.Paint += new PaintEventHandler(MyForm_Paint);
this.MouseDown += new
MouseEventHandler(MyForm_MouseDown);
this.ResizeRedraw = true;

m_rect = new Rectangle(100, 100, 100, 100);


}

void MyForm_MouseDown(object sender, MouseEventArgs e)


{
if (e.Button == MouseButtons.Left)
{
if (m_rect.Contains(e.X, e.Y))
{
MessageBox.Show("İçeride");
}
}
}

void MyForm_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Image image;

try
{
image =
Image.FromFile(@"E:\WebApplicationSample\RoleSamples\App_Data\
triomph.jpg");
g.DrawImage(image, this.ClientRectangle);

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

Brush br = new SolidBrush(Color.FromArgb(100, 255,


0, 0));
g.FillRectangle(br, m_rect);
}
}
}

Color sınıfının, pek çok static property elemanı vardır. Bu elemanlar,


önceden hazırlanmış çeşitli renklere ilişkin Color nesnesi verir. Örneğin:

23
public static Color Red (get;)

Yani ne zaman bizden renk istense, biz o rengi Color.Red gibi


Color.Yellow gibi verebiliriz. Aynı zamanda Color yapısının static
olmayan A, R, G, B read only property elemanları, rengin tonal
birleşimlerini vermektedir. Örneğin:

Color c = Color.Turquoise;

Console.WriteLine("Red:{0}, Green:{1}, Blue:{2}",


c.R, c.G, c.B);

Tabi Color yapısının ToString fonksiyonu da bu tonal değerleri veren bir


yazı döndürmektedir.

Color yapısının FromName isimli, ilginç static bir fonksiyonu vardır.

public static Color FromName (string name)

Bu fonksiyona parametre olarak renk ismi verilirse, fonksiyon o renge


ilişkin Color nesnesini vermektedir.

Color c = Color.FromName(“Blue”)

Şüphesiz buradaki renk isimleri, static property ile belirtilen renk isimleri
ile uymalıdır.

Ayrıca Color yapısı ile ilgili KnownColor isimli bir enum türü de vardır. Bu
enum türü, bütün temel renkleri içermektedir. KnownColor değerini alarak
bir Color nesnesi veren, Color yapısının FromKnownColor isimli static
fonksiyonu vardır. Örneğin:

Color c = Color.FromKnownColor(KnownColor.Red);

Anahtar Notlar

Anımsanacağı gibi tüm enum türleri System.Enum isimli yapıdan


türetilmiştir. Bu yapının ToString fonksiyonu reflection uygulayarak
enum değerinin tamsayı değerine karşılık gelen yazıyı döndürür. Yani
elimizde bir sayı varsa, biz bunun enum sabit yazısını elde edebiliriz.
System.Enum yapısının GetNames static fonksiyonu, enum türünün type

24
bilgisini parametre olarak alır ve tüm enum sabit yazılarını bir dizi olarak
elde eder. Örneğin:

public static void Main()


{
string[] colorNames = Enum.GetNames(typeof(KnownColor));

foreach (string colorName in colorNames)


Console.WriteLine(colorName);
}

GetValues isimli static fonksiyon tam ters işlemi yapmaktadır.

public static void Main()


{
int [] a = (int []) Enum.GetValues(typeof(Test));

foreach (int x in a)
Console.WriteLine(x);
}

.NET PENCERE SINIFLARI

Programın ana penceresi, düğmeler, edit alanları, listeleme kutuları


hep birer penceredir. Pencerenin türü ne olursa olsun, ortak bir takım
özelliklere sahiptir. Örneğin; her pencerenin zemin rengi vardır. Her
pencerenin bir konumu, genişlik ve yüksekliği vardır. Her pencerenin ilişkin
olduğu bir başlık söz konusudur. İşte .Net’te tüm bu farklı pencerelerin
ortak özellikleri, Control isimli bir sınıfta toplanmıştır.

Anahtar Notlar

.Nette pencere kavramına ilişkin tüm türler System.Windows.Forms.dll


dosyası içerisinde ve isim alanı olarak ta System.Windows.Forms
alanında bulunmaktadır.

Programın ana penceresi, Form isimli bir sınıfla temsil edilmiştir. Form
sınıfı da dolaylı olarak Control sınıfından türetilmiştir.

Form

25
... Button Form ListBox ...

Görüldüğü gibi Control sınıfındaki her eleman, Form sınıfında, Button


sınıfında, ListBox sınıfında da vardır.

İskelet programda Form sınıfından türettiğimiz sınıfın içerisinde,


taban sınıfın elemanlarını doğrudan kullanabiliriz.

Anahtar Notlar

Anımsanacağı gibi türemiş sınıf fonksiyonları içerisinde, taban sınıfın


public ve protected elemanlarına doğrudan eleman ismi yazılarak ya da
this anahtar sözcüğü ile erişilebilir. this anahtar sözcüğü ile erişim, ilgili
değişkenin yerel bir değişken olmadığını vurgulamak için tercih edilebilir.
Taban ve türemiş sınıflar da aynı isimli elemanlar varsa (bu durumda
uyarıyı kesmek için türemiş sınıftaki bildirimde new belirleyicisi
kullanılmalıdır), türemiş sınıftan erişim yapıldığında türemiş sınıftaki
elemana erişilir. Fakat erişim base anahtar sözcüğü ile yapılırsa isim
yalnızca taban sınıflarda sırasıyla aranır.

Form sınıfından türetilen sınıfın fonksiyonları içerisinde, sınıfın kontrol


kısmındaki elemanlara doğrudan (yani niteliksiz) this anahtar sözcüğü ile
ya da base anahtar sözcüğü ile erişebiliriz.

Anahtar Notlar

Taban sınıf elemanlarına base anahtar sözcüğü ile erişmek, this anahtar
sözcüğü ile erişmekten okunabilirlik açısından daha iyidir. Fakat
Microsoft kendi örneklerinde this anahtar sözcüğünü tercih etmektedir.
Bu tercih muhtemelen programcıları da yanlış etkilemiştir.

Control SINIFININ ÖNEMLİ PROPERTY ELEMANLARI

Control sınıfının birçok property elemanı vardır. Bu property elemanlarının


set bölümleri, arka planda API fonksiyonlarını çağırarak çeşitli
belirlemeleri oluşturur.

Text PROPERTYSİ: Her pencerede bir yazı kavramı vardır. Örneğin; ana
pencere için bu yazı, pencere başlığındaki yazı, düğme için bu yazı
düğmenin üzerindeki yazıdır.

public virtual string Text {get; set;}

Hocanın Örneği

26
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "İskelet Program";
}
}
}

Control sınıfının BackColor isimli property elemanı, pencerenin zemin


rengini belirtmektedir. Bu property Color türündendir.

public virtual Color BackColor {get; set;}

Hocanın Örneği
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "İskelet Program";
base.BackColor = Color.Blue;
}

27
}
}

Bu propertye atanılan rengin, Denetim Masası ayarları ile değişmesi


isteniyorsa, SystemColors sınıfı kullanılmalıdır. SystemColors sınıfının
Window isimli static property elemanı Color türündendir. Eğer BackColor
property’sine SystemColors.Window değeri atanırsa, pencerenin zemin
rengi Denetim Masasında belirtilen renk olur. Bu renk değiştiğinde
otomatik olarak değişir.

base.BackColor = SystemColors.Window;

Windows’un default kurulumunda zemin rengi beyazdır. Aslında


SystemColors sınıfının diğer static property elemanları da renk için
kullanılabilir. Örneğin; bu sınıfın Control elemanı default Control rengini,
ActiveCaption elemanı, aktif pencerenin pencere başlık rengini
belirtmektedir.

base.BackColor = SystemColors.ActiveCaption;

Burada pencerenin zemin rengi, Denetim Masası’ndaki aktif pencere başlık


rengi ile ilişkilendirilmiştir. BackColor property’sine atanmış olan default
değer, SystemColors.Control değeridir. Bu renk default olarak gri renktir.

Control sınıfının bir grup elemanı, pencereyi konumlandırmak için


kullanılır. Form sınıfının StartPosition isimli property elemanı yalnızca
ana pencereler için ilk açılma durumunu belirmektedir. Bu property
FormStartPosition isimli enum türündendir. Bu enum türünün elemanları
ve anlamları şöyledir:

a) CenterScreen: Bu değer ana pencerenin, tam ekranın ortasında


açılmasını sağlar. Örneğin:

base.StartPosition = FormStartPosition.CenterScreen;

b) CenterParent: Bu seçenek ana pencereyi üst pencereye göre


ortalamaktadır.

c) WindowsDefaultBounds: Bu seçenek ana pencereyi Windows’un kendi


seçtiği default yerde ve büyüklükte görüntüler. Bu değer seçilirse, biz ana
pencerenin büyüklüğünü ve yerini Control propertyleri ile değiştiremeyiz.

d) WindowsDefaultLocation: Bu değer seçilirse pencerenin konumu


Windows tarafından seçilir fakat büyüklüğünü biz Control propertyleri ile
ayarlayabiliriz. Bu değer aynı zamanda, StartPosition property’sinin
default değeridir.

28
base.StartPosition =
FormStartPosition.WindowsDefaultLocation;

e) Manual: Bu seçenekle biz ana pencerenin konum ve büyüklüğünü


Control propertyleri ile değiştirebiliriz.

base.StartPosition = FormStartPosition.Manual;
base.Size = new Size(100, 200);
base.Location = new Point(0, 0);

Control sınıfının Left, Right, Top ve Bottom property elemanları,


pencerenin sol üst ve sağ alt noktalarının koordinatlarının vermektedir. Top
ve Left read write property, Right ve Bottom read only propertylerdir.
Örneğin:

base.StartPosition = FormStartPosition.Manual;
base.Left = 0;
base.Top = 0;

Left ve Top propertyleri bir arada, Point türünden Location propertysi


ile ifade edilmiştir.

base.StartPosition = FormStartPosition.Manual;
base.Location = new Point(0, 0);

{24 Kasım 2007 Cumartesi}

Control sınıfının Width ve Height property elemanlarını, pencerenin


genişlik ve yüksekliğini elde edip set etmede kullanabiliriz.

Control sınıfının Location isimli property elemanı Point


türündendir. Read/Write propertydir. Pencerenin sol üst köşesini
belirlemede kullanılır. Benzer biçimde Form sınıfının Size isimli property
elemanı Size türündendir. Read/Write propertydir. Pencerenin genişlik ve
yüksekliğini belirlemede kullanılır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()

29
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.StartPosition = FormStartPosition.Manual;
base.Location = new Point(400, 400);
base.Size = new Size(100, 100);
}
}
}

Control sınıfının Bounds isimli Rectangle türünden Read/Write property


elemanı, tek hamlede pencerenin konumunu, genişlik ve yüksekliğini set
edip almada kullanabiliriz.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form
{
public MyForm()
{
base.StartPosition = FormStartPosition.Manual;
base.Bounds = new Rectangle(100, 100, 500, 500);
}
}
}

Şüphesiz konum ve boyut belirten property elemanları birbirine bağlıdır.


Yani biri değiştiğinde diğerleri de değişir. Çünkü hepsi aynı pencereye
ilişkindir. Konum ve boyut belirten property elemanlarının listesi şöyledir:

Top Width
Left Height

30
Bottom(Read only) Size
Right(Read only) Location
Bounds

Control sınıfının bir grup property elemanı da çalışma alanının


boyutlandırılmasına ilişkindir. Örneğin; ClientSize property elemanı,
read/write elemandır ve Size türündendir. Biz bu propertye değer atarsak,
pencere çalışma alanının genişlik ve yüksekliği atanan değer olacak şekilde
boyutlandırılır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.ClientSize = new Size(100, 100);

MessageBox.Show(base.Size.ToString());
}
}
}

Şüphesiz pencerenin sınır çizgileri ve başlık kısmı olmasaydı çalışma alanı


ile pencerenin kendi boyutu aynı olurdu.

ClientRectangle isimli property elemanı, read only elemandır.


Çalışma alanının konum ve boyutunu Rectangle olarak verir. Fakat verilen
dikdörtgensel bölge, yine çalışma alanı orjinlidir. Dolayısıyla sol üst köşe
0,0’dır.

using System;
using System.Windows.Forms;
using System.Drawing;

31
namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.ClientSize = new Size(100, 100);
MessageBox.Show(base.ClientRectangle.ToString());
}
}
}

ALT PENCERELERİN YARATILMASI

Control sınıfı, doğrudan alt pencere oluşturmak için kullanılabilir. Ya


da programcı, zaten hazır olarak bulunan düğme, listeleme kutusu,
seçenek kutusu gibi hazır alt pencereleri kullanabilir. Alt pencere
yaratıldıktan sonra, bir üst pencere ile ilişkilendirilmesi gerekir. Bu
ilişkilendirme işlemi ile alt pencerenin hangi üst pencerenin alt penceresi
olacağı belirlenmiş olur. İlişkilendirme işlemi için Control sınıfının
Controls isimli property elemanı kullanılmaktadır.

public ControlCollection Controls {get;}

Programcı oluşturduğu alt pencereyi bu collection elemanına


eklediğinde, artık alt pencere belirli bir pencerenin alt penceresi olur.
ControlCollection isimli collection sınıf IList arayüzünü
desteklemektedir. Yani liste tarzı bir collectiondur. Örneğin; bu collection
sınıfın Add isimli fonksiyonu ekleme için kullanılabilir.

Nesne yönelimli programlama tekniği bakımından alt pencerelere


ilişkin referansların, üst pencereye ilişkin pencere sınıfının veri elemanı
olarak bulundurulması uygundur. Alt pencerenin yaratılması işlemi,
herhangi bir zaman yapılabilir. Fakat tipik olarak üst pencerenin başlangıç
fonksiyonu uygun bir yerdir. Örneğin:

using System;
using System.Windows.Forms;

32
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Control m_childWnd;

public MyForm()
{
m_childWnd = new Control();
base.Controls.Add(m_childWnd);
}
}
}

Alt pencere nesnesinin new operatörü ile yaratılması sırasında henüz


pencere işletim sistemi düzeyinde fiziksel olarak yaratılmaz. Yalnızca
yaratım belirlemeleri belirlenir. Fiziksel yaratım alt pencerenin, üst
pencerenin Controls propertysine eklenmesi sırasında yapılmaktadır.

Anahtar Notlar

Visual Stodio kurulduğunda Spy++ isimli bir yardımcı programda


paketin içerisinde bulunmaktadır. Spy++ programı çalışmakta olan
prosesleri threadları ve sistemdeki tüm pencereleri görüntülemektedir.

Bir alt pencere Control sınıfı ile yaratıldığında zemin rengi, otomatik
olarak üst pencereden alınır. Yaratılmış alt pencerenin sınır çizgileri ile
başlık kısmı yoktur. Genişlik ve yükseklik değerleri 0’dır. Bu nedenle
programcının alt pencereyi yaratıktan sonra buna bir genişlik ve yükseklik
ataması ve duruma göre bir zemin rengi vermesi gerekir. Alt pencerenin
ayrıca görünür hale getirilmesine gerek yoktur. Çünkü üst pencereye
bağlama sırasında pencere zaten görünür yapılmaktadır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{

33
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Control m_childWnd;

public MyForm()
{
base.Text = "TestWindow";

m_childWnd = new Control();


m_childWnd.Bounds = new Rectangle(100, 100, 100,
100);
m_childWnd.BackColor = Color.Red;

base.Controls.Add(m_childWnd);
}
}
}

Control collection sınıfının IList arayüzünde olmayan AddRange isimli bir


fonksiyonu da vardır. AddRange tek bir kontrolü değil bir grup kontrolü tek
hamlede eklemek için kullanılır.

public virtual void AddRange (Control [] controls)

Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{

34
private Control m_childWnd1;
private Control m_childWnd2;

public MyForm()
{
base.Text = "TestWindow";

m_childWnd1 = new Control();


m_childWnd1.Bounds = new Rectangle(100, 100, 100,
100);
m_childWnd1.BackColor = Color.Red;

m_childWnd2 = new Control();


m_childWnd2.Bounds = new Rectangle(200, 100, 100,
100);
m_childWnd2.BackColor = Color.Blue;

base.Controls.AddRange(new Control[]
{ m_childWnd1, m_childWnd2 });

}
}
}

Control sınıfının read/write Control türünden Parent isimli bir property


elemanı da vardır. Alt pencerenin Parent propertysine üst pencerenin
referansı atanırsa yine ekleme işlemi gerçekleştirilmiş olur. Örneğin:

base.Controls.Add(m_childWnd); ile

m_childWnd.Parent = this; aynı anlamdadır.

Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Control m_childWnd;

35
public MyForm()
{
base.Text = "TestWindow";

m_childWnd = new Control();


m_childWnd.Bounds = new Rectangle(100, 100, 100,
100);
m_childWnd.BackColor = Color.Red;

m_childWnd.Parent = this;
}
}
}

Control sınıfının Parent propertysinin set bölümü aşağıdaki gibi yazılmış


olabilir:

public Control Parent


{
set
{
value.Controls.Add(this);
}
}

Şimdide alt pencerenin alt penceresini yaratmaya çalışalım. İçerme


ilişkisi dikkate alındığında (yani alt pencerelerin üst pencerenin veri
elemanı olarak saklanması prensibi) Control sınıfından sınıf türetilerek bu
işlemin gerçekleştirilmesi daha uygundur. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{

36
base.Text = "TestWindow";

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

Bir sınıfı doğrudan kullanmakla türeterek kullanma arasında kullanım


bakımından bir fark yoktur. Fakat türeterek kullanma daha genişletilebilir
bir yapı sunmaktadır.

Anahtar Notlar

Bir ana pencere için orjin noktası, masaüstünün sol üst köşesidir. Fakat
alt pencereler için orjin noktası, üst pencerenin çalışma alanının sol üst
köşesidir.

FORM SINIFININ BAZI ÖNEMLİ ELEMANLARI

Form sınıfındaki elemanlar, yalnızca ana pencere için söz konusu olan
elemanlardır. Sınıfın MaximizeBox isimli bool türden property elemanı,
maximize kutucuğunu kaldırmak için kullanılabilir. Default durum true‘dur.

37
Sınıfın MinimizeBox isimli property elemanı da minimize kutucuğunu
kaldırmak için kullanılabilir. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{
base.Text = "TestWindow";
base.MaximizeBox = false;
base.MinimizeBox = false;

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{

38
base.BackColor = Color.Blue;
}
}
}

Form sınıfının FormBorderStyle isimli property elemanı,


FormBorderStyle isimli bir enum türündendir.

Anahtar Notlar

Sınıfın bir elemanın ismi ile onun türünün ismi aynı olabilir. Bu durum
sorun yaratmaz. .Net sınıf sisteminde bir isim kalabalıklığı oluşturmamak
için bu yönteme başvurulmuştur. Bu durumda bu isim “.” operatörü ile
kullanılırsa tür ismi niteliksiz olarak kullanılırsa eleman ismi anlamına
gelir.

FormBorderStyle isimli enum türünün elemanları, pencerenin sınır


çizgilerinin nasıl olacağını belirtir. Bu enum türünün FixedXXX elemanları,
pencereyi fare ile genişletilebilir daraltılabilir olmaktan çıkarır.

Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{
base.Text = "TestWindow";
base.MaximizeBox = false;
base.FormBorderStyle =
FormBorderStyle.FixedDialog;

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);

39
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

Form sınıfının ControlBox isimli bool türden property elemanı,


sistem menüsünü kaldırmak için kullanılabilir.

Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{

40
base.Text = "TestWindow";
base.MaximizeBox = false;
base.ControlBox = false;

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

Form sınıfının ShowIcon isimli property elemanı ise sistem menüsünü


kaldırmadan pencere başlığındaki sistem görüntüsünü kaldırır.

Form sınıfının bool türden TopMost elemanı, pencerenin sürekli ön


planda gözükmesini sağlamakta kullanılabilir.

Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{

41
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{
base.Text = "TestWindow";
base.MaximizeBox = false;
base.ShowIcon = false;
base.TopMost = true;

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

Form sınıfının MinimumSize ve MaximumSize isimli read/write, Size


türden property elemanları, ana pencerenin en dar ve en geniş durumunu
belirlemekte kullanılabilir.

Hocanın Örneği:
using System;

42
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{
base.Text = "TestWindow";
base.MinimumSize = new Size(100, 100);
base.MaximumSize = new Size(400, 400);

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

class MyControl2 : Control


{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

43
Form sınıfının double türden Opacity elemanı 0 ile 1 arasında
saydamlık belirten değer alır. 1 değeri tam saydamsız, 0 değeri tam
saydamlıdır.

Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MyControl1 m_c;

public MyForm()
{
base.Text = "TestWindow";
base.MinimumSize = new Size(100, 100);
base.MaximumSize = new Size(400, 400);
this.Opacity = 0.5;

m_c = new MyControl1();


m_c.Bounds = new Rectangle(100, 100, 100, 100);
base.Controls.Add(m_c);
}
}

class MyControl1 : Control


{
private MyControl2 m_c;

public MyControl1()
{
m_c = new MyControl2();
m_c.Bounds = new Rectangle(20, 20, 20, 20);
base.Controls.Add(m_c);

base.BackColor = Color.Red;
//...
}
}

44
class MyControl2 : Control
{
public MyControl2()
{
base.BackColor = Color.Blue;
}
}
}

PENCERE MESAJLARININ İŞLENMESİ

Ne zaman bir pencere nesnesi oluşturup bunu üst pencereye


bağlasak, .Net ortamı yaratılan bu nesnenin Control referansını kendi
içerisinde bir collectionda tutar. Örneğin; biz 1 tane ana pencere, 10
tanede alt pencere yaratmış olalım. Uygulamamıza özel 11 pencere vardır.
.Net ortamı, bu 11 pencerenin referansını arka planda tutmaktadır.

Anımsanacağı gibi Windows işletim sistemi, bir pencere üzerinde bir


olay gerçekleştiğinde, bu olayı bir mesaj adı altında ilgili threadın mesaj
kuyruğuna ekler. Application.Run fonksiyonları ismine mesaj döngüsü
denilen bir döngü içerisinde, bu mesajları alarak aşağıda anlatıldığı gibi
işlemektedir.

Window işletim sistemi, pencere üzerinde bir olay gerçekleştiğinde


bu olayı bir mesaj şeklinde mesaj kuyruğuna bırakırken, olayın hangi
pencereye ilişkin olduğunu da mesajın içerisine kodlamaktadır.
Application.Run kuyruktaki mesajı aldığında, ilk iş olarak söz konusu
olayın, threadın hangi pencereye ilişkin olduğuna bakar. Application.Run
yaratılan tüm pencerelerin Control referanslarına erişebilmektedir.
Böylece mesaja konu olan pencerenin Control referansı belirlenmiş olur.

Control sınıfının bir grup OnXXX protected virtual fonksiyonu vardır.


Burada XXX mesaja konu olan olayı betimlemektedir. (Örneğin OnClick,
OnMouseDown, OnResize...) İşte Application.Run fonksiyonu, mesaj
kuyruğundan çektiği olaya bakarak elde ettiği Control referansı ile bu
OnXXX sanal fonksiyonunu çağırır.

{25 Kasım 2007 Pazar}

Eğer programcı OnXXX sanal fonksiyonunu kendi sınıfında override


etmişse, programcının override ettiği fonksiyon çağrılacaktır. Böylece
programcı bir mesaj karşılığında bir işlem yapma fırsatını elde eder.
Örneğin; farenin aktif tuşu ile herhangi bir pencereye tıklandığında
Windows pencereye click mesajını gönderir. Application.Run click yapılan
pencereye ilişkin Control referansını bularak, Control sınıfının OnClick
sanal fonksiyonunu çağırır. Örneğin; biz Form sınıfından türettiğimiz sınıfta

45
OnClick sanal fonksiyonunu override edersek bu durumda bizim
fonksiyonumuz çağrılır. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "Message Sample";
}

protected override void OnClick(EventArgs e)


{
MessageBox.Show("Click");
}
}
}
Mesajların çoğunda, mesaja konu olan olay hakkında ek bilgiler söz
konusudur. Örneğin; fare ile pencereye tıkladığımızda nereye tıkladığımızı
da bilmek isteriz. Windows bu bilgiyi de mesaja kodlamaktadır. Ya da
klavyeden bir tuşa bastığımızda Windows kuyruğa mesajı bırakır. Ayrıca
hangi tuşa basıldığını da mesajda kodlar.

OnXXX fonksiyonlarının bir parametresi vardır. Application.Run


fonksiyonu, kuyruktan aldığı parametrik bilgileri bir sınıf nesnesine
yerleştirerek OnXXX fonksiyonlarına, bu sınıf nesnesini parametre olarak
geçirmektedir. Yani mesaja konu olan ek bilgiler OnXXX fonksiyonunun
parametresi olan sınıfın içerisinden alınır. OnXXX fonksiyonlarının
parametresi olan sınıflara, mesaj parametre sınıfları denir. Mesaj
parametre sınıfları EventArgs denilen bir sınıftan türetilmiş sınıflar
biçimindedir.

EventArgs

MouseEventArgs KeyEventArgs
46
Programcı şunları bilmelidir:

— Hangi olaylar gerçekleştiğinde, hangi OnXXX fonksiyonları çağırılır?


— Çağrılan OnXXX fonksiyonunun mesaj parametre sınıfı nedir?
— Mesaj parametre sınıfında hangi değerli bilgiler bulunmaktadır?

EventArgs sınıfının içerisinde faydalı bir bilgi yoktur. Bu sınıf yalnızca


taban sınıflık yapmaktadır. Eğer bir mesajın mesaj parametre sınıfı
EventArgs ise bu mesajda programcıya verilecek bir mesaj yoktur.

Bir pencereye fare ile tıklandığında Application.Run fonksiyonu


yalnızca OnClick sanal fonksiyonunu değil OnMouseDown sanal
fonksiyonunu da çağırmaktadır. OnMouseDown mesajının mesaj parametre
sınıfı MouseEventArgs sınıfıdır. Bu sınıfın içerisinde click yapılan noktanın
koordinatları bulunmaktadır.

Örneğin; MouseEventArgs sınıfının X,Y ve Location property elemanları,


klik yapılan koordinat bilgisini vermektedir. Sınıfın Button isimli property
elemanı MouseButton isimli enum türdendir ve hangi fare tuşuna basıldığını
anlamaktadır.
Hocanın Örneği:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "Message Sample";
}

protected override void OnMouseDown(MouseEventArgs e)


{
MessageBox.Show(e.Location.ToString());

47
}
}
}

Programcı OnXXX fonksiyonlarını override etmezse, Control


sınıfının OnXXX fonksiyonları çağrılır. Control sınıfında, her OnXXX sanal
fonksiyonu için bir XXX isimli event eleman vardır. İşte Control sınıfının
OnXXX fonksiyonu default davranış olarak, XXX event elemanına ilişkin
fonksiyonları çağırmaktadır. Bu durumda programcı mesajları, XXX event
elemanına fonksiyon girerek de işleyebilir. Bu durumda mesaj işlemek için
3 durum söz konusudur:

1- OnXXX sanal fonksiyonunu override etmek


2- XXX event elemanına fonksiyon girmek
3- 1 ve 2 yöntemi birlikte uygulamak.

Eğer programcı OnXXX sanal fonksiyonunu override ederse, XXX


event elemanı artık tetiklenmez. Çünkü bu event elemanını tetikleyen,
Control sınıfının OnXXX fonksiyonudur. Ayrıca event elemanının da
tetiklenmesi isteniyorsa, programcının override ettiği OnXXX fonksiyonu
içerisinde, base.OnXXX(...) çağırması ile Control sınıfının OnXXX
fonksiyonunu çağırmasını gerekir.

OnXXX fonksiyonlarının hepsinin geri dönüş değeri void türdendir.


Anımsanacağı gibi event elemanlar kısıtlanmış delege elemanlardır. Yani
event elemanlar delege türdendir. İşte XXX isimli event eleman genel
olarak YYYEventHandler isimli delege türdendir. Bütün YYYEventHandler
isimli delege sınıflarının iki parametresi vardır. Birinci parametre object
türündendir ve Control sınıfının OnXXX fonksiyonu, bu parametreye
mesaja konu olan nesnenin referansını geçirir. İkinci parametre mesaj
parametre sınıfın türündendir. Bu parametre yine mesaj parametrelerini
kodlamaktadır.

Örneğin; Control sınıfının, OnMouseDown fonksiyonunun MouseDown


isimli event elemanını nasıl tetiklediği aşağıdaki gibi gösterilebilir:

class Control
{
//...
protected virtual void OnMouseDown(MouseEventArgs mea)
{
MouseDown(this, mea);
}
//...
}

48
Özetle Application.Run fonksiyonu OnXXX fonksiyonunu
çağırmakta, Control sınıfının OnXXX fonksiyonu da XXX event elemanını
tetiklemektedir. Şüphesiz OnXXX fonksiyonunun parametresi Windows’un
kuyruğa bıraktığı mesajı alan Application.Run tarafından
oluşturulmaktadır.

Örneğin; click mesajı için çağrılan OnXXX fonksiyonu Click event


elemanını tetikler. Click event elemanı EventHandler isimli bir delege
türündendir. EventHandler delegenin mesaj parametre sınıfı EventArgs
sınıfıdır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "Message Sample";

base.Click += new EventHandler(ClickHandler);


}

private void ClickHandler(object o, EventArgs e)


{
MessageBox.Show("Click");
}
}
}

EventHandler isimli delegenin mesaj parametre sınıfı EventArgs sınıfıdır.


Yani EventHandler delegesi programcıya ek bir bilgi vermemektedir.

TEMEL MESAJLAR
Fare Mesajları:

49
En temel fare mesajı Click mesajıdır. Çağrılan sanal fonksiyon
OnClick, ilgili event eleman Click ismindedir. Mesaj parametre sınıfı
EventArgs sınıfındandır. Click event elemanı EventHandler delege
türündendir. Click mesajı bize hangi noktaya click yapıldığı bilgisini
vermez. Farenin aktif tuşu etkilidir.

MouseDown isimli mesaj farenin herhangi bir tuşuna basıldığında


oluşturulur. Çağrılan sanal fonksiyon OnMouseDown fonksiyonudur. Mesaj
parametre sınıfı MouseEventArgs türündendir. MouseDown event elemanı
MouseEventHandler isimli delege türündendir. Bu mesaj bize farenin
hangi tuşuna basıldığı ve hangi noktaya click yapıldığı bilgisini de
vermektedir.

MouseUp isimli mesaj, el farenin tuşundan çekildikten sonra


oluşturulmaktadır. Çağrılan sanal fonksiyon OnMouseUp fonksiyonudur. İlgili
event eleman MouseUp isimli elemandır. Mesaj parametre sınıfı
MouseEventArgs isimli sınıftır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
base.Text = "Message Sample";
base.MouseUp += new
MouseEventHandler(MouseUpHandler);

private void MouseUpHandler(object o, MouseEventArgs


e)
{
if (e.Button == MouseButtons.Left)
{
MessageBox.Show(e.Location.ToString());
}

50
}
}
}

Fare programın çalışma alanı içerisinde sürüklendiğinde MouseMove


isimli mesaj oluşur. Çağrılan sanal fonksiyon OnMouseMove fonksiyonudur.
Tetiklenen event eleman MouseMove isimli elemandır. Mesaj parametre
sınıfı yine MouseEventArgs sınıfıdır. MouseMove mesajı fare sürüklenirken
her nokta için oluşmak zorunda değildir. O anki sistemin içinde bulunduğu
duruma göre daha seyrek gönderilebilir. Fakat aynı piksel için birden fazla
kez gönderilmesi söz konusu değildir. Programcı çoğu kez fare hareket
ederken hangi tuşa basıldığını da kontrol etmek ister.

Yukarıdaki fare mesajları yalnızca çalışma alanı ile ilgili mesajlardır.


Örneğin; fare pencere başlığına click yapılsa bu mesajlar oluşmamaktadır.

Resize Mesajı:

Pencere boyut değiştirdiğinde Resize isimli mesaj oluşmaktadır.


Çağrılan sanal fonksiyon OnResize fonksiyonudur. İlgili event eleman
Resize isimli elemandır. Mesaj parametre sınıfı EventArgs sınıfıdır. Yani bu
mesaj bize ek bir bilgi vermemektedir. ResizeEvent elemanı,
EventHandler delege türündendir. Tabi ki programcı mesaj oluştuğunda
Control sınıfının elemanlarına bakarak pencerenin yeni boyutları hakkında
bilgi elde edebilir. Denetim masası ayarlarına bağlı olarak Resize mesajı,
boyut değiştirme işleminin sonunda ya da boyut değiştirme süresince
oluşturulabilmektedir. Pencerenin maximize, minimize ve restore durumları
özel durumlardır. Bazen programcı pencere minimize edildiğinde özel bazı
şeyler yapmak isteyebilir.

Sınıf Çalışması:
Control sınıfından bir sınıf türeterek alt pencere için bir sınıf oluşturunuz.
Ana pencerenin içerisinde, bu sınıfı kullanarak kırmızı zemin renkli bir alt
pencere yaratınız. Alt pencere için MouseDown mesajını hem OnMouseDown
mesajını override ederek hem de MouseDown event elemanına fonksiyon
girerek işleyiniz.

Çözüm:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()

51
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
MyControl m_mc;

public MyForm()
{
Text = "Test Child Window";

m_mc = new MyControl();

Controls.Add(m_mc);
}
}

class MyControl : Control


{
public MyControl()
{
Bounds = new Rectangle(100, 100, 100, 100);
BackColor = Color.Red;
MouseDown += new
MouseEventHandler(MyControl_MouseDown);
}

void MyControl_MouseDown(object sender,


MouseEventArgs e)
{
MessageBox.Show("Event click");
}

protected override void OnMouseDown(MouseEventArgs e)


{
MessageBox.Show("OVERRİDE METHOD");

base.OnMouseDown(e);
}
}
}

Sınıf Çalışması:
Bir form içine 5 satır 5 sütun olmak üzere toplam 25 adet rasgele zemin
renklerinden oluşan alt pencere yaratınız. Alt pencerelerin hepsi Control
sınıfından türetilen MyControl isimli sınıftan yaratılacaktır. Sonra alt
pencerelerin her biri için text propertysine 1’den 25’e kadar sayı

52
yerleştiriniz. Daha sonra click mesajını işleyiniz. Click mesajı oluştuğunda
alt pencere text propertysini MessageBox ile yazdırınız.

Çözüm:
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
public MyForm()
{
Text = "Test Child Window";
Random r = new Random();

for (int i = 0; i < 25; ++i)


{
MyControl mc = new MyControl();
mc.Location = new Point(i % 5 * 50, 0 + i / 5
* 50);
mc.Size = new Size(50, 50);
mc.BackColor = Color.FromArgb(r.Next(256),
r.Next(256), r.Next(256));
mc.Text = i.ToString();
base.Controls.Add(mc);
}
}
}

class MyControl : Control


{
public MyControl()
{
base.Click += new EventHandler(MyControl_Click);
}

void MyControl_Click(object sender, EventArgs e)


{
MessageBox.Show(base.Text);
}
}

53
}

{01 Aralık 2007 Cumartesi}

TEMEL WİNDOWS KONTROLLERİ

GUI programlarında sıklıkla gördüğümüz düğmeler, edit alanları,


listeleme kutuları gibi alt pencerelere temel kontroller denir.

—Düğme Kontrolleri:
Düğmeler(push buttons), fare ile tıklandığında basılma hareketi
yapan, el fareden çekildiğinde çekilme hareketi yaparak bir olayı başlatan
en popüler kontrollerdendir. Genellikle bir hareketi başlatmak ya da
sonlandırma için kullanılır. .Nette düğme kontrolü Button sınıfı ile temsil
edilmektedir.

Seçenek kutuları (check boxes) ve radio(radio button) düğmeleri de


bir çeşit düğmedir. Normal düğmeler ve bu düğmelerin bazı elemanları
ortak olduğu için ortak elemanlar bir taban sınıfta toplanmıştır.

Control

ButtonBase

Ortak elemanlar ButtonBase sınıfında toplanmıştır.


Button CheckBox RadioButton
Nesne Button sınıfının default başlangıç fonksiyonu ile yaratılır.
Kontrol, üst pencerenin alt pencere listesine bağlandığında yaratılır.
Düğmenin üzerindeki yazı Text propertysi ile belirtilir. Kontrolün default
konumu 0,0 noktasıdır. Genişlik yükseklik ayarlaması yapılmazsa pencere
başlık yazısına bakılarak default bir değer alır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

54
class MyForm : Form
{
private Button m_buttonOK;
private Button m_buttonCancel;

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(50, 100);

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Cancel";
m_buttonCancel.Location = new Point(140, 100);

base.Controls.AddRange(new Control[]
{ m_buttonOK, m_buttonCancel });
}
}
}

Button sınıfının Control sınıfından gelen Click event elemanı, el fareden


çekildiğinde tetiklenmektedir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private Button m_buttonOK;
private Button m_buttonCancel;

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();

55
m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(50, 100);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Cancel";
m_buttonCancel.Location = new Point(140, 100);
m_buttonCancel.Click += new
EventHandler(buttonCancelClickHandler);

base.Controls.AddRange(new Control[]
{ m_buttonOK, m_buttonCancel });
}

private void buttonOkClickHandler(object o, EventArgs


e)
{
MessageBox.Show("Ok");
}

private void buttonCancelClickHandler(object o,


EventArgs e)
{
MessageBox.Show("Cancel");
}
}
}
Düğmenin zemin rengini Control sınıfından gelen BackColor propertysi ile
yazının rengini ForeColor propertysi ile değiştirebiliriz.

Anahtar Notlar

Control sınıfının BackColor ve ForeColor property elemanlarına atama


yapılmazsa, bunların default değerleri üst pencereden alınmaktadır.
Bunlara .Net terminolojisinde “ambient propertry” denmektedir.
Örneğin; biz düğme kontrolünün zemin rengine atama yapmazsak
düğmenin zemin rengi, üst pencere olan Form‘un zemin rengi ile aynı
olacaktır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{

56
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;
private Button m_buttonCancel;

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(50, 100);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);
m_buttonOK.BackColor = Color.Cyan;
m_buttonOK.ForeColor = Color.Red;

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Cancel";
m_buttonCancel.Location = new Point(140, 100);
m_buttonCancel.Click += new
EventHandler(buttonCancelClickHandler);
m_buttonCancel.BackColor = Color.Cyan;
m_buttonCancel.ForeColor = Color.Red;

base.Controls.AddRange(new Control[] { m_buttonOK,


m_buttonCancel });
}

private void buttonOkClickHandler(object o, EventArgs


e)
{
MessageBox.Show("Ok");
}

private void buttonCancelClickHandler(object o,


EventArgs e)
{
MessageBox.Show("Cancel");
}
}
}

Anahtar Notlar

57
Control sınıfının bool türden Enabled isimli propertry elemanı,
read/write elemandır. Bu propertynin default değeri true’dur. Yani
pencere aktif durumdadır. Bu propertye False değeri geçirilerek pencere
pasif duruma getirilebilir. Bir pencere pasif durumda iken, Windows
pencereye fare ve klavye mesajlarının yollamaz. Böylece örneğin düğme
kontrolü pasif hale getirilirse düğmeye tıklamak bir sonuca yol açmaz.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;
private Button m_buttonCancel;

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();


m_buttonOK.Text = "&Start";
m_buttonOK.Location = new Point(50, 100);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);
m_buttonOK.ForeColor = Color.Red;
m_buttonOK.Enabled = true;

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Stop";
m_buttonCancel.Location = new Point(140, 100);
m_buttonCancel.Click += new
EventHandler(buttonCancelClickHandler);
m_buttonCancel.ForeColor = Color.Red;
m_buttonCancel.Enabled = false;

base.Controls.AddRange(new Control[] { m_buttonOK,


m_buttonCancel });
}

58
private void buttonOkClickHandler(object o, EventArgs
e)
{
m_buttonOK.Enabled = false;
m_buttonCancel.Enabled = true;
}

private void buttonCancelClickHandler(object o,


EventArgs e)
{
m_buttonCancel.Enabled = false;
m_buttonOK.Enabled = true;
}
}
}

Anahtar Notlar

Bir pencere Control sınıfının Visible propertysi ile ya da Show


fonksiyonu ile görünür ya da görünmez yapılabilir. Pencere görünmez
yapıldığında pencerenin bütün varlığı devam etmektedir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;
private Button m_buttonCancel;

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();


m_buttonOK.Text = "&Start";
m_buttonOK.Location = new Point(50, 100);

59
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);
m_buttonOK.Visible = true;

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Stop";
m_buttonCancel.Location = new Point(140, 100);
m_buttonCancel.Click += new
EventHandler(buttonCancelClickHandler);
m_buttonCancel.Visible = false;

base.Controls.AddRange(new Control[] { m_buttonOK,


m_buttonCancel });
}

private void buttonOkClickHandler(object o, EventArgs


e)
{
m_buttonOK.Visible = false;
m_buttonCancel.Visible = true;
}

private void buttonCancelClickHandler(object o,


EventArgs e)
{
m_buttonCancel.Visible = false;
m_buttonOK.Visible = true;
}
}
}

Bir düğmenin üzerine, zemini oluşturan bir resim yerleştirilebilir. Yani


düğmenin zemini bir resim olabilir. Fakat bu konu ileride ele alınacaktır.

Button sınıfının ButtonBase sınıfından gelen TextAling isimli


property elemanı ContentAligment isimli bir enum türündendir. Bu enum
türünün dokuz elemanı, dikdörtgensel hizalamaya ilişkindir. Yazı default
olarak kontrolün ortasına basıldığına göre, bu propertynin default biçimi
MiddleCenter biçimindedir.

Button sınıfının FlatSyle isimli elemanı, düğmenin görüntü biçimi


üzerinde etkili olur.

public MyForm()
{
base.Text = "Sample buttons";

m_buttonOK = new Button();


m_buttonOK.Text = "&Start";

60
m_buttonOK.Location = new Point(50, 100);
m_buttonOK.Size = new Size(100, 100);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);
m_buttonOK.Visible = true;
m_buttonOK.TextAlign =
ContentAlignment.BottomCenter;
m_buttonOK.FlatStyle = FlatStyle.Flat;

m_buttonCancel = new Button();


m_buttonCancel.Text = "&Stop";
m_buttonCancel.Location = new Point(140, 100);
m_buttonCancel.Click += new
EventHandler(buttonCancelClickHandler);
m_buttonCancel.Enabled = false;

base.Controls.AddRange(new Control[]
{ m_buttonOK, m_buttonCancel });
}

Anahtar Notlar

Application sınıfının EnableVisualStyle isimli static fonksiyonu işletim


sistemine bağlı olarak bazı kontrollerin görüntü biçimine stil olarak
değiştirmekte kullanılmaktadır. Örneğin xp ve vista kontrolleri klasik 95
ve 98 görüntüsünden farklıdır. Main fonksiyonu içerisinde bu
fonksiyonun bir kez çağrılması yeterlidir.

Button sınıfının başka yararlı event elemanları olsa da bunlar


üzerinde şu aşamada durulmayacaktır.

—TextBox Kontrolü
TextBox kontrolü, programcının kullanıcıdan yazısal bir giriş elde
etmesini sağlamaktadır. Kontrol bir alt pencere oluşturur. Kullanıcı bir edit
alanı olarak bu alt pencereye yazıyı girer sonra programcı girilen yazıyı
elde eder. TextBox kontrolü TextBox sınıfı ile temsil edilmektedir.

TextBox kontrolünün benzer biçimde kullanılan kardeş kontrolleri


vardır. Bu kontroller TextBoxBase isimli bir sınıftan türetilerek, ortak
elemanlar bu sınıfta toplanmıştır.

Control

TextBoxBase

TextBox RichTextBox MaskedTextBox


61
Anahtar Notlar

Nesne yönelimli programlama tekniğinde, bazı sınıfların ortak elemanları


varsa bu ortak elemanlar bir sınıfta toplanmalı ve diğer sınıflar bu
sınıftan türetilmelidir. TextBox sınıfı türünden bir nesne, default
başlangıç fonksiyonu ile yaratılır. Sınıfın Control sınıfından gelen Text
property elemanı, TextBox içerisindeki yazıyı temsil eder. TextBox
kontrolü default olarak belli bir büyüklükte yaratılır. Location propertysi
default 0, 0 olduğu için konumlandırma yapılmalıdır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private TextBox m_textBox;
private Button m_buttonOK;

public MyForm()
{
m_textBox = new TextBox();
m_textBox.Text = "Bu yazı kontrolde
görüntülenecek";
m_textBox.Location = new Point(50, 100);
m_textBox.Size = new Size(200, 30);

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(100, 150);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

base.Controls.Add(m_textBox);
base.Controls.Add(m_buttonOK);

62
}

private void buttonOkClickHandler(object o, EventArgs


e)
{
MessageBox.Show(m_textBox.Text);
}
}
}

TextBox sınıfının TextAling isimli property elemanı


HorizontalAlingment isimli bir Aling türündendir. Yatay hizalama için
kullanılır. Default durum Left biçimindedir.

public MyForm()
{
m_textBox = new TextBox();
m_textBox.Text = "Bu yazı kontrolde
görüntülenecek";
m_textBox.Location = new Point(50, 100);
m_textBox.Size = new Size(200, 30);
m_textBox.TextAlign = HorizontalAlignment.Right;

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(100, 150);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

base.Controls.Add(m_textBox);
base.Controls.Add(m_buttonOK);
}

TextBox sınıfının CharacterCasing isimli property elemanı


CharacterCasing isimli enum türündendir. Girişin hep büyük harf hep
küçük harf ya da normal biçimde yapılmasını sağlar. Default durum normal
biçimdedir.

public MyForm()
{
m_textBox = new TextBox();
m_textBox.Text = "Bu yazı kontrolde
görüntülenecek";
m_textBox.Location = new Point(50, 100);
m_textBox.Size = new Size(200, 30);
m_textBox.CharacterCasing =
CharacterCasing.Upper;

63
m_buttonOK = new Button();
m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(100, 150);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

base.Controls.Add(m_textBox);
base.Controls.Add(m_buttonOK);
}

TextBox sınıfının TextBoxBase sınıfından gelen ReadOnly isimli


property elemanı, bool türdendir ve kontrole klavye yoluyla girişin
engellenmesi için kullanılır. Bu durumda kontrole klavye yoluyla bir şey
yazılamaz. Fakat Text propertysi yoluyla yazılabilir. Default durum false
biçimindedir.

public MyForm()
{
m_textBox = new TextBox();
m_textBox.Text = "Bu yazı kontrolde
görüntülenecek";
m_textBox.Location = new Point(50, 100);
m_textBox.Size = new Size(200, 30);
m_textBox.CharacterCasing =
CharacterCasing.Upper;
m_textBox.ReadOnly = true;

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(100, 150);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

base.Controls.Add(m_textBox);
base.Controls.Add(m_buttonOK);
}

TextBox sınıfının TextBoxBase sınıfından gelen TextLenght isimli


read only int türden property elemanı, kontrol içerisindeki girilen karakter
sayısını uzunluğunu belirtir. Aslında aynı işlem Text propertysinden elde
edilen yazının uzunluğu belirlenerek de yapılabilir.

TextBox kontrolü, Singleline ya da Multiline olabilir. TextBox


kontrolünü Multiline yapabilmek için TextBox sınıfının bool türden
Multiline propertysi true girilmelidir. Çok satırlı TextBox kontrolü adeta
bir editör gibidir.

64
public MyForm()
{
m_textBox = new TextBox();
m_textBox.Location = new Point(20, 100);
m_textBox.Size = new Size(200, 100);
m_textBox.BackColor = Color.Blue;
m_textBox.ForeColor = Color.Yellow;
m_textBox.Multiline = true;

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(200, 150);
m_buttonOK.Click += new
EventHandler(buttonOkClickHandler);

base.Controls.Add(m_textBox);
base.Controls.Add(m_buttonOK);
}

Anahtar Notlar

Bir alt pencerenin, üst pencerenin çalışma alanını kaplayacak hale


getirilmesi işlemi ile sık karşılaşılmaktadır. Alt pencerenin Size
propertysine, üst pencerenin ClientSize propertysi atanırsa ve alt
pencerenin Location propertysi 0,0 da tutulursa bu sağlanabilir. Ya da
alt pencerenin Bounds propertysine doğrudan üst pencerenin
ClientRectangle propertysi atanabilir. Fakat üst pencerenin boyutunu
değiştirdiğimizde bundan üst pencere etkilenir. Alt pencere etkilenmez.
Bu durumda üst pencerenin Resize mesajında, alt pencere yeniden üst
pencerenin çalışma alanını kaplayacak hale getirilmelidir.

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = base.ClientRectangle;

base.Controls.Add(m_textBox);

private void resizeHandler(object o, EventArgs e)


{
m_textBox.Bounds = base.ClientRectangle;
}

65
TextBox sınıfının bool türden WordWrap isimli property elemanı,
sözcük sarma işlemini gerçekleştirir. Default durum true biçimindedir. Yani
sarma yapılmaktadır.

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = base.ClientRectangle;
m_textBox.WordWrap = false;

base.Controls.Add(m_textBox);
}

TextBox sınıfının TextBoxBase sınıfından gelen Modified isimli bool


türden property elemanı, TextBox içerisindeki yazının değişip değişmediği
bilgisini verir. Pencere yaratıldığında property false durumdadır. Yani
henüz pencere içerisindeki yazıda bir değişiklik yoktur. Pencereye herhangi
bir karakter girildiğinde, bu property otomatik olarak true hale getirilir. Bu
property tipik olarak save mekanizması için düşünülmüştür. Örneğin; save
işleminden sonra property programcı tarafından false değerine çekilir.
Programcı editörden çıkacağı zaman ya da başka bir dokümanı açacağı
zaman bu propertye bakılır. Bu property true değerinde ise değişiklik
yapılmış ve save edilmemiştir.

TextBox sınıfının TextBoxBase sınıfından gelen Lines isimli property


elemanı, string dizisi türündendir. Kontrol içerisindeki yazıları satırlara
bölerek diziye yerleştirir ve bize bu diziyi verir. Bu property read write
propertydir.

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);
base.Click += new EventHandler(MyForm_Click);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = new Rectangle(0, 0, 100, 100);

base.Controls.Add(m_textBox);

66
void MyForm_Click(object sender, EventArgs e)
{
string[] lines = { "Kaan", "Ali", "Necati",
"Güray" };

m_textBox.Lines = lines;
}

{02.12.2007 Pazar }

TextBox sınıfının PasswordChar isimli property elemanı char


türdendir ve parola girilirken görüntülenecek karakteri belirtir. Eğer kontrol
Multiline modda ise parola giriş etkisi oluşturulmaz. Bu propertye “0”
geçirilirse password giriş işlemi sonlandırılır.

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);

m_textBox = new TextBox();


m_textBox.Multiline = false;
m_textBox.Bounds = base.ClientRectangle;
m_textBox.PasswordChar = '*';

base.Controls.Add(m_textBox);
}

Nesne yaratıldığında bu nesnenin içerisinde “0” bulunmaktadır.

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);

m_textBox = new TextBox();


m_textBox.Multiline = false;
m_textBox.Bounds = base.ClientRectangle;
m_textBox.PasswordChar = (char)0;

base.Controls.Add(m_textBox);
}

TextBox sınıfının SelectionStart isimli int türden property


elemanı, programlama yoluyla seçim yapabilmek için bir başlangıç indeks
numarası belirtir. TextBox içerisindeki her bir karakterin, ilk karakter “0”
olmak üzere bir indeks numarası vardır. (Enter karakteri için aslında iki
karakter kullanılmaktadır.)

67
TextBox sınıfının TextBoxBase sınıfından gelen SelectionLenght
isimli property elemanı, belirtilen indeksten itibaren kaç karakterin
seçilmiş olduğunu belirtir. SelectionStart ve SelectionLenght property
elemanlarına değer atayarak, programlama yoluyla seçme işlemi
yaptırabiliriz ya da seçilen bölgenin yerini ve uzunluğunu elde edebiliriz.

Seçilmiş olan yazıyı uzun bir yöntemle aşağıdaki gibi elde edebiliriz:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private TextBox m_textBox;

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);
base.Click += new EventHandler(MyForm_Click);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = new Rectangle(0, 0, 100, 100);

base.Controls.Add(m_textBox);
}

void MyForm_Click(object sender, EventArgs e)


{
string str =
m_textBox.Text.Substring(m_textBox.SelectionStart,
m_textBox.SelectionLength);

MessageBox.Show(str);

}
private void resizeHandler(object o, EventArgs e)

68
{
m_textBox.Bounds = base.ClientRectangle;
}
}
}

Ancak TextBox sınıfının TextBoxBase sınıfından gelen SelectedText isimli


string türden property elemanı, zaten seçilmiş yazıyı yukarıdaki yöntemle
vermektedir. Eğer hiçbir seçim yapılmamışsa bu property boş bir string
vermektedir. SelectedText propertysi read write bir propertydir. Yani bu
propertye değer atanırsa, seçilmiş olan yazı silinerek yerine atanan yazı
gelir. Eğer seçim yoksa, atanan yazı imlecin bulunduğu yere insert edilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private TextBox m_textBox;

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);
base.Click += new EventHandler(MyForm_Click);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = new Rectangle(0, 0, 100, 100);

base.Controls.Add(m_textBox);
}

void MyForm_Click(object sender, EventArgs e)


{
m_textBox.SelectedText = "Savaş";
}
private void resizeHandler(object o, EventArgs e)
{

69
m_textBox.Bounds = base.ClientRectangle;
}
}
}

Seçilmiş alan üzerine Copy, Cut, Paste gibi fonksiyonlar


uygulanabilir. Bu fonksiyonlar sanki klavyeden CTRL+C, CTRL+X, CTRL+V
tuşuna basılmış gibi etki yapar. Paste fonksiyonunun string parametreli
bir biçimi de vardır. Bu biçim SelectedText propertysi ile benzer işlem
yapmaktadır. Yani text alanındaki yazıyı değil parametresi ile belirtilen
yazıyı Paste yapar. Yine TextBox sınıfının TextBoxBase sınıfından gelen
Select fonksiyonları vardır. Bunlar SelectionStart ve SelectionLenght
property işlemlerini fonksiyon biçiminde yapar. Örneğin:

public void Select (int start, int lengt)

Eğer Start parametresine bir değer verilir, Lenght “0” da tutulursa, seçme
işlemi yapılmaz. Bunun yerine imleç belli bir indekse taşınır. Aynı işlem
yalnızca SelectionStart‘a değer verilerek de yapılabilir.

Yine programlama yoluyla TextBox sınıfından gelen Undo fonksiyonu


çağrılarak Undo işlemi yapışabilir.

Chat programlarında rastladığımız çok sık kullanılan bir tema da çok


satırlı edit kontrolünün sonuna yazı eklenmesidir. Örneğin; tipik olarak
programda bir çok satırlı TextBox kontrolü, birde tek satırlı TextBox
kontrolü vardır. Tek satırlı TextBox kontrolünde, yazılan yazı çok satırlı
TextBox kontrolünün sonuna eklenmektedir. Bu işlem aşağıdaki gibi
yapılmamalıdır:

m_multiTextBox.Text = m_SingleTextBox.Text;

Çünkü bu durumda kaydırma çubukları yeniden başa çekilir.

Kontrolde kaydırma çubukları kontrolün ScrollBars propertysine atama


yapılarak çıkartılır. ScrollBars propertysi, ScrollBars enum türündendir.
Fakat çok satırlı TextBox kontrolü, eğer sarma modunda ise yatay
kaydırma çubuğu çıkmaz. Çok satırlı edit kontrolünde kaydırma çubukları
ile sona ekleme yapabilmek için Paste işlemi yapmak gerekir. Yani imleç
yazının sonuna çekilip ekleme yapılmalıdır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD

70
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private TextBox m_single;
private TextBox m_multi;
private Button m_buttonOK;

public MyForm()
{
base.Text = "Sample Chat";
base.Size = new Size(600, 350);
base.FormBorderStyle = FormBorderStyle.Fixed3D;

m_single = new TextBox();


m_single.Bounds = new Rectangle(30, 250, 530, 30);

m_multi = new TextBox();


m_multi.Bounds = new Rectangle(30, 20, 530, 210);
m_multi.Multiline = true;
m_multi.ReadOnly = true;
m_multi.BackColor = SystemColors.Window;
m_multi.ScrollBars = ScrollBars.Vertical;

m_buttonOK = new Button();


m_buttonOK.Location = new Point(260, 280);
m_buttonOK.Text = "&Send";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

base.Controls.AddRange(new Control[] { m_single,


m_buttonOK, m_multi});
}

void m_buttonOK_Click(object sender, EventArgs e)


{
m_multi.SelectionStart = m_multi.TextLength;
m_multi.SelectedText = m_single.Text + "\r\n";
m_single.Text = "";
m_single.Focus();
}
}
}

71
MessageBox SINIFI

MessageBox sınıfının Show static fonksiyonu ekrana mesaj penceresi


çıkartmaktadır. Bir mesaj penceresinde dört öğe vardır:

1- Pencere Başlık Yazısı


2- Pencerenin İçerisine Yazılacak Yazı
3- Tuş Takımı
4- İkonik Görüntü

Tek parametreli Show fonksiyonu yalnızca iç yazıyı belirler. Default tuş


takımı OK‘dir. İki parametreli Show fonksiyonu, iç yazıyı ve pencere başlık
yazısını belirtir. En genel Show fonksiyonu aşağıdaki gibidir:

public static DialogResult Show (string text, string caption,


MessageBoxButton buttons, MessageButtonIcaon icon)

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private TextBox m_textBox;

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);
base.Click += new EventHandler(MyForm_Click);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = new Rectangle(0, 0, 100, 100);
m_textBox.PasswordChar = (char) 0;
m_textBox.ScrollBars = ScrollBars.Both;
m_textBox.WordWrap = false;

72
base.Controls.Add(m_textBox);
}

void MyForm_Click(object sender, EventArgs e)


{
MessageBox.Show("İç yazı", "Caption",
MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
}

private void resizeHandler(object o, EventArgs e)


{
//m_textBox.Bounds = base.ClientRectangle;
}
}
}
Show fonksiyonlarının geri dönüş değeri DialogResult isimli bir enum
türündendir. Mesaj penceresinden hangi tuşla çıkıldığını belirtir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private TextBox m_textBox;

public MyForm()
{
base.Text = "Sample";
base.Resize += new EventHandler(resizeHandler);
base.Click += new EventHandler(MyForm_Click);

m_textBox = new TextBox();


m_textBox.Multiline = true;
m_textBox.Bounds = new Rectangle(0, 0, 100, 100);
m_textBox.PasswordChar = (char) 0;
m_textBox.ScrollBars = ScrollBars.Both;
m_textBox.WordWrap = false;

base.Controls.Add(m_textBox);
}

73
void MyForm_Click(object sender, EventArgs e)
{
DialogResult dr = MessageBox.Show("İç yazı",
"Caption", MessageBoxButtons.YesNo,
MessageBoxIcon.Exclamation);

if (dr == DialogResult.Yes)
MessageBox.Show("Yes");
else
MessageBox.Show("No");
}

private void resizeHandler(object o, EventArgs e)


{
//m_textBox.Bounds = base.ClientRectangle;
}
}
}

ColorDialog PENCERESİ

Programlarda renk seçtirmek için ColorDialog isimli, Windows’un


içerisinde bulunan bir dialog penceresi bulunmaktadır. Bu pencere
ColorDialog sınıfı ile temsil edilmiştir. ColorDialog sınıfının kullanılması
oldukça basittir. Programcı ColorDialog türünden bir nesne yaratır. Bu
nesne ile ShowDialog fonksiyonunu çağırır. ShowDialog fonksiyonunun
geri dönüş değeri DialogResult türünden bir enumdur. Seçilmiş olan
rengi, sınıfın Color türünden Color isimli property elemanı ile elde eder.
Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private Button m_buttonOK;

public MyForm()

74
{
base.Text = "Choose a color!";

m_buttonOK = new Button();


m_buttonOK.Location = new Point(100, 100);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);
m_buttonOK.Parent = this;
}
void m_buttonOK_Click(object sender, EventArgs e)
{
ColorDialog cd = new ColorDialog();

if (cd.ShowDialog() == DialogResult.OK)
{
base.BackColor = cd.Color;
}
}
}
}

ColorDialog sınıfının bool türden AllowFullOpen isimli property elemanı,


renk dialog penceresinin ayrıntılı seçim yapılan bölümünün, görüntülenip
görüntülenmeyeceğini belirtir. Bu propertynin default durum true’dir. Yani
özel renk seçilebilmektedir.

ColorDialog sınıfının bool türden FullOpen isimli property elemanı, ilk


açılışta renk tanımlama penceresinin açılıp açılmayacağını belirtir.

void m_buttonOK_Click(object sender, EventArgs e)


{
ColorDialog cd = new ColorDialog();
cd.FullOpen = true;

if (cd.ShowDialog() == DialogResult.OK)
{
base.BackColor = cd.Color;
}
}

Sınıfın SolidColorOnly isimli bool türden property elemanı, yalnızca ana


renklerin seçilmesine izin verir. Bu propertynin default durumu false
biçimindedir.

void m_buttonOK_Click(object sender, EventArgs e)


{
ColorDialog cd = new ColorDialog();
cd.FullOpen = true;

75
cd.SolidColorOnly = true;

if (cd.ShowDialog() == DialogResult.OK)
{
base.BackColor = cd.Color;
}
}

Sınıfın int [] türünden CustomColors isimli property elemanı, tanımlanan


renkleri alıp geri set etmede kullanılabilir. Örneğin; tipik olarak dialog
penceresinde bir takım renk tanımlamaları yapılmış olsun pencere yeniden
açıldığında aynı renklerin gözükmesi isteniyorsa, bunların saklanarak geri
alınması gerekir. Bir int değer 4 byte olduğu için ARGB değerlerini
tutabilmektedir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;

public MyForm()
{
base.Text = "Choose a color!";

m_buttonOK = new Button();


m_buttonOK.Location = new Point(100, 100);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(buttonClickHandler);
m_buttonOK.Parent = this;
}
private int[] m_custColors = new int[16];
void buttonClickHandler(object sender, EventArgs e)
{
ColorDialog cd = new ColorDialog();

cd.CustomColors = m_custColors;

76
if (cd.ShowDialog() == DialogResult.OK)
{
base.BackColor = cd.Color;
m_custColors = cd.CustomColors;
}
}
}
}

Anahtar Notlar

Color yapısının ToArgb fonksiyonu yapının tuttuğu rengin int karşılığını


geri döndürür.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;

public MyForm()
{
base.Text = "Choose a color!";

m_buttonOK = new Button();


m_buttonOK.Location = new Point(100, 100);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(buttonClickHandler);
m_buttonOK.Parent = this;
}

private int[] m_custColors = new int[16] {


Color.White.ToArgb(), Color.Yellow.ToArgb(),
Color.White.ToArgb(),
Color.White.ToArgb(), Color.White.ToArgb(),
Color.White.ToArgb(), Color.White.ToArgb(),

77
Color.White.ToArgb(), Color.White.ToArgb(),
Color.White.ToArgb(), Color.White.ToArgb(),
Color.White.ToArgb(), Color.White.ToArgb(),
Color.White.ToArgb(), Color.White.ToArgb(),
Color.White.ToArgb()
};
void buttonClickHandler(object sender, EventArgs e)
{
ColorDialog cd = new ColorDialog();

cd.CustomColors = m_custColors;

if (cd.ShowDialog() == DialogResult.OK)
{
base.BackColor = cd.Color;
m_custColors = cd.CustomColors;
}
}
}
}

SEÇENEK KUTUSU KONTROLÜ


Seçenek kutusu basit bir alt penceredir. Kontrol küçük bir dikdörtgen ve
bir yazıdan oluşur. CheckBox sınıfı ile temsil edilmektedir. Bu sınıfta
ButtonBase sınıfından türetilmiştir
Programcı CheckBox sınıfı türünden bir nesneyi, default başlangıç
fonksiyonu ile yaratır. Kontrolün Text propertysini set eder. Seçenek
kutusu default olarak Auto durumdadır. Yani kontrolün üzerine
tıklandığında çarpılama işlemi ve çarpılmanın kaldırılması otomatik olarak
yapılmaktadır. Programcı genellikle form kapatıldığında kontrolün
çarpılanıp çarpılanmadığını belirlemek ister. Sınıfın bool türeden Checked
property elemanı read write propertydir. Hem çarpılama durumunu elde
etmek için hem de çarpılama yapmak için kullanılır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form

78
{
private CheckBox m_checkBox;

public MyForm()
{
m_checkBox = new CheckBox();
m_checkBox.Location = new Point(100, 100);
m_checkBox.Text = "Kabul mü?";
m_checkBox.Parent = this;
}
}
}

CheckBox sınıfının TextAling isimli property elemanı yazıyı kutuya göre


hizalamaktadır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private CheckBox m_checkBox;

public MyForm()
{
m_checkBox = new CheckBox();
m_checkBox.Location = new Point(100, 100);
m_checkBox.Size = new Size(100, 100);
m_checkBox.Text = "Kabul mü?";
m_checkBox.TextAlign = ContentAlignment.TopLeft;
m_checkBox.Parent = this;
}
}
}

LABEL KONTROLÜ

79
Label kontrolü en basit alt penceredir. Label sınıfı ile temsil edilmiştir. Bu
alt pencerenin amacı bir yazıyı göstermektir. Label türünden bir nesne,
default başlangıç fonksiyonu ile yaratılır. Sonra Text propertysine değer
atanarak gösterilecek yazı belirlenir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private CheckBox m_checkBox;
private Label m_label;

public MyForm()
{
m_checkBox = new CheckBox();
m_checkBox.Location = new Point(100, 100);
m_checkBox.Size = new Size(100, 100);
m_checkBox.Text = "Kabul mü?";
m_checkBox.TextAlign = ContentAlignment.TopLeft;

m_label = new Label();


m_label.Text = "jhgjhgjhgjhgjhgj";
m_label.Parent = this;
m_checkBox.Parent = this;
}
}
}

using System.IO;

namespace CSD
{
class App
{
public static void Main()
{

80
FileInfo fi = new
FileInfo(@"E:\CSCUPA\Sample\sample.cs");

if ((fi.Attributes & FileAttributes.Archive) ==


FileAttributes.Archive)
System.Console.WriteLine("Evet");
else
System.Console.WriteLine("Hayır");
}
}
}

RADIO DÜĞMESİ KONTROLÜ


{08.12.2007 Cumartesi}

Radio düğmesi kontrolü RadioButton sınıfı ile temsil edilmiştir.


Normal düğmeler, seçenek kutuları(CheckBox) ve radio düğmeleri birer
düğme kontrolüdür ve hepsinin ortak elemanları vardır. Ortak elemanlar
ButtonBase sınıfında toplanmıştır.

Control

ButtonBase

Bir RadioButton
radyo düğmesi kontrolü, bir alt pencerenin
Button içerisinde bir
CheckBox
yuvarlakçık ve onun yanında bir yazı ile temsil edilmiştir. Tıpkı seçenek
kutusu kontrolünde olduğu gibi, otomatik versiyonları vardır. Default
durum otomatik olmasıdır. Otomatik radio düğmesi kontrolünde, bir radio
düğmesine tıklandığında daha önce tıklanmış olanın çarpısı otomatik
olarak kaldırılır. Tüm kardeş radio düğmesi pencereleri, kendi aralarında
bir grup oluşturmaktadır. Grup içerisindeki bir radio düğmesine
tıklandığında, daha önce çarpısı olanın çarpısı kaldırılır. Bu durumda aynı
form üzerinde iki farklı radio düğmesi grubu oluşturmak için, mecburen
sözde bir formun alt penceresi olacak bir pencere yaratmak gerekir. Grubu
oluşturacak radio düğmeleri, bu sözde alt pencere içerisinde yaratılır. Bu
amaçla kullanılan en yaygın alt pencere GroupBox isimli kontroldür.

Tipik bir radyo düğmesi çalışması şöyle yapılır:

1- Programcı radyo düğmelerini RadioButton sınıfının başlangıç


fonksiyonu ile yaratır.
2- Artık çarpılama otomatik olarak gerçekleştirilir.
3- RadioButton sınıfının bool türden, Checked isimli read/write
property elemanı, hem programlama yoluyla çarpılama yapmak için

81
hem de ilgili radio düğme kontrolünün çarpılanıp çarpılanmadığını
anlamak için kullanılır. Programcının tek tek tüm radyo düğmelerine
check sorgulaması yapması gerekir. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private RadioButton m_buttonA;
private RadioButton m_buttonB;
private RadioButton m_buttonC;
private RadioButton m_buttonD;
private RadioButton m_buttonE;

private Button m_buttonOK;

public MyForm()
{
m_buttonA = new RadioButton();
m_buttonA.Text = "&A";
m_buttonA.Location = new Point(0, 20);

m_buttonB = new RadioButton();


m_buttonB.Text = "&B";
m_buttonB.Location = new Point(0, 40);

m_buttonC = new RadioButton();


m_buttonC.Text = "&C";
m_buttonC.Location = new Point(0, 60);

m_buttonD = new RadioButton();


m_buttonD.Text = "&D";
m_buttonD.Location = new Point(0, 80);

m_buttonE = new RadioButton();


m_buttonE.Text = "&E";
m_buttonE.Location = new Point(0, 100);

82
m_buttonOK = new Button();
m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(120, 45);
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

this.Controls.AddRange(new Control[]
{ m_buttonA, m_buttonB, m_buttonC,
m_buttonD, m_buttonE, m_buttonOK });

void m_buttonOK_Click(object sender, EventArgs e)


{
string str;
if (m_buttonA.Checked)
str = "A checked";
else if (m_buttonB.Checked)
str = "B checked";
else if (m_buttonC.Checked)
str = "C checked";
else if (m_buttonD.Checked)
str = "D checked";
else if (m_buttonE.Checked)
str = "E checked";
else
str = "No button checked";

MessageBox.Show(str); // her else de çağrılacağına


bir defa çağrılır.
}
//...
}
}

Aynı işlem şöyle de yapılabilir:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}

83
}

class MyForm : Form


{
private RadioButton m_buttonA;
private RadioButton m_buttonB;
private RadioButton m_buttonC;
private RadioButton m_buttonD;
private RadioButton m_buttonE;

private Button m_buttonOK;

public MyForm()
{
m_buttonA = new RadioButton();
m_buttonA.Text = "&A";
m_buttonA.Location = new Point(0, 20);

m_buttonB = new RadioButton();


m_buttonB.Text = "&B";
m_buttonB.Location = new Point(0, 40);

m_buttonC = new RadioButton();


m_buttonC.Text = "&C";
m_buttonC.Location = new Point(0, 60);

m_buttonD = new RadioButton();


m_buttonD.Text = "&D";
m_buttonD.Location = new Point(0, 80);

m_buttonE = new RadioButton();


m_buttonE.Text = "&E";
m_buttonE.Location = new Point(0, 100);

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(120, 45);
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

this.Controls.AddRange(new Control[]
{ m_buttonA, m_buttonB, m_buttonC,
m_buttonD, m_buttonE, m_buttonOK });

void m_buttonOK_Click(object sender, EventArgs e)


{
RadioButton rb;
foreach (Control c in this.Controls)

84
{
rb = c as RadioButton;
if (rb != null && rb.Checked)
{
MessageBox.Show(rb.Text + " Checked");
break;
}
}
}

//...
}
}

Bazen bir radio düğmesine tıklama yapıldığında, otomatik bazı


işlemlerin de yapılması gerekebilir. Bunun RadioButton sınıfının
EventHandler delege türünden, CheckedChange event elemanı kullanılır.
İlgili radio düğmesi kontrolünde, çarpılama oluşmuş ya da oluşan
çarpılama kaldırılmışsa bu event tetiklenir. Kontrolün Control sınıfından
gelen Click event elemanı da bu amaçla kullanılabilir. Fakat Click event
elemanı, çarpılama değişse de değişmese de tetiklenmektedir. Oysa
CheckedChange event elemanı Check propertysinin değeri değiştiğinde
tetiklenir. Programcı tipik olarak tüm radio düğmelerinin CheckedChange
event elemanına aynı delege fonksiyonunu girebilir ve kontrolü tek
yerden yapabilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private RadioButton m_buttonA;
private RadioButton m_buttonB;
private RadioButton m_buttonC;
private RadioButton m_buttonD;
private RadioButton m_buttonE;

85
private Button m_buttonOK;

public MyForm()
{
m_buttonA = new RadioButton();
m_buttonA.Text = "&A";
m_buttonA.Location = new Point(0, 20);
m_buttonA.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonB = new RadioButton();


m_buttonB.Text = "&B";
m_buttonB.Location = new Point(0, 40);
m_buttonB.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonC = new RadioButton();


m_buttonC.Text = "&C";
m_buttonC.Location = new Point(0, 60);
m_buttonC.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonD = new RadioButton();


m_buttonD.Text = "&D";
m_buttonD.Location = new Point(0, 80);
m_buttonD.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonE = new RadioButton();


m_buttonE.Text = "&E";
m_buttonE.Location = new Point(0, 100);
m_buttonE.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(120, 45);
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

this.Controls.AddRange(new Control[]
{ m_buttonA, m_buttonB, m_buttonC,
m_buttonD, m_buttonE, m_buttonOK });

private void checkedChangeHandler(object sender,


EventArgs e)
{
RadioButton rb = (RadioButton)sender;

86
if (rb.Checked)
{
MessageBox.Show(rb.Text + " Checked");
}
}

void m_buttonOK_Click(object sender, EventArgs e)


{
RadioButton rb;
foreach (Control c in this.Controls)
{
rb = c as RadioButton;
if (rb != null && rb.Checked)
{
MessageBox.Show(rb.Text + " Checked");
break;
}
}
}
//...
}
}

GroupBox KONTROLÜ

GroupBox kontrolünün temel amacı, radio düğmeleri gibi gruplu


çalışan kontrollere üst pencerelik yapmaktır. Fakat kontrol bir takım başka
kontrollere ev sahipliği yapan mantıksal ve görsel grup oluşturma amacıyla
da kullanılabilir. GroupBox kontrolü, bir çerçeve oluşturarak gruplandırma
yapmaktadır. GroupBox kontrolü, sınıfının default başlangıç fonksiyonu ile
yaratılır ve konumlandırma yapılır. Sınıfın Text propertysine atama
yapılarak okunabilirlik attırılabilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());

87
}
}

class MyForm : Form


{
private GroupBox m_groupBox;
private RadioButton m_buttonA;
private RadioButton m_buttonB;
private RadioButton m_buttonC;
private RadioButton m_buttonD;
private RadioButton m_buttonE;

private Button m_buttonOK;

public MyForm()
{
m_groupBox = new GroupBox();
m_groupBox.Text = "Answer";
m_groupBox.Location = new Point(0, 100);
m_groupBox.Size = new Size(200, 140);

m_buttonA = new RadioButton();


m_buttonA.Text = "&A";
m_buttonA.Location = new Point(0, 20);
m_buttonA.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonB = new RadioButton();


m_buttonB.Text = "&B";
m_buttonB.Location = new Point(0, 40);
m_buttonB.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonC = new RadioButton();


m_buttonC.Text = "&C";
m_buttonC.Location = new Point(0, 60);
m_buttonC.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonD = new RadioButton();


m_buttonD.Text = "&D";
m_buttonD.Location = new Point(0, 80);
m_buttonD.CheckedChanged += new
EventHandler(checkedChangeHandler);

m_buttonE = new RadioButton();


m_buttonE.Text = "&E";
m_buttonE.Location = new Point(0, 100);
m_buttonE.CheckedChanged += new
EventHandler(checkedChangeHandler);

88
m_buttonOK = new Button();
m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(120, 45);
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_groupBox.Controls.AddRange(new Control[]
{m_buttonA, m_buttonB, m_buttonC, m_buttonD, m_buttonE});

this.Controls.AddRange(new Control[] { m_groupBox,


m_buttonOK });
}
private void checkedChangeHandler(object sender,
EventArgs e)
{
RadioButton rb = (RadioButton)sender;

if (rb.Checked)
{
MessageBox.Show(rb.Text + " Checked");
}
}

void m_buttonOK_Click(object sender, EventArgs e)


{
RadioButton rb;
foreach (Control c in m_groupBox.Controls)// 
m_groupBox.Controls eklendi Controls e değil
{
rb = c as RadioButton;
if (rb != null && rb.Checked)
{
MessageBox.Show(rb.Text + " Checked");
break;
}
}
}
//...
}
}

LİSTELEME KUTULARI

Listeleme kutuları bir grup elemanı yerleştirip görsel bakımdan


listelemek için kullanılır. Kontrol ListBox sınıfı ile temsil edilmektedir.
ListBox kontrolü, sınıfın default başlangıç fonksiyonu ile yaratılır.
Programcı kontrole eleman eklemek için sınıfın Items isimli collection
propertysini kullanılır. Items isimli collection eleman, object
collection türdendir.

89
public ObjectCollection Items {get; }

ObjectCollection ListBox sınıfının içerisinde bildirilmiş bir sınıftır.

Anahtar Notlar

Bir sınıfının içerisinde başka bir sınıf bildirimi yapılabilir. Bu durumda iç


ve dış sınıflar tamamen bağımsız sınıflar gibi değerlendirilir. İç sınıfı
dışarıdan kullanabilmek için dış sınıf ismi ile kombine etmek gerekir.
Ayrıca iç sınıf dış sınıfın private bölümüne erişebilir.

ObjectCollection sınıfı (ismi üzerinde), object veri tutan bir


collection sınıftır. IList arayüzünü desteklemektedir. Adeta ArrayList
sınıfına benzemektedir. Bir listeleme kutusu kontrolü, adeta bir
collection gibi düşünülmelidir. Yani biz ListBox kontrolüne herhangi bir
nesneyi ekleyebiliriz. ListBox kontrolü nesneyi ekler. Fakat eklenen
nesneye ToString fonksiyonunu uygulayarak elde ettiği yazıyı eleman
olarak gösterir. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(100, 50, 100, 200);

m_lb.Items.Add("Ankara");
m_lb.Items.Add("İzmir");
m_lb.Items.Add("Eskişehir");
m_lb.Items.Add(123);
m_lb.Items.Add(new Point(100, 200));

90
m_lb.Parent = this;
}
}
}

Burada listeleme kutusu üç string, bir int, bir de Point tutmaktadır.


Listeleme kutusu elemanlarında bu nesnelere ToString uygulayarak elde
ettiği yazıları gösterir.

Listeleme kutusuna çok benzeyen seçimli listeleme


kutusu(ComboBox) denilen bir kontrol de vardır. Seçimli listeleme kutusu
ComboBox sınıfı ile temsil edilmiştir. Bu iki sınıfın ortak elemanları
ListControl isimli bir sınıfta toplanmış ve bu iki sınıf ListControl
sınıfından türetilmiştir.

Control

ListControl

ListBox ComboBox

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(100, 50, 100, 200);

91
for (int i = 1; i <= 100; ++i)
m_lb.Items.Add("Item No: " + i);

m_lb.Parent = this;
}
}
}

ListBox kontrolünde hiçbir eleman seçili olmayabilir ya da birden


fazla eleman seçili olabilir. Kontrol yaratıldığında default olarak tek bir
elemanın seçilmesine izin vermektedir. Sınıfın int türden read/write
SelectedIndex isimli property elemanı, seçilmiş olan elemanın indeksini
almak için ya da programlama yoluyla eleman seçmek için kullanılır. Henüz
bir eleman seçilmemişse bu property “–1” değerini verir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(50, 50, 100, 200);

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add("Item No: " + i);

m_buttonOK = new Button();


m_buttonOK.Location = new Point(200, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;

92
}

void m_buttonOK_Click(object sender, EventArgs e)


{
MessageBox.Show(m_lb.SelectedIndex.ToString());
}
}
}

ListBox sınıfının SelectedItem isimli property elemanı, seçili olan


elemana ilişkin nesneyi verir. SelectedItem elemanı object türdendir ve
read/write propertydir. Eğer hiçbir eleman seçili değilse property null
değerini verir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(50, 50, 100, 200);

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add("Item No: " + i);

m_buttonOK = new Button();


m_buttonOK.Location = new Point(200, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;
}

93
void m_buttonOK_Click(object sender, EventArgs e)
{
string str = (string)m_lb.SelectedItem;

MessageBox.Show(str);
}
}
}

Hocanın Örneği:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(50, 50, 100, 200);

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add(new Point(i, i));

m_buttonOK = new Button();


m_buttonOK.Location = new Point(200, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;
}

void m_buttonOK_Click(object sender, EventArgs e)

94
{
Point pt = (Point)m_lb.SelectedItem;

MessageBox.Show(pt.ToString());
}
}
}

Listeleme kutusunu çoklu seçime getirmek için sınıfın,


SelectionMode isimli property elemanı kullanılır. SelectionMode isimli
property elemanı SelectionMode isimli enum türündendir. Bu enum türünün
elemanları şöyledir:

None: Bu durumda listeleme kutusunda hiçbir eleman seçilemez.

m_lb.SelectionMode = SelectionMode.None;

One: Yalnızca tek eleman seçilebilir ve default durumdur.

MultipleSimple: Ctrl tuşuna basılarak birden fazla eleman seçilebilir.

m_lb.SelectionMode = SelectionMode.MultiSimple;

MultiExtended: Ctrl tuşuna basılarak birden fazla eleman seçilebilir. Aynı


zamanda Shift ve ok tuşuyla birden fazla ardışık seçim yapılabilir.

m_lb.SelectionMode = SelectionMode.MultiExtended;

Birden fazla seçimin yapılabildiği durumda, tüm seçili olan elemanları


sınıfın SelectedIndices isimli property elemanı ile elde edebiliriz. Bu
property SelectedIndexCollection isimli bir collection türündendir. Bu
collection int türünden elemanları tutar. Örneğin:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form

95
{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(50, 50, 100, 200);
m_lb.SelectionMode = SelectionMode.MultiExtended;

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add(new Point(i, i));

m_buttonOK = new Button();


m_buttonOK.Location = new Point(200, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;
}

void m_buttonOK_Click(object sender, EventArgs e)


{
string str = "";

foreach (int x in m_lb.SelectedIndices)


str += x.ToString() + " ";

MessageBox.Show(str);
}
}
}

Benzer biçimde seçilen tüm elemanlar SelectedItems isimli property


elemanı ile elde edilebilir. SelectedItems propertysi ListBox sınıfı
içerisindeki SelectedObjectCollection isimli bir collection
türündendir. Bu property tüm elemanları, object türünden nesneleri tutan
bir collection biçiminde verir.

Şüphesiz sınıfın en önemli event elemanı belirli bir elemana double


klik yapıldığında tetiklenen DoubleClick isimli event elemanıdır. Bu event
eleman aslında Control sınıfından gelmektedir. EventHandler isimli
delege türündendir. Bir elemana double klik yapıldığında, bir işlemin
yapılması çok karşılaşılan bir durumdur. Bunun için programcı bu event
elemanına fonksiyon girip, SelectedXXX propertyleri ile seçilmiş olan
elemana ulaşır. Örneğin:

96
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
m_lb = new ListBox();
m_lb.Bounds = new Rectangle(50, 50, 100, 200);
m_lb.SelectionMode = SelectionMode.MultiExtended;
m_lb.DoubleClick += new
EventHandler(m_lb_DoubleClick);

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add(new Point(i, i));

m_buttonOK = new Button();


m_buttonOK.Location = new Point(200, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;
}

void m_lb_DoubleClick(object sender, EventArgs e)


{
MessageBox.Show(m_lb.SelectedItem.ToString());
}

void m_buttonOK_Click(object sender, EventArgs e)


{

}
}
}

97
Diğer önemli bir event elemanı da SelectedIndexChange elemanıdır. Bu
event elemanı da EventHandler türündendir. Listeleme kutusundaki aktif
eleman fare, klavye ya da programlama yoluyla değiştirildiğinde tetiklenir.

Sınıf Çalışması: Yan yana iki tane listeleme kutusu oluşturunuz. Soldaki
listeleme kutusunda çeşitli şehir isimleri bulunsun. Sağdaki listeleme
kutusunda ise bu şehirlere ilişkin ilçeler görüntülensin. Sol şehir listesinde
herhangi bir şehir değiştiğinde soldaki elemanlar değişsin.

Çözüm:

using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Sehir
{
static class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(fals
e);
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private ListBox m_listBox_Sehir;
private ListBox m_listBox_Ilce;
private string[] Sehir = { "İstanbul", "İzmir",
"Giresun", "Ankara" };
private string[][] dizi = new string[4][];

public MyForm()
{
dizi[0] = new string[] { "beyoğlu", "Şişli",
"Tuzla", "Beşiktaş" };
dizi[1] = new string[] { "Tire", "Konak",
"Alsancak" };
dizi[2] = new string[] { "Görele", "Tirebolu",
"Keşap" };
dizi[3] = new string[] { "Kızılay", "Mamak" };

m_listBox_Sehir = new ListBox();

98
m_listBox_Sehir.Bounds = new Rectangle(10, 10,
150, 300);
m_listBox_Sehir.DoubleClick += new
EventHandler(double_Click);
m_listBox_Sehir.SelectedIndexChanged += new
EventHandler(double_Click);
m_listBox_Ilce = new ListBox();
m_listBox_Ilce.Bounds = new Rectangle(170, 10,
150, 300);

base.Size = new Size(400, 500);


base.Controls.AddRange(new Control
[]{m_listBox_Sehir, m_listBox_Ilce});

for (int i = 0; i < Sehir.Length; ++i)


m_listBox_Sehir.Items.Add(Sehir[i]);

for (int i = 0; i < dizi[0].Length; ++i)


m_listBox_Ilce.Items.Add(dizi[0][i]);
}
private void double_Click(object o, EventArgs e)
{
m_listBox_Ilce.Items.Clear();

for (int i = 0; i <


dizi[m_listBox_Sehir.SelectedIndex].Length; ++i)
m_listBox_Ilce.Items.Add(dizi[m_listBox_Sehir.
SelectedIndex][i]);
}
}
}

{09.12.2007 Pazar}

ListBox sınıfının bool türden MultiColumn property elemanı, çok sütunlu


listeleme kutusu oluşturmak için kullanılabilir. Bu propertynin default
biçimi, false biçimindedir. Yani listeleme kutusu tek sütunludur.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}

99
}

class MyForm : Form


{
private ListBox m_lb;
private Button m_buttonOK;

public MyForm()
{
base.Text = "Sample ListBox";
base.Size = new Size(500, 400);

m_lb = new ListBox();


m_lb.Bounds = new Rectangle(50, 50, 300, 300);
m_lb.SelectionMode = SelectionMode.MultiExtended;
m_lb.DoubleClick += new
EventHandler(m_lb_DoubleClick);
m_lb.MultiColumn = true;

for (int i = 1; i <= 100; ++i)


m_lb.Items.Add(i);

m_buttonOK = new Button();


m_buttonOK.Location = new Point(400, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_lb.Parent = this;
m_buttonOK.Parent = this;
}

void m_lb_DoubleClick(object sender, EventArgs e)


{
MessageBox.Show(m_lb.SelectedItem.ToString());
}
}
}

SEÇİMLİ LİSTELEME KUTULARI

Seçimli listeleme kutuları, bir listeleme kutusu ile bir TextBox ve Label
kontrolünün birleşimi şeklindedir. ComboBox sınıfı ile temsil edilmiştir.
ComboBox sınıfının kullanımı da ListBox sınıfına çok benzemektedir.
Yalnızca seçilen eleman seçimli listeleme kutusunun edit kısmında
görüntülenir.

using System;
using System.Windows.Forms;
using System.Drawing;

100
namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ComboBox m_cb;
private Button m_buttonOK;

public MyForm()
{
base.Text = "Sample ListBox";
base.Size = new Size(500, 400);

m_cb = new ComboBox();


m_cb.Bounds = new Rectangle(50, 50, 200, 300);

for (int i = 1; i <= 100; ++i)


m_cb.Items.Add(i);

m_buttonOK = new Button();


m_buttonOK.Location = new Point(400, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_cb.Parent = this;
m_buttonOK.Parent = this;
}
}
}

Seçimli listeleme kutusunun edit kısmında, seçilen değer gözükür. Bu


değerin alınması yine tıpkı ListBox sınıfında olduğu gibi, ComboBox
sınıfının SelectedIndex ve SelectedItem elemanları ile yapılabilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App

101
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ComboBox m_cb;
private Button m_buttonOK;

public MyForm()
{
base.Text = "Sample ListBox";
base.Size = new Size(500, 400);

m_cb = new ComboBox();


m_cb.Bounds = new Rectangle(50, 50, 200, 300);

for (int i = 1; i <= 100; ++i)


m_cb.Items.Add(i);

m_buttonOK = new Button();


m_buttonOK.Location = new Point(300, 50);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_cb.Parent = this;
m_buttonOK.Parent = this;
}

void m_buttonOK_Click(object sender, EventArgs e)


{
int val = (int)m_cb.SelectedItem;

MessageBox.Show(val.ToString());
}
}
}

Şüphesiz seçimli listeleme kutusunda çoklu seçim diye bir kavram yoktur.
Aynı zamanda çok sütunlu seçimli listeleme kutusu oluşturmakta mümkün
değildir.

Anımsanacağı gibi SelectedItem ve SelectedIndex read/write


propertydirler. Yani biz bu propertylere değer atayarak belirli bir elemanı
seçilmiş hale getirebiliriz. SelectedItem propertysine atanan object

102
değeri tek tek kontrollerin tuttuğu object elemanların üzerinde Equals
sanal fonksiyonu çağrılarak desteklenebilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private ComboBox m_cb;
private Button m_buttonOK;

public MyForm()
{
base.Text = "Sample ListBox";
base.Size = new Size(500, 400);

m_cb = new ComboBox();


m_cb.Bounds = new Rectangle(50, 50, 200, 300);

for (int i = 1; i <= 100; ++i)


m_cb.Items.Add(i);

m_cb.SelectedItem = 10;

m_buttonOK = new Button();


m_buttonOK.Location = new Point(400, 125);
m_buttonOK.Text = "&Ok";
m_buttonOK.Click += new
EventHandler(m_buttonOK_Click);

m_cb.Parent = this;
m_buttonOK.Parent = this;
}

void m_buttonOK_Click(object sender, EventArgs e)


{
int val = (int)m_cb.SelectedItem;

MessageBox.Show(val.ToString());

103
}
}
}

Seçimli listeleme kutusu, sınıfın DropDownStyle elemanı ile biçimsel


olarak değiştirilebilir. DropDownStyle property elemanı ComboBoxStyle
isimli enum türündendir. Bu enum türünün 3 elemanı şöyledir:

DropDown: Property default bu değerdedir. Bu biçimde seçimli listeleme


kutusu tıklanınca açılır ve edit alanına klavye ile yazı girilebilir. Kullanıcı
seçimli listeleme kutusunun edit alanına klavye ile yazı girdiğinde listeleme
kutusu elemanına otomatik konumlandırma yapılır. Fakat seçilmiş olmaz.

DropDownList: Burada seçimli listeleme kutusunun edit alanına klavye ile


giriş yapılamaz. Yani kullanıcı, olan bir elemanı seçer. Fakat klavye ile
uygun eleman seçilebilmektedir.

m_cb = new ComboBox();


m_cb.Bounds = new Rectangle(50, 50, 200, 300);
m_cb.DropDownStyle = ComboBoxStyle.DropDownList;

Simple: Bu biçimde seçimli listeleme kutusunun seçimli listeleme kutusu


kısmı her zaman açıktır ve edit alanına klavye ile giriş yapılabilir.

ComboBox sınıfının Text isimli read/write elemanı, her zaman edit


kısmındaki yazıyı alıp set etmede kullanılır.

void m_buttonOK_Click(object sender, EventArgs e)


{
MessageBox.Show(m_cb.Text);
}

ComboBox sınıfının da tıpkı ListBox sınıfında olduğu gibi önemli bazı


event elemanları vardır. Örneğin; SelectedIndexChange event elemanı,
tamamen listeleme kutusundaki gibidir. Sınıfın DropDown isimli event
elemanı listeleme kutusu açıldığında tetiklenir.

KONTROLLERİN DEMİRLENMESİ

Demirleme bir alt pencerenin, üst pencerenin bir ya da birden fazla


köşesine sabit uzaklıkta olmasının sağlayan bir özelliktir. Demirleme
tamamen yapay yöntemlerle sağlanmaktadır. Yani üst pencerenin Resize
mesajında, alt pencere yeniden konumlandırılarak, alt pencere yeniden
konumlandırılır. Bu nedenle programcı OnResize fonksiyonunu override
etmişse kesinlikle base.OnResize(...) ifadesi ile Control sınıfının
OnResize fonksiyonunun çağrılmasını sağlamalıdır. Çünkü bu

104
konumlandırma Control sınıfının OnResize fonksiyonunda yapılmaktadır.
Demirleme özelliği Control sınıfının Anchor propertysi ile belirlenir.

Anahtar Notlar

Bir enum türünün birden fazla elemanı tek çubuk(|) operatörü ile
birleştirilebilir. Bunun yapılabilmesi için ilgili enum türünün
FlagsAttibute ile özniteliklendirilmesi gerekir.

Anchor propertysi AnchorStyle isimli bir enum türündendir. Bu enum


türünün birden fazla elemanı çubuk operatörü ile birleştirilebilir. Örneğin:

class MyForm : Form


{
private Button m_buttonOK;
public MyForm()
{
m_buttonOK = new Button();
m_buttonOK.Text = "&Ok";
m_buttonOK.Location = new Point(100, 100);
m_buttonOK.Anchor = AnchorStyles.Right |
AnchorStyles.Bottom;
this.Controls.Add(m_buttonOK);
}
}

Burada düğme kontrolü aşağıya ve sağa demirlenmiştir. O halde default


demirlemenin sol ve üst kenara göre yapıldığı söylenebilir.

m_buttonOK.Anchor = AnchorStyles.Right | AnchorStyles.Bottom|


AnchorStyles.Left | AnchorStyles.Top; //tüm köşelere

Sınıf Çalışması: Anchor özelliğini kullanmadan bir düğmeyi tam çalışma


alanının ortasında belirli bir boyutta yaratınız. Resize mesajını işleyerek
onun hep ortada olmasının sağlayınız.

Çözüm:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{

105
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_buttonOK;

public MyForm()
{
m_buttonOK = new Button();
m_buttonOK.Text = "&Ok";
m_buttonOK.Size = new Size(70, 30);
m_buttonOK.Location = new
Point(base.ClientSize.Width / 2 -
35,base.ClientSize.Height/2-15); //çalışma alanına göre
ortalama

base.Resize += new
EventHandler(MyForm_ClientSizeChanged);
this.Controls.Add(m_buttonOK);
}

void MyForm_ClientSizeChanged(object sender,


EventArgs e)
{
m_buttonOK.Size = new Size(70, 30);
m_buttonOK.Location = new
Point(base.ClientSize.Width / 2 - 35, base.ClientSize.Height
/ 2 - 15); //tekrar konumlandırma
}
}
}

Sınıf Çalışması: Bir form penceresinin içerisine çok satırlı bir TextBox
kontrolü yerleştiriniz. Çok satırlı TextBox kontrolü, formun çalışma alanına
göre dörtkenardan eşit miktarda içerde olsun. Fakat form genişletip
daraltıldığında köşelere olan uzaklık korunsun.

Açıklama: Bir Rectangle nesnesi yaratılıp ClientRectangle propertysi


buna atanmalı sonra Inflate fonksiyonu ile bu Rectangle büzdürülmeli.
Daha sonra çok satırlı TextBox kontrolü bu Rectangle kullanılarak
yaratılmalıdır. Çok satırlı TextBox dört tarafa demirlenmelidir.

PENCERELERDEKİ Z SIRASI

Z sırası (z order), çakışık kardeş pencerelerin üst üste görünme sırasını


belirtir. Z sırasına göre en önde olan, en yukarıda görünür. Z sırasına göre

106
en sonda olan, en arkada gözükür. Şüphesiz kardeş pencereler birbirini
kesmedikten sonra z sırasının bir önemi yoktur.

Kardeş pencerelerin z sırası, onların üst pencereye eklenme sırasına


bağlıdır. Üst pencereye ilk eklenen kardeş pencere(yani Controls[0] da
bulunan kardeş pencere), z sırasına göre en üsttedir. Z sırasın, buradaki
sıranın değiştirilmesi ile değiştirilebilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private Button m_button1;
private Button m_button2;
private Button m_button3;

public MyForm()
{
m_button1 = new Button();
m_button1.Location = new Point(100, 100);
m_button1.Text = "&Button 1";
m_button1.Click += new
EventHandler(m_button1_Click);

m_button2 = new Button();


m_button2.Location = new Point(110, 105);
m_button2.Text = "&Button 2";
m_button2.Click += new
EventHandler(m_button1_Click);

m_button3 = new Button();


m_button3.Location = new Point(90, 95);
m_button3.Text = "&Button 3";
m_button3.Click += new
EventHandler(m_button1_Click);

this.Controls.AddRange(new Control[] { m_button1,


m_button2, m_button3 });

107
}
void m_button1_Click(object sender, EventArgs e)
{
this.Controls.Remove((Control)sender);
this.Controls.Add((Control) sender);
}
}
}

Control sınıfının BringToFront fonksiyonu, ilgili kontrolü Control


collection’unda değişiklik yaparak z sırasında en öne alır.

void m_button1_Click(object sender, EventArgs e)


{
Control c = (Control)sender;
c.BringToFront();
}

Control sınıfının SendToBack isimli fonksiyonu, ilgili pencereyi z sırasına


göre en geriye alır.

void m_button1_Click(object sender, EventArgs e)


{
Control c = (Control)sender;
c.SendToBack();
}
KONTROLLERİN SÜRÜKLENMESİ

Bir şekli sürükleme oldukça basit bir mantığa dayanmaktadır. İlk tıklanan
nokta saklanır. Sonra fare kaydırıldığında o noktadan ne kadar delta x ve
delta y uzaklaştırıldığına bakılır. Tabi söz konusu olan bir kontrol ise
kontrolün fare mesajlarını işlemek gerekir. Kontrolden alınan fare
koordinatlarının, kontrolün çalışma alanı orijinine olduğu unutulmamalıdır.
Kontrol uygun miktarda hareket ettirildiğinde fare yine o kontrolde göreli
olarak aynı noktaya gelmiş olur. O halde şekli sürükleme şöyle yapılabilir:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}

108
}

class MyForm : Form


{
private Button m_buttonOK;
private int m_posX, m_posY;

public MyForm()
{
m_buttonOK = new Button();
m_buttonOK.Location = new Point(100, 100);
m_buttonOK.Text = "&Ok";
m_buttonOK.MouseDown += new
MouseEventHandler(m_buttonOK_MouseDown);
m_buttonOK.MouseMove += new
MouseEventHandler(m_buttonOK_MouseMove);

this.Controls.Add(m_buttonOK);
}

void m_buttonOK_MouseMove(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int deltaX = e.X - m_posX;
int deltaY = e.Y - m_posY;

m_buttonOK.Location = new
Point(m_buttonOK.Left + deltaX, m_buttonOK.Top + deltaY);

}
}

void m_buttonOK_MouseDown(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_posX = e.X;
m_posY = e.Y;
}
}
}
}

{15.12.2007 Cumartesi}

109
Sınıf Çalışması: Boş bir form oluşturunuz. Fare ile sol tuşa tıklandığında
bir buton yaratınız. Fareyi hareket ettirdikçe düğmenin büyüklüğünü
değiştiriniz. El fareden çekilince düğmeyi belirlenen koordinatta yaratınız.

Açıklamalar: Sınıfın bir veri elemanı olarak, bir Button referansı


bulundurulur. Farenin sol tuşuna basıldığında, Button nesnesi yaratılır ve
Form’un alt pencere listesine eklenir. Sonra MouseMove mesajında, bu alt
pencere farenin durumuna göre büyütülüp küçültülür.

Çözüm:

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;

namespace NewControl
{
static class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private Button m_button;
private Button m_buttonClear;
private int deltaX, deltaY;
public MyForm()
{
m_buttonClear = new Button();
m_buttonClear.Text = "&Clear";
m_buttonClear.Click += new
EventHandler(m_buttonClear_Click);

m_buttonClear.Parent = this;

base.MouseDown += new
MouseEventHandler(MyForm_MouseDown);
base.MouseUp += new
MouseEventHandler(MyForm_MouseUp);

}
void m_buttonClear_Click(object sender, EventArgs e)
{
base.Controls.Clear();
m_buttonClear.Parent = this;

110
}
void MyForm_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_button = new Button();
m_button.Text = "&Ok";
m_button.Location = new Point(deltaX, deltaY);
m_button.Size = new Size(e.X - deltaX, e.Y -
deltaY);
//m_button.MouseMove += new
MouseEventHandler(m_button_MouseMove);
m_button.Parent = this;

}
}
void MyForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
deltaX = e.X;
deltaY = e.Y;
}
}
}
}

TEMEL MENÜ İŞLEMLERİ

.Net’in menü sınıfları Framework 2.0’da ciddi biçimde değiştirilmiştir.


Her ne kadar Framework 1.1’deki menü sınıfları hala desteklense de,
kullanılması tavsiye edilmemektedir. Menüler, araç çubukları ve durum
çubuklarının hepsi birer şerit(strip) kontroldür. Çünkü hepsinde bir
şerit(strip) vardır ve bu üç öğenin de ortak özellikleri bulunmaktadır.

Tipik bir menü sisteminde, menü çubuğu vardır. Bu menü çubuğuna


popup pencereler yerleştirilmiştir. Popup pencereler, menü elemanlarından
ve başka popup pencerelerden oluşur. Framework 1.1ile Framework 2.0
menüleri arasında, ayrıca önemli bir fark daha vardır. Framework 1.1’in
menüleri API fonksiyonları ile oluşturulmuştur. Yani menü çubuğu, çalışma
alanının dışında (non-clientarea) bulunur. Dolayısıyla Framework 1.1
menüleri kullanılırsa, çalışma alanının “0, 0” noktası, programcı tarafından
menünün aşağısında görülebilir durumdadır. Fakat Framework 2.0’ın
menüleri, adeta düğme gibi, TextBox gibi kontrol biçimindedir. Dolayısıyla
çalışma alanının içerisindedir. Bu yaklaşım, çizim konusunda bazı
uyumsuzluklara yol açsa da bu uyumsuzluklar giderilebilir.

111
Menü çubuğu, MenuStrip sınıfı ile temsil edilmiştir. MenuStrip sınıfı,
ToolStrip sınıfından türetilmiştir. Üç önemli şerit kontrolünün türetme
biçimi şöyledir:

ToolStrip

MenuStrip StateStrip

ToolStrip sınıfı araç çubuğu oluşturmak için, MenuStrip sınıfı menü


çubuğu oluşturmak için, StateStrip sınıfı durum çubuğu oluşturmak için
kullanılır. Görüldüğü gibi adeta menü çubuğu ve durum çubuğu bir çeşit
araç çubuğudur.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_mainMenu;

public MyForm()
{
this.Text = "Sample Menu";

m_mainMenu = new MenuStrip();

this.Controls.Add(m_mainMenu);
}
}
}

MenuStrip sınıfının, ToolStrip sınıfından gelen Items isimli


property elemanı, ToolStripItemCollection isimli collection sınıf
türündendir. Bu collection sınıf IList arayüzünü desteklemektedir.

112
ToolStripItemCollection sınıfı, ismi üzerinde ToolStripItem türünden
nesneleri tutmaktadır. Yani kabaca ToolStrip sınıfının, ToolStripItem
nesnelerini tutuğu söylenebilir.

ToolStripItem sınıfı aslında abstract bir sınıftır. ToolStripItem


sınıfından türetilmiş sınıflar şunlardır:

ToolStripButton ToolStripSeperator ToolStripControlHost


ToolStripLabel ToolStripDropDownItem ToolStripStatusLabel

ToolStripDropDownItem sınıfından da şu türetmeler uygulanmıştır:

ToolStripDropDownButton ToolStripMenuItem ToolStripSplitButton

Tipik olarak menüye yerleştirilecek elemanlar, ToolStripDropDownItem


sınıfından türetilmiş sınıflar türünden olmalıdır. Menülere yerleştirilecek en
tipik menü property elemanı, ToolStripMenuItem elemanıdır.

ToolStripItem

ToolStripDropDownItem

ToolStripMenuItem

ToolStripMenuItem sınıfı, hem popup pencerenin tamamını temsil


etmektedir. Hem de basit bir menü elemanını temsil etmektedir.
ToolStripMenuItem sınıfının DropDownItems isimli
ToolStripItemCollection türünden property elemanı, popup pencerenin
tuttuğu menü elemanlarının içermektedir. Eğer programcı bu collection
propertysine hiç eleman eklemezse, menü elemanı basit bir elemandır.
Fakat en az bir eleman eklerse menü elemanı bir popup elemandır.
Görüldüğü gibi hem popup pencere hem de basit bir menü elemanı
ToolStripMenuItem sınıfı ile temsil edilmiştir. Yani basit bir elemanı popup
yapmak için yapılacak tek şey, DropDownItems collection elemanına
eleman eklemektir. Örneğin aşağıdaki gibi bir menü sistemi oluşturmak
isteyelim:

File

113
Open
Fruit Apple
Close Orange
Banana

Burada bir tane MenuStrip nesnesi ve 7 tane de ToolStripMenuItem


nesnesi oluşturulmalıdır. Burada File isimli eleman doğrudan MenuStrip
sınıfının ItemsCollection‘una eklenir. File bir popup eleman olduğuna
göre, bu elemanın DropDownItems collection elemanına Open, Fruit ve
Close eklenmelidir. Nihayet Fruit nesnesinin DropDownItems
collectionuna Apple, Orange, Banana eklenmelidir.

ToolStripMenuItem sınıfının ToolStripItem sınıfından gelen Text


isimli property elemanı, ilgili menü elemanının yazısının belirlenmesinde
kullanılır.

Bir menü sistemi şu aşamalardan geçirilerek oluşturulur:

1- Programcı Form sınıfına bir tane MenuStrip türünden veri elemanı ve


tüm popup ve menü elemanları için ToolStripMenuItem türünden
veri elemanları yerleştirir.

2- İlgili sınıfların default başlangıç fonksiyonları kullanılarak new


operatörü ile nesneler yaratılır.

3- Menü elemanlarının Text propertylerine yazı girilerek menülerin


isimleri belirlenir.

4- Ana popup nesneleri MenuStrip nesnesinin Items collectionuna,


normal menü elemanları ise ilgili popup pencereyi temsil eden
ToolStripMenuItem nesnelerinin DropDownItems collectionuna
eklenir.

5- Menü çubuğunu temsil eden MenuStrip nesnesini ise Form‘un


Controls collectionuna eklemek gerekir.

Anahtar Notlar

Menü sistemine ilişkin nesnelerin, tutarlı isimlendirilmesi tavsiye


edilmektedir. Örneğin; menü çubuğu için m_MainMenu, popup pencereleri
için m_xxxPopup, menü elemanları için m_XXXItem isimlendirmeleri
kullanılabilir. Programcı menü geçişlerini de isme yansıtmalıdır. Örneğin;
m_FileFruitAppleItem gibi.

114
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileFruitPopup;
private ToolStripMenuItem m_fileCloseItem;
private ToolStripMenuItem m_fileFruitAppleItem;
private ToolStripMenuItem m_fileFruitOrangeItem;
private ToolStripMenuItem m_fileFruitBananaItem;

public MyForm()
{
this.Text = "Sample Menu";

m_mainMenu = new MenuStrip();

m_filePopup = new ToolStripMenuItem();


m_filePopup.Text = "&File";

m_fileOpenItem = new ToolStripMenuItem();


m_fileOpenItem.Text = "&Open";

m_fileFruitPopup = new ToolStripMenuItem();


m_fileFruitPopup.Text = "&Fruit";

m_fileCloseItem = new ToolStripMenuItem();


m_fileCloseItem.Text = "&Close";

m_fileFruitAppleItem = new ToolStripMenuItem();


m_fileFruitAppleItem.Text = "&Apple";

m_fileFruitOrangeItem = new ToolStripMenuItem();


m_fileFruitOrangeItem.Text = "&Orange";

m_fileFruitBananaItem = new ToolStripMenuItem();


m_fileFruitBananaItem.Text = "&Banana";

115
m_fileFruitPopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileFruitAppleItem,
m_fileFruitOrangeItem, m_fileFruitBananaItem });

m_filePopup.DropDownItems.AddRange(new
ToolStripItem[] { m_fileOpenItem, m_fileFruitPopup,
m_fileCloseItem });

m_mainMenu.Items.Add(m_filePopup);

this.Controls.Add(m_mainMenu);
}
}
}

ToolStripMenuItem sınıfının ToolStripItem sınıfından gelen Click isimli


event elemanı, bir menü elemanı seçildiğinde tetiklenir. Bu event elemanı
EventHandler isimli delege türündendir. Dolayısıyla ek bir bilgi
vermemektedir.

void m_fileFruitOrangeItem_Click(object sender, EventArgs e)


{
MessageBox.Show("Orange seçildi");
}

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName + " Seçildi");
}
}
ToolStripMenuItem sınıfının Enabled isimli bool türden property elemanı,
menü elemanını aktif yada pasif etmek için kullanılır. Pasif durumdaki
menü elemanları seçilemez ve sönük durumdadır. Default durum true
biçimindedir.

void m_fileCloseItem_Click(object sender, EventArgs e)


{
m_fileOpenItem.Enabled = true;
m_fileCloseItem.Enabled = false;
}

void m_fileFruitOrangeItem_Click(object sender, EventArgs e)

116
{
MessageBox.Show("Orange seçildi");
}

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName + " Seçildi");

m_fileOpenItem.Enabled = false;
m_fileCloseItem.Enabled = true;
}
}

{16.12.2007 Pazar}

ToolStripMenuItem sınıfının bool türden Checked property elemanı,


menü elemanına onaylama çarpısı koyup kaldırmada kullanılır. Bu
propertynin default durumu false biçimindedir.

Bir menü elemanına tıklandığında oluşan Click eventine geçirilen


sender parametresi, ilgili menü elemanına ilişkin ToolStripMenuItem
nesnesidir. Yani farklı menü elemanları için aynı delege fonksiyonu
verilebilir. Bu durumda hangi menü elemanının seçildiği sender
parametresi ile anlaşılabilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileFruitPopup;

117
private ToolStripMenuItem m_fileCloseItem;
private ToolStripMenuItem m_fileFruitAppleItem;
private ToolStripMenuItem m_fileFruitOrangeItem;
private ToolStripMenuItem m_fileFruitBananaItem;

public MyForm()
{
this.Text = "Sample Menu";

m_mainMenu = new MenuStrip();

m_filePopup = new ToolStripMenuItem();


m_filePopup.Text = "&File";

m_fileOpenItem = new ToolStripMenuItem();


m_fileOpenItem.Text = "&Open";
m_fileOpenItem.Click += new
EventHandler(m_fileOpenItem_Click);

m_fileFruitPopup = new ToolStripMenuItem();


m_fileFruitPopup.Text = "&Fruit";

m_fileCloseItem = new ToolStripMenuItem();


m_fileCloseItem.Text = "&Close";
m_fileCloseItem.Click += new
EventHandler(m_fileCloseItem_Click);
m_fileCloseItem.Enabled = false;

m_fileFruitAppleItem = new ToolStripMenuItem();


m_fileFruitAppleItem.Text = "&Apple";
m_fileFruitAppleItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitOrangeItem = new ToolStripMenuItem();


m_fileFruitOrangeItem.Text = "&Orange";
m_fileFruitOrangeItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitBananaItem = new ToolStripMenuItem();


m_fileFruitBananaItem.Text = "&Banana";
m_fileFruitBananaItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitPopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileFruitAppleItem,
m_fileFruitOrangeItem, m_fileFruitBananaItem });
m_filePopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileOpenItem, m_fileFruitPopup,
m_fileCloseItem });

118
m_mainMenu.Items.Add(m_filePopup);

this.Controls.Add(m_mainMenu);
}

void m_fileFruitAppleItem_Click(object sender,


EventArgs e)
{
m_fileFruitAppleItem.Checked = true;
}

void m_fileCloseItem_Click(object sender, EventArgs e)


{
m_fileOpenItem.Enabled = true;
m_fileCloseItem.Enabled = false;
}

void fruitClickHandler(object sender, EventArgs e)


{
ToolStripMenuItem mi = (ToolStripMenuItem)sender;

mi.Checked = !mi.Checked;
}

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName + " Seçildi");

m_fileOpenItem.Enabled = false;
m_fileCloseItem.Enabled = true;
}
}
}
}

ToolStripMenuItem sınıfının bool türden CheckOnClick isimli


property elemanı, otomatik olarak menü elemanı her seçildiğinde, eğer
eleman checked ise unchecked, unchecked ise checked yapar.

m_fileFruitBananaItem.CheckOnClick = true;

Fakat genellikle bir menüde ya da araç çubuğunda “n” tane eleman vardır.
Bu “n” tane elemandan bir seçim yapıldığında o eleman checked yapılır.
Daha önce checked yapılmış olan unchecked yapılır.

119
Sınıf Çalışması: Bir form yaratınız. Bir menü çubuğuna Numbers isimli bir
popup yerleştiriniz. Bu popup içerisine üzerinde Item1, Item2 yazıları
bulunan 30 menü elemanını hiç veri elemanı bulundurmadan programlama
yoluyla popupa ekleyiniz. Sonra menüden herhangi bir eleman checked
yapınız. Daha önce seçilmiş olanı unchecked yapınız.

Çözüm:

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_main;
private ToolStripMenuItem m_numbers;

public MyForm()
{
m_main = new MenuStrip();
m_numbers = new ToolStripMenuItem();
m_numbers.Text = "Numbers";

m_main.Items.Add(m_numbers);

for (int i = 1; i < 31; ++i)


{
ToolStripMenuItem m_item = new
ToolStripMenuItem();
m_item.Text = ("Item" + i);
m_item.Click += new
EventHandler(m_item_Click);
m_numbers.DropDownItems.Add(m_item);
}
base.Controls.Add(m_main);
}

void m_item_Click(object sender, EventArgs e)


{
ToolStripMenuItem tsm = (ToolStripMenuItem)sender;

120
foreach(ToolStripMenuItem a in
m_numbers.DropDownItems)
{
a.Checked = false;
}

tsm.Checked = true;
}
}
}

ToolStripMenuItem sınıfının yalnızca default başlangıç fonksiyonu


yoktur. Bazı propertyleri hemen set eden başka başlangıç fonksiyonları da
vardır. Örneğin; önce ToolStripMenuItem nesnesini yaratıp sonra değer
girmek yerine doğrudan ToolStripMenuItem nesnesini string parametreli
başlangıç fonksiyonu ile yaratmak aynı anlamdadır.

ToolStripMenuItem sınıfının DropDownItems elemanının


ToolStripItemCollection türünden olduğu daha önce belirtilmişti.
Anımsanacağı gibi bu collection eleman ToolStripItem türünden eleman
tutar. Aslında bu collection sınıfının birçok Add fonksiyonu vardır. Bu diğer
Add fonksiyonları kendi içerisinde ToolStripMenuItem nesnesini yaratıp
ilgili propertyleri set ederler.

m_filePopup.DropDownItems.Add(“&File”);

Burada fonksiyonu bir ToolStripMenuItem nesnesi yaratıp, onun Text


propertysine atama yapmakta ve bu nesneyi de collectiona eklemektedir.
Fonksiyonun geri dönüş değeri, yaratılmış olan ToolStripMenuItem
nesnesini vermektedir.

Her ne kadar tavsiye edilmese de tüm menü sistemi bir tek bir ifade ile
yaratılabilir.

ToolStripMenuItem sınıfının ForeColor ve BackColor elemanları,


menü elemanının zemin ve yazı renklerini belirleme de kullanılır.

m_fileOpenItem.BackColor = Color.Turquoise;
m_fileOpenItem.ForeColor = Color.Red;

ToolStripMenuItem sınıfının Width, Height, Size property


elemanları, ToolStripItem sınıfından gelmektedir. Normal olarak popupun
genişliği, en geniş menü elemanının genişliğine göre ayarlanmaktadır.
Aslında menü elemanları ve popup pencereler, istenilen genişlik ve
yüksekliğe getirilebilir. Fakat bunun için ToolStripMenuItem sınıfının bool

121
türden AutoSize propertysi false değerine çekilmelidir. Bu propertynin
default değeri true‘dir ve menü elemanının genişlik ve yükseklik değerleri
Text, Font propertysi dikkate alınarak ayarlanmaktadır.

m_fileOpenItem.Size = new Size(200, 50);


m_fileOpenItem.AutoSize = false;

Ayrıca menü elemanlarının üzerindeki yazının büyüklük-küçüklüğü ve bold-


italik gibi durumları değiştirilebilir. Bunun için ToolStripMenuItem sınıfının
ToolStripItem sınıfından gelen Font propertysi kullanılır. Font konusuna
ileride değinilecektir.

m_fileOpenItem.Font = new Font("Times New Roman", 20);

Kısayol tuşu(shorcut key), hiç menüyü açmadan klavyeden sanki bir menü
elemanı seçilmiş etkisi yaratan tuş kombinasyonudur. ToolStripMenuItem
sınıfının ShorcutKeys isimli property elemanı Keys isimli enum türündendir.
Birden fazla değer “|” operatörü ile birleştirilebilir. Örneğin:

m_fileOpenItem.ShortcutKeys = Keys.Control | Keys.O;

Default durumda atanan kısayol tuşu, menü elemanının yanında


görüntülenir. Aslında bu otomatik görüntülenme ToolStripMenuItem
sınıfının bool türden ShowShortcutKeys property elemanı ile yapılır.
Default durumu true’dir. Programcı bu propertyi true‘de tutarak sınıfın
string türden ShortcutKeyDisplayString elemanını set ederek, kısayol
tuşuna ilişkin başka bir yazının görüntülenmesini sağlayabilir. Yani özetle
ShortcutKeyDisplayString eğer null değerinde ise kısayol yazısı
otomatik belirlenmektedir. Aksi halde bu propertyde belirtilen yazı kısayol
yazısı yapılmaktadır. ShowShortcutKeys propertysi ise bu yazının
görüntülenip görüntülenmeyeceğini belirtir.

m_fileOpenItem.ShortcutKeys = Keys.Control | Keys.O;


m_fileOpenItem.ShowShortcutKeys = false;
m_fileOpenItem.ShortcutKeyDisplayString = "Kontrol O";

ToolStripMenuItem sınıfının bool türden AutoToolTip elemanı,


ipucu yazısının otomatik görüntülenip görüntülenmeyeceğini belirtir.
Default değer false‘dir. Bu property ToolStripItem sınıfından
gelmektedir. ToolStripMenuItem sınıfının ToolStripItem sınıfından gelen
string türden ToolTipText propertysi, ipucu yazısını belirlemede
kullanılır.

m_fileOpenItem.ToolTipText = "Dosya açar";

122
AutoToolTip property elemanı eğer true ise ipucu yazısı menü
elemanının Text propertysindeki yazı olarak alınır. Özetle ToolTipText
propertysine değer atanırsa ipucu yazısı olarak bu yazı görüntülenir. Eğer
yazı atanmazsa bu durumda AutoToolTip propertysine bakılır. Eğer bu
property true ise Text propertysindeki yazı, ipucu yazısı olarak
görüntülenir. False ise görüntülenme yapılmaz.

ToolStripMenuItem sınıfının ToolStripItem sınıfından gelen bool


türden Visible propertysi, menü elemanını görünür ya da görünmez
yapmak için kullanılır. Aynı işlem dolaylı olarak DropDownItems listesinden
çıkartılmasıyla da sağlanabilir.

Bir programda aslında birden fazla menü çubuğu olabilir. Bunun için tek
yapılacak şey birden fazla MenuStrip nesnesi yaratıp Controls’e
eklemektir. İleride anlatılacak olan Dock özelliğinden dolayı son eklenen
menü çubuğu daha yukarıda görüntülenir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileFruitPopup;
private ToolStripMenuItem m_fileCloseItem;
private ToolStripMenuItem m_fileFruitAppleItem;
private ToolStripMenuItem m_fileFruitOrangeItem;
private ToolStripMenuItem m_fileFruitBananaItem;

private MenuStrip m_otherMenu;

public MyForm()
{
this.Text = "Sample Menu";

m_mainMenu = new MenuStrip();

123
m_filePopup = new ToolStripMenuItem();
m_filePopup.Text = "&File";

m_fileOpenItem = new ToolStripMenuItem();


m_fileOpenItem.Text = "&Open";
m_fileOpenItem.Click += new
EventHandler(m_fileOpenItem_Click);
m_fileOpenItem.BackColor = Color.Turquoise;
m_fileOpenItem.ForeColor = Color.Red;
m_fileOpenItem.ShortcutKeys = Keys.Control |
Keys.O;
m_fileOpenItem.AutoToolTip = true;
m_fileOpenItem.ToolTipText = "Dosya açar";

m_fileFruitPopup = new ToolStripMenuItem();


m_fileFruitPopup.Text = "&Fruit";

m_fileCloseItem = new ToolStripMenuItem();


m_fileCloseItem.Text = "&Close";
m_fileCloseItem.Click += new
EventHandler(m_fileCloseItem_Click);
m_fileCloseItem.Enabled = false;

m_fileFruitAppleItem = new ToolStripMenuItem();


m_fileFruitAppleItem.Text = "&Apple";
m_fileFruitAppleItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitOrangeItem = new ToolStripMenuItem();


m_fileFruitOrangeItem.Text = "&Orange";
m_fileFruitOrangeItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitBananaItem = new ToolStripMenuItem();


m_fileFruitBananaItem.Text = "&Banana";
m_fileFruitBananaItem.Click += new
EventHandler(fruitClickHandler);
m_fileFruitBananaItem.CheckOnClick = true;

m_fileFruitPopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileFruitAppleItem,
m_fileFruitOrangeItem, m_fileFruitBananaItem });
m_filePopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileOpenItem, m_fileFruitPopup,
m_fileCloseItem });
m_mainMenu.Items.Add(m_filePopup);

m_otherMenu = new MenuStrip();

124
m_otherMenu.Items.Add(new
ToolStripMenuItem("Other"));

this.Controls.Add(m_mainMenu);
this.Controls.Add(m_otherMenu);
}

void m_fileFruitAppleItem_Click(object sender,


EventArgs e)
{
m_fileFruitAppleItem.Checked = true;
}

void m_fileCloseItem_Click(object sender, EventArgs e)


{
m_fileOpenItem.Enabled = true;
m_fileCloseItem.Enabled = false;
}

void fruitClickHandler(object sender, EventArgs e)


{
//ToolStripMenuItem mi =
(ToolStripMenuItem)sender;

//mi.Checked = !mi.Checked;
}

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName + " Seçildi");

m_fileOpenItem.Enabled = false;
m_fileCloseItem.Enabled = true;
}
}
}
}

Tabi MenuStrip sınıfının Visible propertysi ile bunlardan yalnızca birinin


görüntülenmesini duruma göre diğerinin görüntülenmesini sağlayabiliriz.
Pek çok programda bir menü elemanı seçildiğinde, programın menü yapısı
değişmektedir. Programcı bu işlemi birden fazla menü çubuğu oluşturarak
sağlayabileceği gibi menü elemanlarının dinamik olarak yaratıp yok ederek
de sağlayabilir. Ya da menü birleştirme işlemi(menu merge), bu işlemleri
kolaylaştırmaktadır.

125
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileFruitPopup;
private ToolStripMenuItem m_fileCloseItem;
private ToolStripMenuItem m_fileFruitAppleItem;
private ToolStripMenuItem m_fileFruitOrangeItem;
private ToolStripMenuItem m_fileFruitBananaItem;

private MenuStrip m_otherMenu;

public MyForm()
{
this.Text = "Sample Menu";

m_mainMenu = new MenuStrip();

m_filePopup = new ToolStripMenuItem();


m_filePopup.Text = "&File";

m_fileOpenItem = new ToolStripMenuItem();


m_fileOpenItem.Text = "&Open";
m_fileOpenItem.Click += new
EventHandler(m_fileOpenItem_Click);
m_fileOpenItem.BackColor = Color.Turquoise;
m_fileOpenItem.ForeColor = Color.Red;
m_fileOpenItem.ShortcutKeys = Keys.Control |
Keys.O;
m_fileOpenItem.AutoToolTip = true;
m_fileOpenItem.ToolTipText = "Dosya açar";

m_fileFruitPopup = new ToolStripMenuItem();


m_fileFruitPopup.Text = "&Fruit";

126
m_fileCloseItem = new ToolStripMenuItem();
m_fileCloseItem.Text = "&Close";
m_fileCloseItem.Click += new
EventHandler(m_fileCloseItem_Click);
m_fileCloseItem.Enabled = false;

m_fileFruitAppleItem = new ToolStripMenuItem();


m_fileFruitAppleItem.Text = "&Apple";
m_fileFruitAppleItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitOrangeItem = new ToolStripMenuItem();


m_fileFruitOrangeItem.Text = "&Orange";
m_fileFruitOrangeItem.Click += new
EventHandler(fruitClickHandler);

m_fileFruitBananaItem = new ToolStripMenuItem();


m_fileFruitBananaItem.Text = "&Banana";
m_fileFruitBananaItem.Click += new
EventHandler(fruitClickHandler);
m_fileFruitBananaItem.CheckOnClick = true;

m_fileFruitPopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileFruitAppleItem,
m_fileFruitOrangeItem, m_fileFruitBananaItem });
m_filePopup.DropDownItems.AddRange(new
ToolStripItem[]
{ m_fileOpenItem, m_fileFruitPopup,
m_fileCloseItem });
m_mainMenu.Items.Add(m_filePopup);

m_otherMenu = new MenuStrip();

m_otherMenu.Items.Add(new
ToolStripMenuItem("Other"));
m_otherMenu.Visible = false;

this.Controls.Add(m_mainMenu);
this.Controls.Add(m_otherMenu);
}

void m_fileFruitAppleItem_Click(object sender,


EventArgs e)
{
m_fileFruitAppleItem.Checked = true;
}

void m_fileCloseItem_Click(object sender, EventArgs e)


{
m_fileOpenItem.Enabled = true;

127
m_fileCloseItem.Enabled = false;
}

void fruitClickHandler(object sender, EventArgs e)


{
//ToolStripMenuItem mi =
(ToolStripMenuItem)sender;

//mi.Checked = !mi.Checked;
}

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName + " Seçildi");

m_fileOpenItem.Enabled = false;
m_fileCloseItem.Enabled = true;

m_mainMenu.Visible = false;
m_otherMenu.Visible = true;
}
}
}
}

Menü işlemlerinin diğer ayrıntıları sonraki konularda kısmen ele alınacaktır.

STANDART DİALOG PENCERELERİNİN KULLANIMI

Open, Save, Color dialog pencereleri aslında API fonksiyonları tarafından


sağlanan standart dialog pencerelerdir. Bu pencerelerin açılması ve ilgili
seçimin yapılması oldukça basit bir biçimde gerçekleştirilebilmektedir.

Open DİALOG PENCERESİNİN KULLANIMI

Open dialog penceresi OpenFileDialog sınıfı ile temsil edilmiştir. Bu sınıf


FileDialog sınıfından türetilmiştir.

CommonDialog

FileDialog

128
OpenFileDialog SaveFileDialog
Programcı OpenFileDialog türünden bir nesneyi default başlangıç
fonksiyonu ile yaratır ve sınıfın ShowDialog fonksiyonunu çağırır. Örneğin:

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
//...
}
}

ShowDialog fonksiyonunun geri dönüş değeri, DialogResult türünden bir


enumdur. Pencereden hangi tuşa basılarak çıkıldığını anlamak için kullanılır.

OpenDialog penceresi dosyayı açmaz. Yalnızca bir dosyanın


seçilmesini sağlar. Yani programcı, bu dialog penceresinden seçilen
dosyanın yol ifadesi elde etmektedir. OpenFileDialog sınıfının FileDialog
sınıfından gelen FileName isimli string türden propertye elemanı, seçilen
dosyanın yol ifadesini vermektedir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Sınıfın Filter isimli string türden property elemanı, ShowDialog


fonksiyonu çağrılmadan önce doldurulur. Bu eleman filtreleme
belirlemelerini içerir. Filtreleme yazısı çubuk karakteri ile ayrılmış çiftlerden
oluşur. Çubuk karakterinin solundaki yazı filtreleme yazısını, sağındaki yazı
gerçek yazı filtreleme bilgisini içerir. Örneğin:

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";

129
if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Eğer gerçek filtreleme birden fazla joker ifade içeriyorsa “;” ile devam
edilmelidir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Default durumda ilk filtreleme bilgisi aktiftir. Fakat sınıfın FilterIndex


elemanı ile istenilen bir filtreleme yazısının, pencere açıldığında
görüntülenmesi sağlanabilir. Index 1’den başlamaktadır.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Sınıfın FileDialog sınıfından gelen string türden DefaultExt


property elemanı, eğer dosya uzantı verilemezse eklenecek default
uzantıyı belirtmektedir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

130
ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|
*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Nesne yaratıldığında bu property boş string içerir.

Sınıfın FileDialog sınıfından gelen string türden InitialDirectory


property elemanı, dialog penceresi ilk açıldığında hangi dizinin
görüntüleneceğini belirtir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";
ofd.InitialDirectory = "C:\\windows";

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Sınıfın FileDialog sınıfından gelen bool türden ValidateNames isimli


property elemanı, klavyeden girilen dosyanın olup olmadığını birinci elden
kontrol edilmesini sağlamaktadır. Default durum true‘dir. Yani kontrol
yapılmaktadır.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";
ofd.InitialDirectory = @"C:\windows";

131
ofd.ValidateNames = false;

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Sınıfın FileDialog sınıfından gelen string türden Title isimli property


elemanı, dialog penceresinin başlık yazısını belirlemede kullanılır.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";
ofd.InitialDirectory = @"C:\windows";
ofd.ValidateNames = false;
ofd.Title = "Bir dosya seçiniz...";

if (ofd.ShowDialog() == DialogResult.OK)
{
MessageBox.Show(ofd.FileName);
}
}

Aslında OpenDialog penceresi ile birden fazla dosya da seçilebilmektedir.


Bunun için sınıfın bool türden MultiSelect elemanı bunu belirlemek için
kullanılır. Bu eleman default false durumundadır. Şüphesiz çoklu seçim
sonucunda tek bir dosya değil birden fazla dosya elde edilir. Seçilen tüm
dosyalar bir dizi halinde FileNames propertysi ile elde edilir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";
ofd.InitialDirectory = @"C:\windows";
ofd.ValidateNames = false;
ofd.Title = "Bir dosya seçiniz...";
ofd.Multiselect = true;

132
if (ofd.ShowDialog() == DialogResult.OK)
{
foreach (string s in ofd.FileNames)
MessageBox.Show(s);
}
}

Sınıfın bool türden RestoreDirectory property elemanı, dialog penceresi


kapatıldığında geçerli dizinin dikkate alınmasını sağlar. Bu propertynin
default değeri false biçimindedir.

void m_fileOpenItem_Click(object sender, EventArgs e)


{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "Text Files|*.txt;*.doc|All Files (*.*)|


*.*";
ofd.FilterIndex = 2;
ofd.DefaultExt = ".doc";
ofd.InitialDirectory = @"C:\windows";
ofd.ValidateNames = false;
ofd.Title = "Bir dosya seçiniz...";
ofd.Multiselect = true;
ofd.RestoreDirectory = true;

if (ofd.ShowDialog() == DialogResult.OK)
{
foreach (string s in ofd.FileNames)
MessageBox.Show(s);
}
}

RESİM İŞLEMLERİ

.Net ortamında kullanılan GDI+ isimli grafik kütüphanesi, tüm


resimleri formatı ne olursa olsun aynı arayüzle kullanmaya izin
vermektedir. Örneğin; biz bmp, jpg, png, gif, tif uzantılı dosyaların
hepsi, resim görüntülemek için doğrudan kullanılabilir.
Image isimli sınıf abstract bir sınıftır ve ortak bir arayüz
sunmaktadır. Bitmap sınıfı, Image sınıfından türetilmiştir ve somut bir
sınıftır. .Net içerisinde pek çok durumda bizden bir Image istenmektedir.
Biz Image türünden bir nesne yaratamayacağımıza göre bir Bitmap
türünden nesne yaratarak bu isteği karşılarız. Image sınıfındaki pek çok
abstract fonksiyon Bitmap sınıfında override edilmiştir.

Image ve Bitmap sınıfları, System.Drawing isim alanı içerisindedir.


Bitmap sınıfının 3 başlangıç fonksiyonu çok sık kullanılmaktadır:

133
public Bitmap (string filename)

Fonksiyon parametre olarak resim dosyasının yol ifadesini alır.

public Bitmap (Stream stream)

Biz bir resim dosyasını FileStream referansı ile açıp bu Stream referansını
veririz.

public Bitmap (int Width, int Height)

Bu başlangıç fonksiyonu ile biz, belirli bir genişlik ver yükseklikte bellekte
resim oluşturabiliriz. Bu resim daha sonra diske save edilebilir.

Bitmap nesnesi yaratılırken çeşitli exceptionlar oluşabilir. Bu nedenle bu


nesnenin try-catch blokları içerisinde yaratılması daha anlamlıdır.

Bir resim Bitmap nesnesi olarak oluşturulduktan sonra DrawPicture


sınıfının Graphics sınıfının fonksiyonları ile doğrudan çalışma alanına
çizilebilir. Fakat bu çizim konusunda ele alınacaktır. Ancak PictureBox
isimli kontrol, bir image alarak onu kendi alt penceresinde göstermektedir.

PictureBox kontrolü oldukça basit bir kontroldür. Kontrol sınıfın


default başlangıç fonksiyonu ile yaratılır. Sonra sınıfın public Image
property elemanına, görüntülenecek resim girilir. PictureBox sınıfının
SizeMode isimli PictureBoxSizeMod enum türünden bir propertysi de
vardır. Bu enum türünün elemanları şunladır:

Normal: Propertynin default değeri bu biçimdedir. Büyütme küçültme


yapılmaz. Resim kontrolün sol üst köşesinden itibaren orijinal büyüklükte
görüntülenmeye çalışılır. Kontrol resme göre küçükse, resmin yalnızca
belirli bir alanı görüntülenir.

AutoSize: Bu seçenekte resim üzerinde büyütme küçültme uygulanmaz.


PictureBox hangi büyüklükte olursa olsun resim boyutuna çekilir. Bu
seçenek, resmi orijinal boyutunda görmek için idealdir.

StretchImage: Bu seçenekte resim büyütülüp küçültülerek PictureBox


boyutuna getirilir.

CenterImage: Burada resim kontrolün ortasında görüntülenmeye çalışılır.


Bu seçenekte resim kontrolden küçükse, kontrolün ortasında görüntülenir.
Fakat resim kontrolden büyükse, resmin ortası kontrolün tamamında
görüntülenir.

134
Zoom: Bu seçenekte resim kontrolde belirtilen Size dikkate alınarak zoom
edilir. Yani bu seçenekte büyütme ve küçültme genişlik ve yükseklik oranı
korunacak şekilde yapılmaktadır.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}
class MyForm : Form
{
private PictureBox m_pictureBox;

public MyForm()
{
this.Text = "Sample Picture";
this.Resize += new EventHandler(MyForm_Resize);

m_pictureBox = new PictureBox();


m_pictureBox.Size = this.ClientSize;
m_pictureBox.SizeMode =
PictureBoxSizeMode.StretchImage;

try
{
Bitmap bmp = new
Bitmap(@"E:\DotNetAppBasic\Picture1\Generic\Beatles2.jpg");

m_pictureBox.Image = bmp;

}
catch (Exception e)
{
MessageBox.Show(e.Message);
}

this.Controls.Add(m_pictureBox);
}

void MyForm_Resize(object sender, EventArgs e)


{
m_pictureBox.Size = this.ClientSize;

135
}
}
}

Anahtar Notlar

Programlama yoluyla sanki formun çarpı ikonuna klik yapma etkisi close
fonksiyonu ile sağlanabilir.

using System;
using System.Windows.Forms;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private PictureBox m_pictureBox;
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileExitItem;

public MyForm()
{
this.Text = "Sample Picture";
this.Resize += new EventHandler(MyForm_Resize);
this.BackColor = SystemColors.Window;

m_mainMenu = new MenuStrip();


m_filePopup = new ToolStripMenuItem("&File");
m_fileOpenItem = new ToolStripMenuItem("&Open");
m_fileExitItem = new ToolStripMenuItem("&Exit");

m_filePopup.DropDownItems.AddRange(new
ToolStripItem[] {m_fileOpenItem, m_fileExitItem});
m_mainMenu.Items.Add(m_filePopup);

m_fileOpenItem.Click += new
EventHandler(openFileHandler);

136
m_fileExitItem.Click += new
EventHandler(exitFileHandler);

m_pictureBox = new PictureBox();


m_pictureBox.Size = this.ClientSize;
m_pictureBox.SizeMode =
PictureBoxSizeMode.StretchImage;

this.Controls.Add(m_mainMenu);
this.Controls.Add(m_pictureBox);
}

void MyForm_Resize(object sender, EventArgs e)


{
m_pictureBox.Size = this.ClientSize;
}

private void openFileHandler(object sender, EventArgs


e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Picture files|
*.jpg;*.bmp;*.tif;*.gif;|All files|*.*";

if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap bmp = new Bitmap(ofd.FileName);

m_pictureBox.Image = bmp;

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

private void exitFileHandler(object sender, EventArgs


e)
{
this.Close();
}
}
}

137
Bitmap sınıfının Image sınıfından gelen int türden Width ve Height
propertyleri, resmin genişlik ve yükseklik değerlerini vermektedir.

Anahtar Notlar

System.IO isim alanındaki Path isimli sınıf, yalnızca static elemanlardan


oluşmaktadır. Bu sınıfın static fonksiyonları, bir yol ifadesine ilişkin bazı
bilgileri ayrıştırıp vermektedir. Örneğin; sınıfın GetFileName isimli static
fonksiyonu, bir yol ifadesini parametre olarak alıp, yol ifadesine ilişkin
dosya ismi ve uzantıyı ayrıştırmaktadır. GetExtension isimli fonksiyon
yalnızca uzantıyı vermektedir. GetDirectoryName isimli fonksiyon
dosyanın bulunduğu dizini vermektedir.

using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());
}
}

class MyForm : Form


{
private PictureBox m_pictureBox;
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileExitItem;

public MyForm()
{
this.Text = "Sample Picture";
this.Resize += new EventHandler(MyForm_Resize);
this.BackColor = SystemColors.Window;

m_mainMenu = new MenuStrip();


m_filePopup = new ToolStripMenuItem("&File");
m_fileOpenItem = new ToolStripMenuItem("&Open");
m_fileExitItem = new ToolStripMenuItem("&Exit");

138
m_fileOpenItem.ShortcutKeys = Keys.Control |
Keys.O;
m_fileExitItem.ShortcutKeys = Keys.Control |
Keys.X;

m_filePopup.DropDownItems.AddRange(new
ToolStripItem[] {m_fileOpenItem, m_fileExitItem});
m_mainMenu.Items.Add(m_filePopup);

m_fileOpenItem.Click += new
EventHandler(openFileHandler);
m_fileExitItem.Click += new
EventHandler(exitFileHandler);

m_pictureBox = new PictureBox();


m_pictureBox.Size = this.ClientSize;
// m_pictureBox.SizeMode =
PictureBoxSizeMode.StretchImage;

this.Controls.Add(m_mainMenu);
this.Controls.Add(m_pictureBox);
}

void MyForm_Resize(object sender, EventArgs e)


{
m_pictureBox.Size = this.ClientSize;
}

private void openFileHandler(object sender, EventArgs


e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Picture files|
*.jpg;*.bmp;*.tif;*.gif;|All files|*.*";

if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap bmp = new Bitmap(ofd.FileName);

m_pictureBox.Image = bmp;

this.Text = string.Format("Sample Picture


- {0} ({1}X{2})",Path.GetFileName (ofd.FileName), bmp.Width,
bmp.Height);
}
catch (Exception ex)
{

139
MessageBox.Show(ex.Message);
}
}
}

private void exitFileHandler(object sender, EventArgs


e)
{
this.Close();
}
}
}

Anahtar Notlar

String sınıfının format isimli static fonksiyonu write ve writeline


fonksiyonlarındaki küme parantezli formatlamayı yapar ve bunu bir yazı
olarak geri verir. Örneğin:

this.Text = string.Format("Sample Picture - {0} ({1}X{2})",


Path.GetFileName(ofd.FileName), bmp.Width,
bmp.Height);

Bitmap sınıfının Image sınıfından gelen Save fonksiyonları, ilgili resmi


herhangi bir formatta diskte bir dosya olarak save etmekte kullanılabilir.
Örneğin:

public void Save(string filename, ImageFormat format)

Fonksiyonun birinci parametresi ilgili yol ifadesi, ikinci parametresi


dosyanın hangi formatta save edileceğine ilişkin ImageFormat‘ta bir sınıfı
belirtir. ImageFormat sınıfının static bmp, gif, png, jpeg gibi static property
elemanları ilgili dosya formatını betimlemek için kullanılabilir.

using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;

namespace CSD
{
class App
{
public static void Main()
{
Application.Run(new MyForm());

140
}
}

class MyForm : Form


{
private Bitmap m_bmp;
private PictureBox m_pictureBox;
private MenuStrip m_mainMenu;
private ToolStripMenuItem m_filePopup;
private ToolStripMenuItem m_fileOpenItem;
private ToolStripMenuItem m_fileSaveItem;
private ToolStripMenuItem m_fileExitItem;

public MyForm()
{
this.Text = "Sample Picture";
this.Resize += new EventHandler(MyForm_Resize);
this.BackColor = SystemColors.Window;

m_mainMenu = new MenuStrip();


m_filePopup = new ToolStripMenuItem("&File");
m_fileOpenItem = new ToolStripMenuItem("&Open");
m_fileSaveItem = new ToolStripMenuItem("&Save");
m_fileExitItem = new ToolStripMenuItem("&Exit");

m_fileOpenItem.ShortcutKeys = Keys.Control |
Keys.O;
m_fileSaveItem.ShortcutKeys = Keys.Control |
Keys.S;
m_fileExitItem.ShortcutKeys = Keys.Control |
Keys.X;

m_filePopup.DropDownItems.AddRange(new
ToolStripItem[] {m_fileOpenItem,
m_fileSaveItem, m_fileExitItem});
m_mainMenu.Items.Add(m_filePopup);

m_fileOpenItem.Click += new EventHandler


(openFileHandler);
m_fileSaveItem.Click += new EventHandler
(saveFileHandler);
m_fileExitItem.Click += new EventHandler
(exitFileHandler);

m_pictureBox = new PictureBox();


m_pictureBox.Size = this.ClientSize;
// m_pictureBox.SizeMode =
PictureBoxSizeMode.StretchImage;

this.Controls.Add(m_mainMenu);

141
this.Controls.Add(m_pictureBox);
}

void MyForm_Resize(object sender, EventArgs e)


{
m_pictureBox.Size = this.ClientSize;
}

private void openFileHandler(object sender, EventArgs


e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Picture files|*.jpg;*.bmp; *.tif;
*.gif;|All files|*.*";

if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
m_bmp = new Bitmap(ofd.FileName);

m_pictureBox.Image = m_bmp;

this.Text = string.Format("Sample Picture


- {0} ({1}X{2})",
Path.GetFileName(ofd.FileName),
m_bmp.Width, m_bmp.Height);

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

private void saveFileHandler(object sender, EventArgs


e)
{
if (m_bmp == null)
{
MessageBox.Show("Cannot save picture file!..",
"Error", MessageBoxButtons.OK);
return;
}

SaveFileDialog sfd = new SaveFileDialog();


sfd.Filter = "Picture files| *.jpg;*.bmp; *.tif;
*.gif;|All files|*.*";

142
if (sfd.ShowDialog() == DialogResult.OK)
{
ImageFormat ifo;
string ext = Path.GetExtension(sfd.FileName)
.ToLower();

try
{
switch (ext)
{
case ".bmp":
ifo = ImageFormat.Bmp;
break;
case ".jpg":
ifo = ImageFormat.Jpeg;
break;
case ".gif":
ifo = ImageFormat.Gif;
break;
case ".png":
ifo = ImageFormat.Png;
break;
default:
MessageBox.Show("Invalid
extension!");
return;
}

m_bmp.Save(sfd.FileName, ifo);

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

private void exitFileHandler(object sender, EventArgs


e)
{
this.Close();
}
}
}

Bitmap sınıfının Image sınıfından gelen RawFormat isimli property


elemanı, ImageFormat sınıfın türündendir. Resmin doğal formatının ne
olduğu bu elemanla alınabilir.

143
Bitmap sınıfının Image sınıfından gelen PixelFormat isimli property
elemanı, PixelFormat isimli enum türündendir. Resmin pixel formatını
vermektedir.

Bitmap sınıfının GetPixel ve SetPixel fonksiyonları ilgili resmin


belirli bir x,y pixelinin rengini alıp set etmek için kullanılır.

Bitmap sınıfı Image sınıfından türetilmiştir ve Image sınıfı da


IDisposable arayüzünü desteklemektedir.

Partial SINIF BİLDİRİMLERİ

Partial bildirimi C# Framework 2.0 eklenmiştir. Bu bildirim bir sınıfı


parçalara ayırarak birden fazla yerde hatta farklı kaynak dosyalarda
bildirmek için kullanılır. Bir sınıf partial anahtar sözcüğü ile ilk kez
bildirilmişse, artık o isim alanındaki aynı isimli diğer bildirimlerde de
zorunlu olarak partial anahtar sözcüğü kullanılmak zorundadır. Örneğin:

partial class A
{
//...
}
//...
//...
partial class A
{
//...
}

Her partial sınıf bildirimi daha öncekine ekleme anlamındadır.

using System;
using System.Collections;
using System.Drawing;

namespace CSD
{
class App
{
public static void Main()
{
Sample.Bar();
}
}

partial class Sample

144
{
public static void Foo()
{

}
}

partial class Sample


{
public static void Bar()
{

}
}
}

VISUAL STUDIO PROJE ŞABLONUNU KULLANARAK C# PROJESİ


OLUŞTURMA

Visual Studio’da File/Project/Visual C#/Windows seçildiğinde sağ tarafta


WindowsApplication şablonu seçilerek, görsel belirlemelerle projenin çatısı
kolay bir biçimde oluşturulabilir. Bu proje seçeneği seçildiğinde aşağıdaki
dosyaları oluşturur:

Program.cs : Bu dosyanın içerisinde Program isimli bir sınıf bildirilmiş ve


Main fonksiyonu bu sınıfın içerisine yerleştirilmiştir. Application.Run
fonksiyonu da bu dosyanın içerisindedir.

Form1.cs: Sihirbaz bu dosyanın içerisinde partial bir biçimde Form1


isimli bir sınıfı Form sınıfından türetmiştir ve bu sınıfının başlangıç
fonksiyonunda InitializeComponent isimli fonksiyonu çağırmıştır:

public partial class Form1 : Form


{
public Form1()
{
InitializeComponent();
}
}

Görüldüğü gibi Form1 sınıfı türünden bir nesne yaratıldığında başlangıç


fonksiyonu çağrılacak, bu fonksiyonda InitializeComponent
fonksiyonunu çağıracaktır.

Form1.Designer.cs: Bu dosyanın içerisinde Form1 sınıfının diğer partial


bildirimi vardır. Görüldüğü gibi Form1 sınıfı Form1.cs ve Form1.Designer.cs
olmak üzere 2 dosyada bildirilmiştir. Form1.Designer.cs dosyası içerisinde

145
Dispose ve InitializeComponent fonksiyonları tanımlanmıştır. Microsoft
Form1.Designer.cs dosyasının programcı tarafından manual bir biçimde
değiştirilmesini tavsiye etmemektedir. Her ne kadar bu dosya üzerinde
bazı masum değişiklikler probleme yol açmıyorsa da prensip olarak
bunlardan kaçınmak gerekir.

AssemblyInfo.cs: Bu dosyanın içerisinde assembly düzeyinde


özniteliklendirme bilgileri yerleştirilmiştir.

Resources.resx, Resources.Desinger.cs: Bu dosyalar global düzeydeki


kaynaklara ilişkindir.

Sihirbaz yukarıdaki tüm dosyalardaki elemanları proje ismine ilişkin


bir isim alanına yerleştirir. Programcı ilk aşamada bu isim alanını
değiştirmesi tavsiye edilir. Bu değişiklik için Edit/Find and Replace(Ctrl +
H) seçilir. Değiştirme alanı Current Project yapılır. Yani değişiklikler
projedeki tüm dosyalara uygulanır.

Programcı daha sonra Form1 isimli sınıfın ismini anlamlı bir biçimde
değiştirmelidir. Dosyalar açık olmamak koşuluyla Form1.cs dosyasının ismi
değiştiğinde bu sınıfın ismi de değişmektedir.

IDE bu işlemleri yapacak kodu InitializeComponent fonksiyonunun


içine yazar. Anımsanacağı gibi InitializeComponent türetilen Form
sınıfının başlangıç fonksiyonundan çağrılmaktadır.

Anahtar Notlar

Control sınıfının string türünden Name isimli property elemanı tamamen


programcının kontrolü betimlemek için kullandığı bir ismi içerir. Name
propertysi kontrol tarafından kullanılmaz. Bundan programcı
faydalanmaktadır. Bunu IDE’nin kendisi de kullanmaktadır.

Herhangi bir zaman Form dosyasının üzerine klik yapılarak tasarım


ekranına geçilebilir. Bu bir form editördür. Form editöründe farenin sağ
tuşuna basılarak Properties menüsüne geçilebilir. Properties menüsü
formun çeşitli propertylerini görsel olarak değiştirmemize olarak
sağlamaktadır. Burada propertyleri değiştirdiğimizde otomatik olarak bu
propertylere değer yerleştiren atama işlemleri, InitializeComponent
fonksiyonu içerisine yerleştirilir. Form seçildiği zaman araç kutusu
görüntülenebilir. Programcı buradan sürükle ve bırak yöntemiyle herhangi
kontrolü forma taşıyabilir.

Herhangi bir kontrol seçilip Properties menüsüne geçilirse o


kontrole ilişkin propertyler üzerinde çalışılabilir. Bir kontrol form üzerine

146
taşındığında IDE bu kontrol için sınıfta bir private veri elemanı tanımlar
sonra InitializeComponent fonksiyonu içerisine nesneyi yaratan ve
propertylerini set eden kodları yerleştirir. IDE kontroller için otomatik
olarak deve notasyonunda, sırayla numaralandırma yaparak isimlendirme
uygular (Örneğin: listBox1, listBox2 gibi). Programcının bu isimleri
Properties menüsünden değiştirmesi tavsiye edilmektedir. IDE kontrolün
Name propertysini isimlendirme amacıyla kullanmaktadır. Yani programcı
Name propertysini değiştirerek kontrolün ismini değiştirebilir.

Çoğu kez programcı görsel belirlemelerin dışında da Form sınıfına


veri elemanları yerleştirmek isteyebilir. Ya da Form sınıfının başlangıç
fonksiyonuna bir takım kodlar eklemek isteyebilir. Bunun için en iyi yer,
Form sınıfının başlangıç fonksiyonunda InitializeComponent
fonksiyonunun çağrılmasından sonraki bölümdür. Örneğin; programcı bir
şekilde form editörün dışında manual olarak bir düğme kontrolünü
eklemek istesin. Bu işlemi şöyle yapmalıdır:

public partial class TestWizardForm : Form


{
private Button m_buttonOK;

public TestWizardForm()
{
InitializeComponent()

//...

m_buttonOK = new Button();


m_buttonOK.Text = “&Ok”;
this.Controls.Add(m_buttonOK);
}
}

Properties menüsünde, şimşek menüsüne tıklanırsa ilgili kontrolün


event listesi görüntülenir. Programcı buradaki herhangi bir event eleman
double klik yaparsa IDE otomatik olarak o event elemanına bir fonksiyon
girer ve fonksiyonu içi boş olarak yazar. Event fonksiyonları kontrolün ismi
xxx olmak üzere xxx_yyy biçiminde isimlendirmektedir. Burada yyy ilgili
event’in ismidir. Programcı isterse bu ismi değiştirebilir. Ya da fonksiyonun
istediği bir isme ilişkin olmasını sağlayabilir. Bazen farklı eventlere aynı
fonksiyon girmek gerekebilir. Bu durumda bunların listesi verilip seçim
sağlanabilmektedir.

Programcı araç kutusundan MenuStrip seçerek menü oluşturabilir.


Sonra bütün popup ve menü elemanlarını görsel bir biçimde oluşturabilir.
Bu işlem karşısında IDE tüm popup ve normal menü elemanları için ayrı
birer veri elemanı oluşturur. İlgili eklemeyi yapan kodu

147
InitializeComponent fonksiyonu içerisine yerleştirir. Programcının menü
elemanlarının isimlerini de değiştirmesi tavsiye edilir. En çok kullanılan
event işlemleri double klik ile otomatize edilmiştir.

Bazı kontroller form editörde çizildiğinde editörün sağ üst köşesinde


bir ok görülür. En çok kullanılan özellikler oradaki bir menüye eklenmiştir.
Örneğin; MenuStrip için burada “Insert Standart Items” seçildiğinde klasik
menü sistemi yaratılmaktadır. Programcı bu genel menüden hareketle
istediği elemanları silip istediklerini bu menüye ekleyebilir.

DİALOG PENCERELERİNİN YARATILMASI VE KULLANILMASI

Program çalışırken birtakım girişler ve mesajlar için oluşturulmuş


olan owned pencerelere, dialog pencereleri denir. Örneğin; MessageBox
standart bir dialog penceresidir. Dialog pencereleri modal ve modeless
olmak üzere ikiye ayrılır. Modal pencerelerde, dialog penceresi açıkken
arkadaki pencereler üzerinde işlem yapamayız. Ayrıca pencereyi
kapatmadığız sürece dialog penceresini açan pencerede kalır. Hâlbuki
modeless pencerelerde, pencere owned olarak hep yukarıda gözüküyor
olsa da arkadaki pencereler üzerinde yine işlemler yapılabilir. Pencereyi
açan fonksiyon hemen sonlanır. Akış devam eder.

Dialog penceresi bir forma çok benzemektedir. Ancak form default


olarak owned bir durumda değildir.

MODAL DİALOG PENCERELERİNİN OLUŞTURULMASI VE


KULLANILMASI

Modal bir dialog penceresi oluşturmak için şunların yapılması gerekir:

1- Programcı Form sınıfından dialog penceresi için bir sınıf türetmelidir.


IDE’den bu işlem Add/New Item/Windows Form seçeneği ile
yapılabilir. IDE’den bu işlem yapıldığında IDE yeni form için yine
FormNN.cs ve FormNN.designer.cs dosyalarını oluşturur. Oluşturulan
bu yeni formun yalnızca kod alt yapısı IDE tarafından hazırlanır.
Fakat bu form henüz hiçbir yerden kullanılabilir değildir. Şüphesiz
oluşturulan her yeni form için, o forma özel bir
InitializeComponent fonksiyonu bulunmaktadır. Yani yeni
oluşturulan form sınıfı için new operatörü ile yaratım yapılmak
istendiğinde bu InitializeComponent fonksiyonu çalıştırılacaktır.
Özetle her dialog penceresi için ayrı bir form oluşturmak gerekir.

2- Programcı dialog penceresini açmak istediği zaman new operatörü ile


yeni bir form nesnesi yaratmalı ve sınıfının ShowDialog
fonksiyonunu çağırmalıdır.

148
3- Dialog penceresini kapatmak için Form sınıfının DialogResult
propertysine atama yapmak yeterlidir. Bu property DialogResult
isimli enum türündendir. Propertynin set bölümde aynı zamanda
Close işlemi yapılmaktadır. Yani aslında dialog penceresi Close
fonksiyonu ile kapatılabilir. Fakat DialogResult propertysine atama
yapılırken zaten Close fonksiyonu çağrılmaktadır. Dialog penceresini
açmakta kullandığımız ShowDialog fonksiyonu da DialogResult geri
dönüş değerine sahiptir. Yani dialog penceresinde atadığımız değer,
ShowDialog fonksiyonunun geri dönüş değeri olmaktadır. Bu
durumda bir dialog penceresi şöyle kapatılabilir:

private void m_buttonOk_Click(object sender, EventArgs e)


{
this.DialogResult = DialogResult.OK;
}

private void m_buttonCancel_Click(object sender, EventArgs e)


{
this.DialogResult = DialogResult.Cancel;
}

Bir dialog penceresinde genellikle OK ve Cancel tuşları bulunmaktadır.


Kullanıcı Cancel tuşuna bastığında, programcı girilmiş olan bilgileri dikkate
almamalıdır. Bu işlem basit olarak şöyle sağlanabilir:

DialogForm df = new DialogForm;

if(df.ShowDialog() == DialogResult.OK)
{
//...
}

DİALOG PENCERESİNDEKİ BİLGİLERİN KULLANILMASI

Dialog penceresi kapatıldığında, programcı kullanıcının girmiş olduğu


bilgileri elde etmek ister. Dialog penceresi ve üzerindeki kontroller yok
edilmiş olsa da dialog nesnesi ve kontrol nesneleri henüz yaşamaktadır.
Yani programcı Form sınıfının veri elemanı olan TextBox içerisinden girilmiş
olan bilgiyi alabilir. Fakat bu kontroller formun private bölümünde
olduğuna göre programcı bunlara erişemeyecektir. Bu durumda en uygun
yöntem programcının almak istediği her kontrol bilgisi için ona özgü public
property yazmasıdır. Örneğin:

public string Name

149
{
get
{
return m_nameTextBox.Text;
}
set
{
m_nameTextBox.Text = value;
}
}

Böylece dialog penceresinden OK tuşuyla çıkıldığında bu property yoluyla


değer elde edilebilir. Propertynin read/write olması dialog penceresi
açıldığında bazı değerlerin default görüntülenmesini sağlamaktır.

DialogForm df = new DialogForm();

dr.Name = “Kaan Aslan”;

if(df.ShowDialog() == DialogResult.OK)
{
//...
}

MODELESS DİALOG PENCERELERİ


{05.01.2008 - Cumartesi}

Modeless dialog pencereleri yine bir owned penceredir. Yani her


zaman üst pencerenin üzerinde görüntülenir. Fakat bu pencereler
açıldığında, biz arka plandaki pencerelere erişip bu pencerelerde işlem
yapabiliriz. Örneğin; tipik olarak “Find and Replace” dialog penceresi bu
şekildedir.

Modeless dialog pencereleri oluşturmak için, model pencere


oluştururken yapılan işlemlere benzer işlemler yapılır. Birkaç önemli
farklılık vardır. Farklılıklar şunlardır:

1- Modeless pencere, önce owned pencere duruma getirilmelidir. Bunun


için, Form sınıfının Owner isimli property elemanına, modeless
pencerenin altında görüntüleneceği pencerenin referansı girilmelidir.

2- Modeless dialog penceresi, ShowDialog fonksiyonu ile değil Show


fonksiyonu ile görüntülenmelidir.

3- Modeless dialog penceresi, Show fonksiyonu ile açıldığında, akış


hemen Show fonksiyonundan çıkar. Bu nedenle modeless dialog
penceresi, DialogResult elemanına değer atayarak değil doğrudan

150
Close fonksiyonu ile kapatılır. Zaten Show fonksiyonunun geri dönüş
değeri DialogResult türünden değil, void türdendir.

Anahtar Notlar
Bir form üç biçimde açılabilir.

1-Modal Olarak: Bu durumda form ShowDialog fonksiyonu ile


görüntülenir.
2-Bağımsız İkinci Bir Form Olarak: Bu durumda form Show fonksiyonu ile
açılır.
3-Modeless Olarak: Bu durumda formun Owner propertysi set edildikten
sonra

Modal pencereler, genellikle bilgi almak için kullanılır. Hâlbuki modeless


pencereler, arka plandaki pencereler için bazı işlemler uygulamak için
kullanılır. Örneğin; “Find and Replace” penceresinde her “Find” yapıldığında
arka plandaki pencerede bir takım değişiklikler olmaktadır. O halde tipik
olarak modeless pencereden Owner pencerenin kullanılması gerekir.

Modeless dialog penceresinden, arka plandaki pencereyi kullanmak


kolaydır. Zaten programcı Owner olarak bu pencereyi getirmektedir. Owner
propertysi read write olduğuna göre biz buradan değer alabiliriz.

Programcı Owner elemanı Form sınıfı türünden elde eder. Hâlbuki onu
arkada bulunan ana form olarak kullanmak isteyecektir. Bu durumda
aşağıya doğru bilinçli dönüştürme yapmak gerekmektedir. Örneğin:

MainForm mf = (MainForm)this.Owner;

Bir formdan diğer bir formun kullanılması klasik bir problemdir.


Örneğin; dialog penceresi modal olsa ya da ikinci bir form söz konusu olsa
ve biz diğer formu kullanmak istesek ne yapmalıyız? Önerilen
yöntemlerden biri, ilgili forma yeni bir başlangıç fonksiyonu eklemek ve o
başlangıç fonksiyonu yoluyla, ana formun referansını diğer forma
aktarmaktır. Örneğin:

public DialogForm(MainForm mf) : this();


{
m_mainForm = mf;
}

Şüphesiz aynı işlem property yoluyla da yapılabilir (tıpkı Owner


propertysinde olduğu gibi). Örneğin:

public MainForm MainForm


{

151
set
{
m_mainForm = value;
}
}

Kullanımda şöyle olabilir:

Dialogform df = new DialogForm();


df.MainForm = this;

df.ShowDialog();

Anahtar Notlar

Bir uygulamaya ilişkin bütün ana pencereler Application sınıfının static


OpenForm property elemanı ile elde edilmektedir.

DİALOG PENCERELERİNDE GEÇERLİLİK DENETİMİ

Bir model dialog penceresi açıldığında kullanıcı bazı bilgiler girer. Peki
bazı alanları boş bırakırsa ya da bazı alanlara uygun olmayan değerler
girerse? Tipik olarak bu tür geçerlilik (validation) işlemleri “Ok” , “Cancel”
tuşlarına basıldığında toptan yapılabilir. Fakat .Net Framework 2.0 ile
beraber bu konuda bazı pratik yöntemlerde de önerilmiştir.

.Net’te Control sınıfından gelen Validating ve Validated isimli iki


eleman vardır. Ne zaman kullanıcı kontrolden “Tab” tuşuyla ya da fareyle
çıkmak isterse Validating eventi tetiklenmektedir. Programcı kontrolün,
geçerlilik kontrolünü bu event içinde yapabilir ve eğer geçerlilik uygun
değilse kontrolden ayrılmaya izin vermeyebilir.

Validating isimli event, CancelEventHandler isimli event


türündendir. Bu delegenin mesaj parametre sınıfı CancelEventArgs
sınıfıdır. Bu sınıfının bool türden Cancel isimli public property elemanı,
geçerliliğe onay verilip verilemeyeceğini belirtir. Eğer bu property true
girilirse, kontrolden çıkmak mümkün olmaz. False girilirse kontrolden
çıkılabilir. Sonuç olarak geçerlilik başarısız ise programcı bu propertye true
değerini atamalıdır.

TextBoxName için;

private void m_nameTextBox_Validating(object sender,


CancelEventArgs e)
{

152
if (m_nameTextBox.Text.Trim() == "")
{
MessageBox.Show("İsim girilmedi");
e.Cancel = true;
}
}

TextBoxNo için;

private void m_noTextBox_Validating(object sender,


CancelEventArgs e)
{
int val = 0;

try
{
val = int.Parse(m_noTextBox.Text);
}
catch (Exception ex)
{
MessageBox.Show("Sayı yanlışgirildi!..");
e.Cancel = true;
return;
}

if (val < 0 || val >= 100)


{
MessageBox.Show("Yanlış değer");
e.Cancel = true;
}

Validating ve Validated elemanlarının dışında birde Form sınıfının


AutoValidate isimli AutoValidate isimli enum türünden bir property
elemanı vardır (aslında bu property elemanı ContainerControl isimli
sınıftan gelmiştir). Bu enum türünün bazı önemli elemanları vardır. Default
durumda bu property elemanı EnablePreventFocusChange durumdadır.
AutoValidate bu değerde ise Cancel propertysi true yapıldığında
kontrolden dışarı çıkılamaz. Eğer AutoValidate elemanı Disable olarak
girilirse, Cancel true olsa bile kontrolden çıkış mümkün olur (bu durumda
Validating eventi zaten tetiklenmez). Yani görüldüğü gibi AutoValidate,
ana şalter gibi görev yapmaktadır. Eğer bu property
EnableAllowFocusChange biçiminde ise, geçerlilik kontrolü yine yapılır.
Fakat kontrolden çıkış sağlanır. Bu propertye Inherit atanırsa propertynin
değeri taban sınıftan alınır.

Dialog penceresinden Cancel tuşuyla çıktığımızda, bir Validating


işleminin yapılması gereksizdir. Zaten Cancel, girilen değerlerin dikkate

153
alınmayacağı anlamına gelir. Fakat Cancel tuşuna tıkladığımızda
kontrolden çıkmayı hedeflediğimizden dolayı yine geçerlilik sorunu ile
karşılaşırız. İşte bunu engellemek için birkaç yöntem önerilebilir. Birincisi
geçici süre AutoValidate işlemini Disable etmek olabilir. Bunun dışında
başka yöntemlerde önerilebilir.

Geçerlilik sınaması konusunun bazı detayları vardır. Örneğin; burada


Validated eventinden bahsedilmemiştir. Ayrıca kontrol içerisindeki
bilgilerin geçerliliğinin etkin bir biçimde sınanması için, bazı yardımcı
sınıflara da gerek duyulabilir. Örneğin; TextBox içerisine bir tarih girilebilir
ve tarihin doğruluğu sınanabilir. Microsoft özel bazı formatlarda giriş
yapmaya izin veren MaskedBox isimli kontrolü Framework 2.0’da
kütüphaneye dahil etmiştir. Bu kontrol hiç olmazsa, sayısal girişlerde, tarih
girişlerde geçerlilik sınamasını kendi yapmaktadır.

ListView KONTROLÜ

ListView, bir bakıma ListBox kontrolünün daha gelişmiş halidir.


Kontrolün birkaç modu olmakla beraber en çok kullanılan modu Details
modudur. ListView, Details moda sütunlardan ve satırlardan
oluşmaktadır. Sütunlardan biri anahtar sütundur. Diğer sütunlar anahtar
sütundaki elemanların özelliklerini belirtir. Örneğin; ListView kontrolü ile
dosyalar listelenirken dosya isimli sütun, anahtar sütünü oluşturabilir. Bu
durumda diğer sütunlar, o dosyanın özelliklerini belirtir. ListView kontrolü
ListView isimli sınıfla temsil edilir.

ListView nesnesi, sınıfın başlangıç fonksiyonu ile yaratılır. Sonra


kontrolü Details moduna geçirmek için, sınıfın View property elemanı
kullanılır. View property elemanı, View isimli enum türündendir. Bu
propertyinin default değeri LargeIcon biçimindedir.

Kontrolün çeşitli propertyleri set edildikten sonra programcı, kontrole


sütun eklemelidir. Kontrolün her bir sütunu ColumnHeader isimli bir sınıfla
temsil edilmektedir. ListView kontrolünün, Column isimli property elemanı
ColumnHeaderCollection isimli, collection sınıf türündendir. Programcı
ColumnHeader nesnelerini yaratıp, ColumnCollection’una ekledikten
sonra sütunları eklemiş olur.

ColumnHeader sınıfının Text property elemanı, sütun başlığında


görüntülenecek yazıyı belirtir. Örneğin:

ColumnHeader chName = new ColumnHeader();


chName.Text = "Name";

ColumnHeader chLength = new ColumnHeader();

154
chLength.Text = "Length";

ColumnHeader chDate = new ColumnHeader();


chDate.Text = "Date";

m_lv.Columns.AddRange(new ColumnHeader[] { chName, chLength,


chDate });

ColumnHeaderCollection sınıfının çeşitli parametrik yapıya sahip


başka Add fonksiyonları da vardır. Örneğin; string parametreli Add
fonksiyonu, ColumnHeader nesnesini kendi içerisinde yaratıp Text
propertysini set ettikten sonra ColumnHeader’ı zaten ekler. O halde tek tek
ColumnHeader nesnelerini oluşturmak yerine bu Add fonksiyonundan
aşağıdaki gibi faydalanabiliriz:

m_lv = new ListView();


m_lv.View = View.Details;

m_lv.Columns.Add(“NAme”)
m_lv.Columns.Add(“Length”)
m_lv.Columns.Add(“Date”)

Bu Add fonksiyonları, kendi içerisinde yarattıkları ColumnHeader nesnesi ile


geri dönmektedir.

ListView kontrolüne sütunları ekledikten sonra satırları eklemeliyiz.

{06.01.2008 Pazar}

ListView kontrolünün satırları, ListViewItem sınıfı ile temsil


edilmiştir. ListView sınıfının Items propertysi ListViewItemCollection
isimli collection türündendir. Dolayısıyla ItemsCollection’una ekleme,
ListViewItem nesneleri eklediğimizde, kontrole satır eklemiş oluruz.
ListViewItem nesnesi, sınıfının default başlangıç fonksiyonu ile
yaratılabilir ya da ListViewItem sınıfının string parametreli başlangıç
fonksiyonu kullanılabilir. Bu başlangıç fonksiyon, satırın anahtar sütununun
yazısını oluşturur. Aslında ListViewItem sınıfının benzer biçimde bir Text
propertysi vardır. O halde;

ListViewItem lvi1 = new ListViewItem();


lvi1.Text = “Kaan Aslan”;

ile

ListViewItem lvi1 = new ListViewItem(“Kaan Aslan”);

155
aynı anlamdadır.

Aslında ListView satırlarının her bir sütunu, ListViewSubItem


nesneleri ile temsil edilmektedir. ListViewItem sınıfının SubItems
collection propertysi, ListViewSubItem nesnelerini tutmaktadır. O halde
ListViewItem sınıfı ListViewSubItem nesnelerini, ListView sınıfı da
ColumnHeader ve ListViewItem nesnelerini tutan bir yapıdadır. Programcı
ListViewSubItem nesnelerini ListViewItem nesnesine ekler.
ListViewItem nesnesini de ListView kontrolüne ekler. ListViewSubItem
sınıfı, ListViewItem sınıfı içerisinde bildirilmiştir.

ListViewItem.ListViewSubItem lvsi1 = new


ListViewItem.ListViewSubItem();
lvsi1.Text = "Test";

lvi1.SubItems.Add(lvsi1);

Name Name Date Bir satır


ListViewItem SubItem1 SubItem1

Görüldüğü gibi bir ListViewItem nesnesi, aslında anahtar sütunu temsil


etmektedir. Satırın diğer sütunları ListViewItem nesnesinin içerisinde
saklanmaktadır. Örneğin; biz uzun yolla 3 sütunlu bir ListViewItem
kontrolü için bir satırı şöyle oluşturabiliriz:

public partial class ListViewSampleForm : Form


{
private ListView m_lv;

public ListViewSampleForm()
{
InitializeComponent();

m_lv = new ListView();


m_lv.View = View.Details;
m_lv.Size = this.ClientSize;

m_lv.Columns.Add("Name");
m_lv.Columns.Add("Length");
m_lv.Columns.Add("Date");

ListViewItem lvi = new ListViewItem();


lvi.Text = "Key";

ListViewItem.ListViewSubItem lvsi1 = new


ListViewItem.ListViewSubItem();
lvsi1.Text = "SubItem1";

ListViewItem.ListViewSubItem lvsi2 = new

156
ListViewItem.ListViewSubItem();
lvsi2.Text = "SubItem2";

lvi.SubItems.Add(lvsi1);
lvi.SubItems.Add(lvsi2);

m_lv.Items.Add(lvi);

this.Controls.Add(m_lv);
}
}

ListView sınıfının Items propertysi, daha önceden de belirtildiği gibi


ListViewItemCollection türündendir. String parametreli Add fonksiyonu
kendi içerisinde ListViewItem nesnesini yaratıp ekleme işlemini
yapmaktadır. Yani;

ListViewItem lvi = new ListViewItem();


lvi.Text = "Key";
m_lv.Items.Add(lvi);

ile

m_lv.Items.Add(“Key”);

aynı anlamdadır.

Yine buradaki Add işleminden de eklenmiş olan ListViewItem nesnesi elde


edilir.

Anımsanacağı gibi ListViewItem sınıfının SubItems property


elemanı, ListViewSubItem collection türündendir. İşte bu sınıfın string
dizi parametreli AddRange fonksiyonları da vardır. Bu fonksiyon kendi
içerisinde birden fazla ListViewSubItem nesnesi yaratarak, bunların
hepsini tek hamlede eklemektedir. O halde;

ListViewItem.ListViewSubItem lvsi1 = new


ListViewItem.ListViewSubItem();
lvsi1.Text = "SubItem1";

ListViewItem.ListViewSubItem lvsi2 = new


ListViewItem.ListViewSubItem();
lvsi2.Text = "SubItem2";

lvi.SubItems.Add(lvsi1);
lvi.SubItems.Add(lvsi2);

ile

157
lvi.SubItems.AddRange(new string[] {"SubItem1", "SubItem2"});

aynı anlamdadır.

Mademki ListViewItemCollection sınıfının Add fonksiyonları


ListViewItem nesnesine geri dönmektedir o halde tüm işlemler tek
hamlede aşağıdaki gibi yapılabilir:

m_lv.Items.Add("Key").SubItems.AddRange(new string[]
{ "SubItem1", "SubItem2" });

O halde örneğin bir dizindeki tüm dosyaların bir ListView kontrolüne


doldurulması işlemi şöyle yapılabilir:

public partial class ListViewSampleForm : Form


{
private ListView m_lv;

public ListViewSampleForm()
{
InitializeComponent();

m_lv = new ListView();


m_lv.View = View.Details;
m_lv.Size = this.ClientSize;

m_lv.Columns.Add("Name");
m_lv.Columns.Add("Length");
m_lv.Columns.Add("Date");

string[] files = null;


try
{
files = Directory.GetFiles("c:\\windows");

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

foreach (string file in files)


{
FileInfo fi = new FileInfo(file);
string onlyDate =
string.Format("{0:D2}/{1:D2}/{2:D4}", fi.CreationTime.Day,
fi.CreationTime.Month, fi.CreationTime.Year);

m_lv.Items.Add(fi.Name).SubItems.AddRange(new
string[] { fi.Length.ToString(), onlyDate});

158
}
this.Controls.Add(m_lv);
}
}

ColumnHeader sınıfının Width property elemanı sütun genişliklerinin


ayarlamada kullanılır.

m_lv.Columns[0].Width = 200;
m_lv.Columns[1].Width = 100;
m_lv.Columns[2].Width = 200;

ColumnHeader sınıfının TextAling isimli property elemanı yazıyı


düşey olarak hizalamaktadır.

m_lv.Columns[0].TextAlign = HorizontalAlignment.Center;
m_lv.Columns[1].TextAlign = HorizontalAlignment.Center;
m_lv.Columns[2].TextAlign = HorizontalAlignment.Center;

Benzer biçimde ListViewItem sınıfının da görüntüye ilişkin bazı elemanları


vardır. Örneğin; BackColor ve ForeColor elemanları zemin ve şekil
renklerini değiştirmek için kullanılır.

m_lv.Items[0].BackColor = Color.Red;
m_lv.Items[1].BackColor = Color.White;

Sınıfın Check property elemanı; check durumunu ayarlamak için


kullanılabilir. Fakat buna izin veriliyor olması gerekir. ListView sınıfının
bool türeden CheckBoxes property elemanı true yapılırsa, elemanların
yanında bir seçenek kutusu görüntülenir.

m_lv.CheckBoxes = true;
//...
m_lv.Items[1].Checked = true;

ListView sınıfının bool türden GridLines propertysi, ızgara çubuklarının


çıkmasını sağlar.

m_lv.GridLines = true;

Benzer biçimde ListView sınıfının zemin ve şekil renklerini değiştirebiliriz.

ListView sınıfının bool türden AllowColumnReorder property elemanı,


sürükle bırak yöntemiyle sütun sıralarının değiştirilmesine olanak
vermektedir.

m_lv.AllowColumnReorder = true;

159
Normal olarak bir elemana tıklandığında yalnızca anahtar sütun elemanı
seçilir. ListView sınıfının bool türden FullRowSelect elemanı ile bu
durum değiştirilebilir.

m_lv.FullRowSelect = true;

ListView sınıfının bool türden MultiSelect elemanı, çoklu seçime izin


vermek için kullanılır. Bu elemanın default durumu true’dir ve çoklu seçim
yapılabilir.

m_lv.MultiSelect = false;

ListView sınıfının SelectedIndices elemanı, seçilmiş olan elemanların


indeks numaralarını bir collection biçiminde verir. Programcı indeks
numarasını elde ettikten sonra bunu, ItemsCollection’una değer yapıp
ListViewItem nesnesine ulaşabilir.

ListView sınıfının SelectedItems isimli propertysi ise seçilmiş olan


satırları ListViewItem nesnelerini tutan bir collection biçiminde verir.

ListView sınıfının Control sınıfından gelen Click event elemanı,


herhangi bir satıra tıklandığında tetiklenmektedir. Böylelikle programcı,
yeni bir satırın seçilmiş olduğunu anlayabilir. Fakat tıpkı ListBox
kontrolünde olduğu gibi SelectedIndexChange elemanı daha kullanışlıdır.
m_lv.KeyPress += new KeyPressEventHandler(m_lv_KeyPress);

//...

void m_lv_KeyPress(object sender, KeyPressEventArgs e)


{
if (e.KeyChar == 'x')
{
foreach (ListViewItem lvi in m_lv.SelectedItems)
{
MessageBox.Show(lvi.Text);
}
}
}

ListView sınıfının ColumnClick isimli event elemanı, bir sütuna


tıklandığında tetiklenir. Mesaj parametre sınıfı bize tıklanan sütunun
numarasını vermektedir.

ListView kontrolünün ve bununla ilişkili sınıfların diğer elemanları MSDN


dokümanlarından incelenmelidir.

160
ListView ELEMANLARININ SÜTUNA GÖRE SIRAYA DİZİLMESİ

ListView sınıfında sütun sıralaması otomatik yapılmamaktadır.


Bunun için programcının kod yazması gerekmektedir. Kontrol bize yalnızca
sütuna klik yapıldığını bildirmektedir.

Anahtar Notlar

Anımsanacağı gibi ListBox kontrolü doğrudan Text tutmaktadır. Fakat


ListView kontrolü ListViewItem nesnelerini tutar. ListViewItem
nesneleri de normal olarak yazı tutmaktadır. Ancak ListViewItem
nesnelerinin object türden Tag türden bir property elemanları vardır.
Programcı bu elemana istediği bir bilgiyi atayıp geri alabilir. Bazen bu
yöntem gereksiz işlemleri engelleyebilmektedir. Örneğin; her satır,
sütunlarda gösterilenden daha fazla eleman ile ilişkili olabilir. Dosya
listeleme örneğinde biz FileInfo nesnelerini Tag propertysinde
saklayabiliriz ve dosyanın diğer özelliklerine erişebiliriz.

Belirli bir sütuna göre ListView elemanlarını sıraya dizmek için şu adımlar
gerçekleştirilmelidir:

1- 1-Programcı IComparer arayüzünü destekleyen bir sınıf oluşturur. Bu


sınıfa sütun numarasını parametre olarak geçirmek gerekir. Bunun
için sınıfının başlangıç fonksiyonu kullanılabilir.
2- Programcı IComparer arayüzünü desteklemek için bu arayüze
aşağıdaki parametrik yapıya sahip Compare fonksiyonunu yazar.

int Compare (Object x, Object y)

Compare fonksiyonu, ListView sınıfının Sort fonksiyonu tarafından


çağrılmaktadır. Aslında Sort fonksiyonu, bu fonksiyon iki
parametresini iki ListViewItem nesnesi olarak geçirmektedir.
Dolayısıyla programcı bu parametreleri ListViewItem türüne
dönüştürmelidir. Compare fonksiyonu, ilk parametre ikinci
parametreden büyükse pozitif herhangi bir değere, küçükse negatif
herhangi bir değere ve eşitse “0” döndürecek şekilde yazılmalıdır.

3- Sütuna tıklandığında ilgili event içerisinde IComparer arayüzünü


destekleyen sınıf türünden bir nesne yaratılır ve bu nesne ListView
sınıfının ListViewItemSorter elemanına yerleştirilir.

4- ListView sınıfın Sort fonksiyonu çağrılır.

161
Birinci Yol(uzun olan):

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace CSD
{
public partial class ListViewSampleForm : Form
{
private ListView m_lv;

public ListViewSampleForm()
{
InitializeComponent();

m_lv = new ListView();


m_lv.View = View.Details;
m_lv.Size = this.ClientSize;
m_lv.CheckBoxes = true;
m_lv.GridLines = true;
m_lv.AllowColumnReorder = true;
m_lv.FullRowSelect = true;
//m_lv.MultiSelect = false;
m_lv.KeyPress += new
KeyPressEventHandler(m_lv_KeyPress);
m_lv.ColumnClick += new
ColumnClickEventHandler(m_lv_ColumnClick);

m_lv.Columns.Add("Name");
m_lv.Columns.Add("Length");
m_lv.Columns.Add("Date");

m_lv.Columns[0].Width = 200;
m_lv.Columns[1].Width = 100;
m_lv.Columns[2].Width = 200;

m_lv.Columns[0].TextAlign =
HorizontalAlignment.Center;
m_lv.Columns[1].TextAlign =
HorizontalAlignment.Center;
m_lv.Columns[2].TextAlign =
HorizontalAlignment.Center;

string[] files = null;


try
{

162
files = Directory.GetFiles("c:\\windows");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
string onlyDate =
string.Format("{0:D2}/{1:D2}/{2:D4}",
fi.CreationTime.Day,
fi.CreationTime.Month, fi.CreationTime.Year);

m_lv.Items.Add(fi.Name).SubItems.AddRange(new
string[]
{ fi.Length.ToString(),
onlyDate});
}

m_lv.Items[0].BackColor = Color.Red;
m_lv.Items[1].BackColor = Color.White;
m_lv.Items[1].Checked = true;

this.Controls.Add(m_lv);
}
void m_lv_ColumnClick(object sender,
ColumnClickEventArgs e)
{
m_lv.ListViewItemSorter = new
MyComparer(e.Column);

m_lv.Sort();
}

void m_lv_KeyPress(object sender, KeyPressEventArgs e)


{
if (e.KeyChar == 'x')
{
foreach (ListViewItem lvi in
m_lv.SelectedItems)
{
MessageBox.Show(lvi.Text);
}
}
}

private void ListViewSampleForm_Resize(object sender,


EventArgs e)
{
m_lv.Size = this.ClientSize;
}

163
class MyComparer : IComparer
{
private int m_cno;

public MyComparer(int cno)


{
m_cno = cno;
}

public int Compare(object o1, object o2)


{
ListViewItem lv1 = (ListViewItem)o1;
ListViewItem lv2 = (ListViewItem)o2;

switch (m_cno)
{
case 0:
return string.Compare(lv1.Text,
lv2.Text, true);
case 1:
try
{
long len1 =
long.Parse(lv1.SubItems[1].Text);
long len2 =
long.Parse(lv2.SubItems[1].Text);

if (len1 > len2)


return 1;
if (len1 < len2)
return -1;

return 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

break;

case 2:
DateTime dt1 =
DateTime.Parse(lv1.SubItems[2].Text);
DateTime dt2 =
DateTime.Parse(lv2.SubItems[2].Text);

return DateTime.Compare(dt1, dt2);


}
return 0;
}

164
}
}
}

İkinci Yol(Tag property ile yapımı kısa yol):

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace CSD
{
public partial class ListViewSampleForm : Form
{
private ListView m_lv;

public ListViewSampleForm()
{
InitializeComponent();

m_lv = new ListView();


m_lv.View = View.Details;
m_lv.Size = this.ClientSize;
m_lv.CheckBoxes = true;
m_lv.GridLines = true;
m_lv.AllowColumnReorder = true;
m_lv.FullRowSelect = true;
//m_lv.MultiSelect = false;
m_lv.KeyPress += new
KeyPressEventHandler(m_lv_KeyPress);
m_lv.ColumnClick += new
ColumnClickEventHandler(m_lv_ColumnClick);

m_lv.Columns.Add("Name");
m_lv.Columns.Add("Length");
m_lv.Columns.Add("Date");

m_lv.Columns[0].Width = 200;
m_lv.Columns[1].Width = 100;
m_lv.Columns[2].Width = 200;

m_lv.Columns[0].TextAlign =
HorizontalAlignment.Center;
m_lv.Columns[1].TextAlign =
HorizontalAlignment.Center;

165
m_lv.Columns[2].TextAlign =
HorizontalAlignment.Center;

string[] files = null;


try
{
files = Directory.GetFiles("c:\\windows");

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
string onlyDate =
string.Format("{0:D2}/{1:D2}/{2:D4}",
fi.CreationTime.Day,
fi.CreationTime.Month, fi.CreationTime.Year);

ListViewItem lvi = m_lv.Items.Add(fi.Name);

lvi.SubItems.AddRange(new string[]
{ fi.Length.ToString(),
onlyDate});
lvi.Tag = fi;
}

m_lv.Items[0].BackColor = Color.Red;
m_lv.Items[1].BackColor = Color.White;
m_lv.Items[1].Checked = true;

this.Controls.Add(m_lv);
}

void m_lv_ColumnClick(object sender,


ColumnClickEventArgs e)
{
m_lv.ListViewItemSorter = new
MyComparer(e.Column);

m_lv.Sort();
}

void m_lv_KeyPress(object sender, KeyPressEventArgs e)


{
if (e.KeyChar == 'x')
{

166
foreach (ListViewItem lvi in
m_lv.SelectedItems)
{
MessageBox.Show(lvi.Text);
}
}
}

private void ListViewSampleForm_Resize(object sender,


EventArgs e)
{
m_lv.Size = this.ClientSize;
}

class MyComparer : IComparer


{
private int m_cno;

public MyComparer(int cno)


{
m_cno = cno;
}

public int Compare(object o1, object o2)


{
ListViewItem lv1 = (ListViewItem)o1;
ListViewItem lv2 = (ListViewItem)o2;
FileInfo fi1 = (FileInfo) lv1.Tag, fi2 =
(FileInfo) lv2.Tag;

switch (m_cno)
{
case 0:
return string.Compare(fi1.Name,
fi2.Name, false);
case 1:
try
{
if (fi1.Length > fi2.Length)
return 1;
if (fi1.Length < fi2.Length)
return -1;

return 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

break;

167
case 2:
return
DateTime.Compare(fi1.CreationTime, fi2.CreationTime);

return 0;
}
}
}
}

Anahtar Notlar

ListView sınıfının Sort fonksiyonu, doğal olarak küçükten büyüğe sort


etmektedir. Fakat bu durum sınıfın Sorter fonksiyonu ile değiştirilebilir.
Yani sort işlemi yapılmadan önce Sorter fonksiyonu değiştirilmelidir.

{12.01.2008 Cumartesi}

ListView kontrolünün sütunları, Properties/Column properties yoluyla


oluşturulabilir, hatta eklemeler Items propertisi yoluyla static biçimde
yapılabilir.

KONTROLLERİN KENARLARA YUVALANDIRILMASI (DOCKING)

Bir kontrolün pencerenin herhangi bir kenarını kaplayacak duruma


getirilmesi işlemine, yuvalandırma(docking) denilmektedir. Yuvalandırma
bütünsel olarak yapılırsa, alt pencere üst pencerenin tüm çalışma alanını
kaplar. Bu işlem aslında Control sınıfının Resize mesajında yapılmaktadır.
Bunu override edersek taban sınıfının Resize fonksiyonunu çağırmayı
unutmamalıyız. Control sınıfının Dock property elemanı DockStyle isimli
enum türündendir.

Anahtar Notlar

Kontrolün üst pencereyi kaplar hale getirebilmek için Resize mesajından


faydalanılır. Aynı işlem Dock propertysi ile de yapılabilir.
Menü, araç çubuğu ve Status Bar, default durumda yukarı ve aşağıya
yuvalandırılmıştır. Diğer kontroller default durumda yuvalandırılmamıştır.

ÇİZİM İŞLEMİNİN TEMELLERİ

168
Bilgisayar sistemi yapay bir çizim ortamı sunmaktadır. Belirli
yoğunlukta piksel grubu vardır ve bunların teker teker aydınlatılmasıyla
görüntü oluşmaktadır. Bu nedenle klasik geometrik şekiller bile bilgisayar
ekranında bozulmuş görünebilirler. Çizim için orijin noktası, çalışma
alanının sol üst köşesidir ve default birim olarak piksel kullanılmaktadır.
Aslında bir pencerenin iç bölgesi, görünenden daha geniş ve daha derindir.
Görünmeyen alana da çizim yapılabilir. Görünmeyen alana yapılan bu
çizimler belki de çözünürlük yükseltildiğinde görünür hale gelecektir.

Windows pencere içerisine yapılan çizimleri kendisi saklamaz.


Örneğin; bir pencereye bir çizim yapmış olalım. Sonra pencerenin üzerine
bir başka pencereyi getirip sonra oradan yine geri çekelim. Geri çekildikten
sonra, Windows arka planda altta kalan görüntüyü bizim için yeniden
basmaz. Microsoft bu sistemin kendisi açısından daha etkin ve daha esnek
olduğunu düşünmüştür. Yani Windows sadece görüntüyü kurtarmaktadır.
Bunun için Windows her pencere için güncelleme alanı “update region”
denilen dikdörtgensel bir alan tutar. Herhangi bir “t” alanında güncelleme
alanı boş kümeyse görüntü bütünlüğünde bir bozulma söz konusu değildir
ve programcının da bir şey yapmasına gerek yoktur. Eğer güncelleme alanı
boş küme değilse pencerenin güncelleme alanı ile belirtilen dikdörtgensel
bölgesi bozulmuştur ve o bölgenin programcı tarafından yeniden çizilmesi
gerekmektedir.

Bir pencerenin görünmeyen bir kısmı görünür hale geldiğinde (yani


üzerinden diğer pencere çekilince) Windows pencerenin güncelleme alanını
günceller. Sonra kendisi için uygun bir zamanda tüm pencereleri dolaşarak
güncelleme alanı boş küme olmayanlar için Paint mesajı gönderir. İşte
programcı Paint mesajını işlemeli ve bu mesajda bozulduğu bildirilen alanı
yeniden çizmelidir.

Programcının tüm çizimleri, Paint mesajıyla yapması iyi bir tekniktir.


Programcı her Paint mesajında, tüm çizimleri yeniden yapmalıdır. Fakat
aslında arka planda, gerçekte sadece bozulan alana çizim yapılır. Örneğin;
biz Paint mesajı içinde bir doğru çizmiş olalım. Programın akışı çizim
yapan DrawLine fonksiyonuna girer. DrawLine fonksiyonu, nerelere piksel
yerleştirileceğini hesaplar (buna rendering denir). Fakat sadece
güncelleme alanı (update region) içerisinde kalan alana gerçek anlamda
çizilmektedir. Normal olarak asıl zaman kaybı piksellerin basılması
sırasında oluşmaktadır. Bu nedenle tüm çizim fonksiyonlarının içerisine
girilmesi gerçek anlamda ciddi bir zaman kaybına yol açmaz.

Paint mesajı pencerenin görünmeyen bir kısmı görünür hale


geldiğinde oluşturulmaktadır. Örneğin; bir pencereyi bir pencerenin
üzerine getirdiğimizde Paint mesajı oluşmaz, çektiğimizde oluşur. Fakat
bazı durumlarda Windows görüntüyü otomatik olarak saklayıp geri

169
basmaktadır. Örneğin; menüler açıldığında (fakat windows’un GDI
menüleri) ya da fare oku hareket ettirildiğinde.

Bir pencerenin boyutu genişletilirken eğer denetim masasından


“sürüklerken pencere içeriğini göster default durumunda bulunuluyorsa,
yani seçiliyse” genişletme sırasında bir dizi Paint mesajı gönderilir. Peki,
güncelleme alanı neresi olacaktır? Güncelleme alanı default durumda
sadece genişletilmiş alandır ve default durumda pencere daraltıldığında
Paint mesajı oluşmaz. Control sınıfının bool türden ResizeRedraw
property elemanı, bu durumu değiştirmektedir. Bu property true yapılırsa
iki şey gerçekleştirilir:

—Birincisi, pencere genişletilirse güncelleme alanı ”update region” sadece


genişletilmiş olan alan değil tüm pencere olur.
—İkincisi, pencere daraltıldığında da Paint mesajı gelir ve güncelleme
alanı tüm pencere olur.

Paint mesajı oluştuğunda Framework, güncelleme alanının zeminini


BackColor propertysi ile belirtilen renkle boyamaktadır. Yani Paint
mesajına ilişkin event fonksiyonuna akış geldiğinde, silinmiş olan zemin
yeniden boyanmış durumdadır. Programcının yalnızca şekilleri çizmesi
yeterlidir. Fakat birkaç yöntemle zeminin Framework tarafından otomatik
boyanması engellenebilir.

GRAPHİCS SINIFI

.Net sisteminde çizim işlemleri System.Drawing isim alanındaki


Graphics sınıfıyla yapılmaktadır. Bu sınıfın bir grup DrawXXX ve FillXXX
fonksiyonları vardır. Programcı Graphics nesnesini bir new operatörü ile
kendisi yaratamaz. Çünkü sınıfın başlangıç fonksiyonu private bölümde
gizlenmiştir. Graphics nesnesi iki biçimde elde edilir:

1. Windows mesaj kuyruğuna VM_PAINT mesajını bıraktığında,


Application.Run fonksiyonu bunu alarak Graphics nesnesini
kendisi oluşturur ve OnPaint sanal fonksiyonunu çağırır. Control
sınıfının OnPaint sanal fonksiyonu, Paint event elemanını
tetiklemektedir. Programcı zaten yaratılmış olan bu Graphics
nesnesini, PaintEventArgs parametre sınıfının Graphics
propertysinden alabilir. Programcılar genellikle bu propertyi yerel bir
Graphics değişkenine taşıyıp kolay kullanmak isterler.

170
2. Programcı başka bir mesajda (Örneğin; fare mesajlarında ..) çizim
yapmak isteyebilir. İşte Control sınıfının CreateGraphics isimli
fonksiyonu, Graphics nesnesini kendi içinde yaratıp bize
vermektedir. Fakat biz Graphics nesnesini CreateGraphics
fonksiyonu ile almışsak işimiz bitince onu Dispose etmeliyiz.
Dispose etmemek her zaman soruna yol açmasa da kötü bir
tekniktir. Fakat Paint mesajı tarafından bize verilen Graphics
nesnesini, biz Dispose etmemeliyiz. Framework bunu kendisi
Dispose etmelidir.

Graphics nesnesinin parametre sınıfından alınmasıyla,


CreateGraphics fonksiyonu ile alınması arasında önemli bir farklılık vardır.
Nesne parametre sınıfından alınırsa gerçek çizimler ancak güncelleme
alanına yapılabilir. Fakat CreateGraphics fonksiyonu ile elde edilirse tüm
çalışma alanına çizim yapılabilir.

INVALIDATE(GEÇERSİZ YAPMAK) İŞLEMİ

Bazen programcı pencerenin belirli bir dikdörtgensel alanını, sanki


bozulmuş gibi programlama yoluyla güncelleme alanı haline getirmek
isteyebilir. İşte Control sınıfının Invalidate fonksiyonları bunu
yapmaktadır. Invalidate fonksiyonları, güncelleme alanını (update
region) programlama yoluyla boş küme olmaktan çıkarmaktadır.
Invalidate fonksiyonları yalnızca güncelleme alanına “update region”ı
günceller. Paint mesajının oluşturulması belirli bir süre sonra Windows
tarafından yapılmaktadır.

Dört adet Invalidate fonksiyonu önemlidir:

Control.Invalidate : Bu fonksiyon tüm çalışma alanını güncelleme alanı


“update region” yapar. Yani Invalidate (geçersiz) yapar.

public void Invalidate (Rectangle rc) : Bu fonksiyon belirli bir


dikdörtgensel alanı geçersiz yapmaktadır.

Bir ana pencerenin içerisinde çeşitli alt pencereler olsun. Pencerenin


görünmeyen kısmı görünür hale geldiğinde, tüm pencerelere bağımsız
olarak ayrı ayrı Paint mesajları gönderilmektedir. Örneğin; düğmenin
üzerindeki bozulmuş olan yazıyı biz yazmayız, Button sınıfının Paint
mesajı kendi içerisinde halledecektir. Yani programcı sadece kendi yarattığı
pencerenin çizimleri ile ilgilenmelidir. İşte diğer iki Invalidate fonksiyonu,
alt pencerelerin de güncelleme alanının güncellenip güncellenmeyeceğini
belirlemektedir.

171
public void Invalidate (bool invalidateChildren) : Parametre true
girilirse alt pencereler de geçersiz hale getirilir. false girilirse alt
pencereler geçersiz hale getirilmez yani onlar için Paint mesajı gelmez.

Diğer Invalidate fonksiyonu da şöyledir:

public void Invalidate (Rectangle rc, bool invalidateChildren) :


bool türden parametresiz Invalidate fonksiyonları, alt pencereleri de
geçersiz hale getirmektedir.

ÇİZİMLERİN KALICILIĞININ SAĞLANMASI

Normal olarak programcının Paint mesajıyla çizimlerini yapması


gerekir. Fakat bazen Paint mesajının dışında da ani çizimlerin yapılması
gerekebilir. Örneğin bir yazboz tahtası uygulamasında, farenin tuşuna
basılarak çizim yapılabilmektedir. İşte çizim kalıcılığını sağlamak için iki
yöntem önerilebilir:

1- Programcı Paint mesajının dışında çizim yapacaksa,


CreateGraphics fonksiyonu ile grafik nesnesini elde eder. Çizimi
gerçekleştirir. Fakat çizim datalarını aynı zamanda saklar ve Paint
mesajında onları yeniden çizer.

2- Tüm çizimler Paint mesajında yapılır. Dışarıda çizim yapılacaksa


sadece çizim dataları oluşturulur ve Invalidate işlemi yapılır.

Anahtar Notlar

Framework 3.0 ile birlikte yani bir GUI kütüphanesi daha sisteme
eklenmiştir. Bu yeni GUI kütüphanesine WPF (Windows Presentation
Foundation) ‘dir. Fakat WPF’nın ortaya çıkması Framework 2.0 daki
kütüphanenin devre dışı kaldığı anlamına gelmemektedir.

{13.01.2008 Pazar}

KALEM(PEN) KAVRAMI

Çizim yapan DrawXXX fonksiyonları, çizimleri bir kalemle


yapmaktadır. Kalem Pen isimli bir sınıfla temsil edilmiştir. Bir kalemin en
tipik üç özelliği renk, kalınlık ve çizgi biçimidir. Kalem nesnesi yaratılırken
Pen sınıfının başlangıç fonksiyonunda bunlar belirtilir. Ayrıca Pen sınıfının
çeşitli property elemanları ile bu özellikler get ve set edilebilmektedir.
Örneğin:

172
private void Form1_Paint(object sender PaintEventArgs e)
{
Graphics g = e.Graphics;

Pen myPen = new Pen(Color.Red, 3);


}

Pen sınıfı IDisposable arayüzünü desteklemektedir ve unmanaged


kaynak kullanmaktadır. Bu nedenle programcının kalemi kullandıktan
sonra Dispose fonksiyonun çağırması iyi bir tekniktir.

IDisposable arayüzünü destekleyen bir kaynağı kullandıktan sonra


Dispose fonksiyonunu çağırmalıyız. Bu can sıkıcı işlemi basitleştirmek için
using deyimi kullanılır. using deyiminin genel biçimi şöyledir:

using (<tür> <değişken> = <ilk değer>)


<deyim>

Örneğin:

using(Pen myPen = new Pen(Color.Red))


{
//...
}

Anahtar Notlar
Programın akışı using bloğundan normal olarak çıksa da, exception ile
çıksa da bildirilen değişken için Dispose fonksiyonu çağrılır. C#
standartları bu deyimin eşdeğerini şöyle vermiştir:

{
<tür> <değişken> = <ilk değer>
try
{
<deyim>
}
finally
{
<değişken>.Dispose();
}

using deyimi ile aynı türden birden fazla değişken da


tanımlanabilir:

173
using(T a=ifade1, b = ifade2,...)
{
//...
}

yukarıdaki işlemin eşdeğeri aşağıdaki gibidir:

using (T a = ifade1)
{
using(T b = ifade2)
{
//...
}
}

Pen sınıfının DashStyle property elemanı DashStyle isimli bir enum


türündendir. Default durumda bu property Solid durumdadır. Yani düz bir
çizgi çizmektedir.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

using (Pen myPen = new Pen(Color.Red, 3))


{
myPen.DashStyle =
System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawLine(myPen, 0, 0, 200, 200);
}
}

Pen sınıfının StartCap ve EndCap property elemanları, kalemin başlangıç


ve bitiş biçimlerini belirtmektedir.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

using (Pen myPen = new Pen(Color.Red, 10))


{
myPen.DashStyle =
System.Drawing.Drawing2D.DashStyle.Solid;
myPen.EndCap =
System.Drawing.Drawing2D.LineCap.RoundAnchor;
g.DrawLine(myPen, 0, 0, 200, 200);
}
}

174
Pen sınıfının diğer elemanları MSDN dokümanlarından incelenmelidir.

Pens isimli sınıf, çeşitli renkte, 1 kalınlıkta düz çizgili hazır kalem veren
çeşitli statik propertylere sahiptir. Bu durumda örneğin; programcı
kendisinden bir kalem istendiğinde Pens.Red gibi bir ifade ile hazır bir
kalemi verebilir. Hazır kalemlerin Dispose edilmemesi gerekir.

FIRÇA(BRUSH) KAVRAMI

Bir alan boyanacaksa, bu işlem bir fırça nesnesi ile yapılır (aslında
kalın bir kalem bir fırça gibi de kullanılabilir). Çeşitli fırçalar söz konusudur
ve bu fırçalar farklı sınıflarla temsil edilmiştir. Tüm bu fırça sınıflarının
ortak elemanları abstract bir Brush sınıfında toplanmıştır ve tüm fırça
sınıfları bu sınıftan türetilmiştir. SolidBrush düz renkli bir fırçadır.
HatchBrush desenli bir fırçayı belirtir.

.Net fonksiyonları fırça nesnesini programcıdan Brush sınıfı ile ister.


Fakat programcı SolidBrush, HatchBrush sınıflarını kullanarak bu nesneyi
yaratır.

Tıpkı Pens sınıfında olduğu gibi düz renkli fırça veren Brushes sınıfı
da vardır. Programcı bir brush istendiğinde pratik olarak Brushes.Red gibi
bir ifade ile fırçayı verebilir.

GRAPHICS SINIFININ ÖNEMLİ ÇİZİM FONKSİYONLARI

DrawLine isimli fonksiyonlar, bir kalem ve iki noktayı alarak doğru çizerler.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

g.DrawLine(Pens.Red, 0, 0, 100, 100);


}

Yazboz Tahtası Uygulaması:

Programın algoritmik yapısı oldukça basittir. İki MouseMove mesajından


elde edilen noktalar bir çizgi ile birleştirilir.

Yöntem:1

namespace CSD

175
{
public partial class ScratchPadForm : Form
{
private int m_prevX, m_prevY;
private Pen m_pen;

public ScratchPadForm()
{
InitializeComponent();

//this.ResizeRedraw = true;

m_pen = new Pen(Color.Red, 5);


}

private void ScratchPadForm_MouseDown(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_prevX = e.X;
m_prevY = e.Y;
}
}

private void ScratchPadForm_MouseMove(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
using (Graphics g = this.CreateGraphics())
{
g.DrawLine(m_pen, m_prevX, m_prevY, e.X,
e.Y);
m_prevX = e.X;
m_prevY = e.Y;
}
}
}
}
}

Yöntem 1–1:

namespace CSD
{
public partial class ScratchPadForm : Form
{
private int m_prevX, m_prevY;
private Pen m_pen;

176
private ArrayList m_lines;
private ArrayList m_currentLine;

public ScratchPadForm()
{
InitializeComponent();

this.ResizeRedraw = true;
m_lines = new ArrayList();

m_pen = new Pen(Color.Red, 5);


}

private void ScratchPadForm_MouseDown(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_prevX = e.X;
m_prevY = e.Y;

m_currentLine = new ArrayList();

m_currentLine.Add(new Point(e.X, e.Y));


}
}

private void ScratchPadForm_MouseMove(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
using (Graphics g = this.CreateGraphics())
{
g.DrawLine(m_pen, m_prevX, m_prevY, e.X,
e.Y);

m_currentLine.Add(new Point(e.X, e.Y));

m_prevX = e.X;
m_prevY = e.Y;
}
}
}

private void ScratchPadForm_MouseUp(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_lines.Add(m_currentLine);

177
}
}

private void ScratchPadForm_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

foreach (ArrayList line in m_lines)


for (int i = 0; i < line.Count - 1; ++i)
{
g.DrawLine(m_pen, (Point) line[i], (Point)
line[i + 1]);
}
}
}
}

Yöntem 2 (tamamlanmamış):

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

namespace CSD
{
public partial class ScratchPadForm : Form
{
private int m_prevX, m_prevY;
private Pen m_pen;
private ArrayList m_lines;
private ArrayList m_currentLine;
private bool m_flag;

public ScratchPadForm()
{
InitializeComponent();

//this.ResizeRedraw = true;
m_lines = new ArrayList();

m_pen = new Pen(Color.Red, 5);


}

178
private void ScratchPadForm_MouseDown(object sender,
MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_prevX = e.X;
m_prevY = e.Y;

m_currentLine = new ArrayList();


m_currentLine.Add(new Point(e.X, e.Y));
m_flag = true;
}
}

private void ScratchPadForm_MouseMove(object sender,


MouseEventArgs e)
{
if (m_flag)
{
m_currentLine.Add(new Point(e.X, e.Y));

Rectangle rect = new Rectangle(e.X, e.Y,


m_prevX - e.X, m_prevY - e.Y);
this.Invalidate(Rectangle.Inflate(rect, 5,
5));
// this.Invalidate();

m_prevX = e.X;
m_prevY = e.Y;
}
}

private void ScratchPadForm_MouseUp(object sender,


MouseEventArgs e)
{
if (m_flag)
{
m_lines.Add(m_currentLine);
m_currentLine = null;
m_flag = false;
}
}

private void ScratchPadForm_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

foreach (ArrayList line in m_lines)


for (int i = 0; i < line.Count - 1; ++i)
{

179
g.DrawLine(m_pen, (Point) line[i], (Point)
line[i + 1]);
}

if (m_currentLine != null)
for (int i = 0; i < m_currentLine.Count - 1;
++i)
{
g.DrawLine(m_pen, (Point)m_currentLine[i],
(Point)m_currentLine[i + 1]);
}
}
}
}

{19.01.2008 Cumartesi}

Örnek devamı

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

namespace CSD
{
public partial class ScratchPadForm : Form
{
private int m_prevX, m_prevY;
private Pen m_pen;
private ArrayList m_lines;
private ArrayList m_currentLine;
private bool m_flag;

public ScratchPadForm()
{
InitializeComponent();

//this.ResizeRedraw = true;
m_lines = new ArrayList();

m_pen = new Pen(Color.Red, 5);


}

private void ScratchPadForm_MouseDown(object sender,


MouseEventArgs e)

180
{
if (e.Button == MouseButtons.Left)
{
m_prevX = e.X;
m_prevY = e.Y;

m_currentLine = new ArrayList();


m_currentLine.Add(new Point(e.X, e.Y));
m_flag = true;
}
}

private void ScratchPadForm_MouseMove(object sender,


MouseEventArgs e)
{
if (m_flag)
{
m_currentLine.Add(new Point(e.X, e.Y));

Rectangle rect = getRect(m_prevX, m_prevY,


e.X, e.Y);
this.Invalidate(rect);

m_prevX = e.X;
m_prevY = e.Y;
}
}

private void ScratchPadForm_MouseUp(object sender,


MouseEventArgs e)
{
if (m_flag)
{
m_lines.Add(m_currentLine);
m_currentLine = null;
m_flag = false;
}
}

private void ScratchPadForm_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

foreach (ArrayList line in m_lines)


for (int i = 0; i < line.Count - 1; ++i)
{
g.DrawLine(m_pen, (Point) line[i], (Point)
line[i + 1]);
}

181
if (m_currentLine != null)
for (int i = 0; i < m_currentLine.Count - 1;
++i)
{
g.DrawLine(m_pen, (Point)m_currentLine[i],
(Point)m_currentLine[i + 1]);
}

private static Rectangle getRect(int x1, int y1, int


x2, int y2)
{
Rectangle rect;

rect = Rectangle.Union(new Rectangle(x1, y1, 5,


5), new Rectangle(x2, y2, 5, 5));

return rect;
}
}
}

DrawRectangle ve FillRectangle FONKSİYONLARI

DrawRectangle fonksiyonları, bir kalem ve dikdörtgensel koordinat alarak


ilgili dikdörtgeni çizerler. Örneğin:

public void DrawRectangle (Pen pen, Rectangle rect)

FillRectangle fonksiyonları ise bir fırça ve dikdörtgen koordinatı alarak


dikdörtgenin içini boyarlar. Ancak DrawRectangle ile FillRectangle
arasında küçük bir mantık farklılığı vardır. Örneğin; 0, 0’dan başlayarak 3
genişlik, 3 yüksekliğe sahip bir dikdörtgeni DrawRectangle ve
FillRectangle ile belirlemeye çalışalım. Görüldüğü gibi FillRectangle
fonksiyonunda genişlik ve yükseklik, kaç pikselin boyanacağını
belirtmektedir. Bu durumda aynı Rectangle önce DrawRectangle sonra
FillRectangle işlemi uygulansa sağ ve aşağı kenar çizik gözükür.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

g.DrawRectangle(Pens.Red, 100, 100, 100, 100);


g.FillRectangle(Brushes.Blue, 100, 100, 100, 100);

// sağ ve aşağı kenar çizik gözüküyor

182
}

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Rectangle rect = new Rectangle(100, 100, 100, 100);

g.FillRectangle(Brushes.Blue, rect);
g.DrawRectangle(Pens.Red, 99, 99, 100, 100);

// tamamiyle gözüküyor
}

İkinci Yol Offset fonksiyonu ile:

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Rectangle rect1 = new Rectangle(100, 100, 100, 100);

g.FillRectangle(Brushes.Blue, rect1);

Rectangle rect2 = rect1;


rect2.Offset(-1, -1);

g.DrawRectangle(Pens.Red, rect2);
}

Çizim işlemlerinde kalın kalem kullanıldığında programcı önce, sanki 1


piksel kalınlığında kalem kullanılıyormuş gibi düşünmelidir. Sonra kalın
kalemin, bu 1 piksellik çizgiye göre nasıl hizalanacağı belirlenmelidir. Bu
durumda kalemin hizalanması üç biçimde olabilir:

1- İçten Hizalama
2- Dıştan Hizalama
3- Ortalayarak Hizalama

Bu özellik Pen sınıfının PenAligment property elemanı ile belirlenmektedir.

Dikdörtgen ile yapılan örnek:

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Rectangle rect1 = new Rectangle(100, 100, 100, 100);

Pen pen = new Pen(Color.Red, 10);

183
pen.Alignment =
System.Drawing.Drawing2D.PenAlignment.Center;

g.DrawRectangle(pen, rect1);

g.DrawRectangle(Pens.Blue, rect1);
}

Hizalama işlemi bazı geometrik şekillerde farklı biçimde yapılmaktadır.


Örneğin; dikdörtgen çizilirken yapılan hizalama ile çizgi çizilirken yapılan
hizalama farklıdır.

Ayrıca köşe çizgilerinin birleştirilmesi, yine Pen sınıfının LineJoin


propertysi ile yapılmaktadır.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Rectangle rect1 = new Rectangle(100, 100, 100, 100);

Pen pen = new Pen(Color.Red, 10);


pen.Alignment =
System.Drawing.Drawing2D.PenAlignment.Center;
pen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;

g.DrawRectangle(pen, rect1);

g.DrawRectangle(Pens.Blue, rect1);
}

DrawEllipse ve FillEllipse FONKSİYONLARI

Elips çizen fonksiyonlar, bir Rectangle parametresi alarak onun iç teğet


elipsini çizerler. İlgili dikdörtgen kare olursa çizilen elips de daire olur.

public void DrawEllipse(Pen pen , Rectangle rect)

public void FillEllipse(Brush brush, Rectangle rect)

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
Rectangle rect1 = new Rectangle(100, 100, 300, 100);

Pen pen = new Pen(Color.Red, 1);

g.DrawRectangle(pen, rect1);

184
g.DrawEllipse(Pens.Blue, rect1);
}

Diğer DrawEllipse ve FillEllipse fonksiyonları MSDN dokümanlarından


incelenmelidir.

ŞEKİL TAŞIMA İŞLEMLERİ

Bir şekli taşımak için yine MouseMove mesajında eski nokta ile yeni nokta
arasındaki deltaX ve deltaY miktarları hesaplanır ve şekil o miktar kadar
ötelenir.

Anahtar Notlar

Programcı birden fazla kez Invalidate fonksiyonu çağırırsa, sistem


bunu biriktirir. Buradaki dikdörtgenleri içine alan en küçük dikdörtgeni
hesaplar ve mümkünse tek bir Paint mesajı yollar. Dolayısıyla şekli
kaydırırken eski şeklin ve yeni şeklin Invalidate edilmesi yeterlidir.

Çizip Taşıma Örneği:

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

namespace Drawing2
{
public partial class Form1 : Form
{
private Rectangle m_rect;
private int m_prevX, m_prevY;
private bool m_inside;

public Form1()
{
InitializeComponent();

m_rect = new Rectangle(100, 100, 150, 50);


}

private void Form1_Paint(object sender, PaintEventArgs


e)
{

185
Graphics g = e.Graphics;

g.DrawRectangle(Pens.Red, m_rect);
}

private void Form1_MouseDown(object sender,


MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (m_rect.Contains(e.X, e.Y))
{
m_prevX = e.X;
m_prevY = e.Y;

m_inside = true;
}
}
}

private void Form1_MouseMove(object sender,


MouseEventArgs e)
{
if (m_inside)
{
int deltaX = e.X - m_prevX;
int deltaY = e.Y - m_prevY;

this.Invalidate(Rectangle.Inflate(m_rect, 1,
1));
m_rect.Offset(deltaX, deltaY);

this.Invalidate(Rectangle.Inflate(m_rect, 1,
1));
m_prevX = e.X;
m_prevY = e.Y;
}
}

private void Form1_MouseUp(object sender,


MouseEventArgs e)
{
m_inside = false;
}
}
}

ŞEKİLLERDEKİ TİTREMELER VE BUNLARIN ENGELLENMESİ

186
Kapalı bir şekli (örneğin FillRectangle ile çizilmiş) taşımaya
çalıştığımızda bir titreme oluşur. Bu titremenin nedeni eski şekil ile yeni
şeklin kesişim bölgesinin önce silinip sonra boyanmasındandır.

Kesişim Bölgesi

Bu titremeyi engellemenin en kolay yolu çift tamponlama (Double Buffer)


tekniğidir. Bu teknikte programcı çizimi doğrudan ekrana yapmaz. Bellekte
ekranı temsil eden bir Bitmap’a yapar. Çizdiği alanın Invalidate yapılmış
bölgesini ekrana kopyalar. Böylece ekrana çizim iki kez değil, bir kez sonuç
görüntü biçimde olur. .Net 2.0’da bu işlem çok kolay bir hale getirilmiştir.
Control sınıfının bool türden protected DoubleBuffered property
elemanı true yapılırsa, Paint mesajı oluştuğunda verilen Graphics
nesnesi ile ekrana değil bellekte bir Bitmap’a çizim yapılmaktadır. Paint
mesajının çıkışında sistem bu Bitmap’ta Invalidate edilmiş alanı gerçek
ekrana kopyalamaktadır.

public Form1()
{
InitializeComponent();

m_rect = new Rectangle(100, 100, 150, 50);

this.DoubleBuffered = true;
}

DrawPolygon ve FillPolygon FONKSİYONLARI

Bu fonksiyonlar bir Point dizisi alarak ilgili noktalara birleştirip, ilk nokta
ile son noktayı da birleştirerek kapalı düz bir şekli elde ederler.

public void DrawPolygon(Pen pen, Point[] points)

public void FillPolygon(Brushes brush, Point[] points)

DrawRectangle ile yapılan örnek:

using System;
using System.Collections.Generic;
using System.ComponentModel;

187
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace Polygon
{
public partial class Form1 : Form
{
private ArrayList m_al;

public Form1()
{
InitializeComponent();

m_al = new ArrayList();


}

private void button1_Click(object sender, EventArgs e)


{
using (Graphics g = this.CreateGraphics())
{
Point[] pt = new Point[m_al.Count];

m_al.CopyTo(pt);

g.DrawPolygon(Pens.Blue, pt);
}
}

private void button2_Click(object sender, EventArgs e)


{
this.Invalidate();

m_al.Clear();
}

private void Form1_MouseDown(object sender,


MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
g.FillEllipse(Brushes.Red, new Rectangle(e.X -
2, e.Y - 2, 4, 4));

m_al.Add(e.Location);
}
}
}
}

188
FillRectangle ile yapılan örnek:

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

namespace Polygon
{
public partial class Form1 : Form
{
private ArrayList m_al;

public Form1()
{
InitializeComponent();

m_al = new ArrayList();

this.ResizeRedraw = true;
}

private void button1_Click(object sender, EventArgs e)


{
using (Graphics g = this.CreateGraphics())
{
Point[] pt = new Point[m_al.Count];

m_al.CopyTo(pt);

g.FillPolygon(Brushes.Blue, pt);
}
}

private void button2_Click(object sender, EventArgs e)


{
this.Invalidate();

m_al.Clear();
}

private void Form1_MouseDown(object sender,


MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())

189
{
g.FillEllipse(Brushes.Red, new Rectangle(e.X -
2, e.Y - 2, 4, 4));

m_al.Add(e.Location);
}
}
}
}

DrawLines FONKSİYONLARI

DrawLines fonksiyonları bir Point dizisi alarak yalnızca o noktaları çizgi ile
birleştirir.(son nokta ile ilk noktayı birleştirmez)

DrawImage FONKSİYONLARI

Bir resmi doğrudan çalışma alanı içerisine DrawImage fonksiyonları ile


çizebiliriz. Zaten PictureBox kontrolü de bunu yapmaktadır. Çizim yapan
birçok DrawImage fonksiyonu vardır. Bu fonksiyonlar büyütme küçültme ve
döndürme işlemlerini de yapabilmektedir. Aşağıdaki DrawImage fonksiyonu
resmi bütünsel olarak, sol üst köşesi belirlenen koordinatta olacak şekilde
çizer.

public void DrawImage(Image image, Point piont)

Örneğin:

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

namespace DrawImage
{
public partial class Form1 : Form
{
private Image m_image;

public Form1()
{
InitializeComponent();

try

190
{
m_image = new
Bitmap(@"E:\DotNetAppBasic\DrawImage\DrawImage\beatles2.jpg")
;
}

catch (Exception e)
{
MessageBox.Show(e.Message);
}
}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(m_image, new Point(0, 0));
}
}
}

Aşağıdaki DrawImage fonksiyonu, resmi dikdörtgene sığdıracak şeklide


büyüterek ya da küçülterek çizmektedir.

public void DrawImage(Image image, Rectangle rect)

Örneğin:

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;
g.DrawImage(m_image, new Rectangle(100, 100, 100, 100));
}

{20.01.2008 Pazar}

Aşağıdaki DrawImage fonksiyonu resmi döndürerek göstermektedir:

public void DrawImage(Image image, Point[] desPoints)

Fonksiyonun parametresi, üç elemanlı Point türünden bir dizidir. Dizinin


elemanları sırasıyla sol üst, sağ üst ve sol alt köşe koordinatlarıdır. Bunlar
bir dikdörtgensel bölge belirtmektedir ve bu durumda sağ alt köşenin
belirtilmesine gerek yoktur.

using System;
using System.Collections.Generic;

191
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DrawImage
{
public partial class Form1 : Form
{
private Image m_image;

public Form1()
{
InitializeComponent();

this.ResizeRedraw = true;
this.DoubleBuffered = true;

try
{
m_image = new
Bitmap(@"E:\DotNetAppBasic\DrawImage\DrawImage\beatles2.jpg")
;
}

catch (Exception e)
{
MessageBox.Show(e.Message);
}
}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

g.DrawImage(m_image, new Point[] {


new Point(this.ClientSize.Width / 2, 0),
new Point(this.ClientSize.Width,
this.ClientSize.Height / 2),
new Point(0, this.ClientSize.Height / 2)});
}
}
}

Resmi Döndürme Örneği:

private void Form1_Paint(object sender, PaintEventArgs e)


{

192
Graphics g = e.Graphics;

g.DrawImage(m_image, new Point[] {


new Point(this.ClientSize.Width,
this.ClientSize.Height),
new Point(0, this.ClientSize.Height),
new Point(this.ClientSize.Width, 0)});
}

Yukarıdaki fonksiyonların dışında daha pek çok DrawImage


fonksiyonu vardır. Örneğin; resmi hem büyütüp, küçülten hem de resmi
döndüren DrawImage fonksiyonu da vardır. Diğer fonksiyonlar MSDN
dokümanlarından incelenmelidir.

FONT İŞLEMLERİ

Yazı karakterlerinin pek çok özelliğini betimleyen bilgi topluluğuna


font denilmektedir. Font aslında aşağı seviyeli olarak düşünülürse, her
karakterin resmini tutan bir data dosyasıdır. Yazının o dosyadaki bilgiden
ilgili karakterlerin şeklini öğrenip çizebilmektedir. Fontlar kabaca bitmap ve
vektör olarak ikiye ayrılmaktadır. Bitmap fontlarda karakter dataları
pikseller biçiminde belirtilmiştir ve bu tür fontlar iyi bir biçimde scale
edilemezler. Hâlbuki vektör formatlarda, karakter görüntüleri doğrusal
denklemlerle belirtilmektedir ve iyi bir biçimde scale edilebilmektedir.

Modern işletim sistemlerinin çoğunda font konusu işletim sisteminin


ilgi alanına girmektedir. Örneğin; Windows’ta yeni bir font kullanılacaksa,
önce bu font install etmek gerekmektedir. Sonra Windows’tan bize bu
fontu hazırlamasını isteriz.

Tipik olarak font dosyasının içerisinde karakterlerin temel biçimleri,


bold ve italiklik bilgileri ve font yüksekliği gibi bilgiler bulunmaktadır. Fakat
fontun rengi, çizim sırasında belirlenebilen bir özelliktir. Elimizde bir font
varsa işletim sistemi bu dataları kullanarak bunu boyutlandırabilmektedir.

.Net’te font işlemleri Font isimli bir sınıfla temsil edilmektedir.


Programcı bir font nesnesini Font sınıfının başlangıç fonksiyonu ile yaratır.
Aşağıdaki başlangıç fonksiyonu, belirli bir fontu belirli bir büyüklükte
kullanıma hazır hale getirmektedir.

public Font(string familyName, float emSize)

Fonksiyonun birinci parametresi font ailesinin ismi, ikinci parametresi


fontun Point büyüklüğüdür. 1 Point, 1,72 inch uzunluğu belirtmektedir.

193
Birinci parametre ile belirtilen font ailesi yanlış yazılırsa exception oluşmaz.
Bu durumda sistem default fontu kullanılır. Bu nedenle bu yazının doğru
yazılması önemlidir.

public partial class Form1 : Form


{
private Font m_font;

public Form1()
{
InitializeComponent();

m_font = new Font("Times New Roman", 100);

label1.Text = "Test";
label1.ForeColor = Color.Red;
label1.Font = m_font;
}

Aşağıdaki başlangıç fonksiyonu, belirli bir fontu karakteristik özellikleri ile


oluşturur.

public Font (FontFamily family, float emSize, FontStyle


style)

Burada FontStyle, bold, italik, underline gibi elemanlar içeren bir enum
türüdür.

public partial class Form1 : Form


{
private Font m_font;

public Form1()
{
InitializeComponent();

m_font = new Font("Times New Roman", 100,


FontStyle.Underline);

label1.Text = "Test";
label1.ForeColor = Color.Red;
label1.Font = m_font;
}
}

public partial class Form1 : Form


{
private Font m_font;

194
public Form1()
{
InitializeComponent();

m_font = new Font("Times New Roman", 100,


FontStyle.Strikeout | FontStyle.Italic);

label1.Text = "Test";
label1.ForeColor = Color.Red;
label1.Font = m_font;
}
}

Bazen elimizde bir font olur ve bizde font ailesinin bu fonttan alınmasını
isteyebiliriz.

public Font(Font prototype, FontStyle newStyle)

Font sınıfının ayrıca bool türden bold, italik, strikeout, underline gibi read
only property elemanları da vardır. Bu elemanlar fontun bu ikincil
karakteristiklerinin bulunup bulunmayacağını belirtir.

Time New Roman, Courier gibi font aileleri FontFamily sınıfı ile
temsil edilmektedir. FontFamily sınıfının Name elemanı, ilgili font ailesinin
ismini verir. Sınıfın pek çok faydalı fonksiyonu vardır. Font sınıfının
FontFamily isimli property elemanı, fontun ilişkili olduğu font ailesini,
FontFamily nesnesine verir.

public partial class Form1 : Form


{
private Font m_font;

public Form1()
{
InitializeComponent();

m_font = new Font("Times New Roman", 100,


FontStyle.Strikeout | FontStyle.Italic);

label1.Text = "Test";
label1.ForeColor = Color.Red;
label1.Font = m_font;

MessageBox.Show(m_font.FontFamily.Name);
}
}

195
Bir font nesnesi, bir FontFamily nesnesinden ve diğer font
karakteristiklerinden oluşur. FontFamily kavramı, font datasına ait
değişmez özellikleri belirtmektedir. Örneğin; fontun boyutu FontFamily
konusu içerisinde değildir.

Belli bir anda işletim sistemine install edilmiş tüm FontFamily


bilgilerini elde edebiliriz. Örneğin; bir text editör yazarken kullanıcının
kullanabileceği tüm fontların bir ComboBox içerisinde listesi çıkarılmak
istenebilir. Böylece kullanıcı bu ComboBox’tan istediği fontu seçebilir.
Bunun için InstalledFontCollection sınıfı kullanılmaktadır.
InstalledFontCollection sınıfı FontCollection sınıfından türetilmiştir.
FontCollection sınıfının Families isimli property elemanı bir FontFamily
dizisi biçiminde mevcut fontları vermektedir.

public FontFamily[] Families(get;)

FontCollection abstract bir sınıftır.

Yazıyı Seçilen Fontla Yazdırma:

public partial class Form1 : Form


{
private Font m_font;

public Form1()
{
InitializeComponent();

InstalledFontCollection ifc = new


InstalledFontCollection();

label1.Text = "Test";

foreach (FontFamily ff in ifc.Families)


comboBox1.Items.Add(ff.Name);
}

private void comboBox1_SelectedIndexChanged(object


sender, EventArgs e)
{
m_font = new Font((string)comboBox1.SelectedItem,
20);

label1.Font = m_font;
}
}

196
Anahtar Notlar

Her font tüm unicode karakterleri içermek zorunda değildir. Aslında bir
fontun hangi karakter görüntülerini içerdiği font data dosyası içerisinde
yazmaktadır. Örneğin; Windows’u kurduğumuzda arapça ya da japonca
karakterler Windows’un default fontları içerisinde bulunmamaktadır. Yani
özetle Windows ve .Net fonksiyonları, tüm unicode karakterleri ifade
edebilmektedir. Fakat bunların gösterilebilmesi için, içerisinde bu
karakterlere ilişkin bilgilerin bulunduğu font dosyasının da bulunması
gerekir. Eğer kullanılan fontta bu karakterler yoksa, karakterlerin yerine
boş kutucuklar ekrana basılmaktadır. Aslında tüm unicode karakterleri
içeren büyük font dosyaları bulunmaktadır. Fakat tüm unicode
karakterlerin font dosyaları çok büyük yer kaplamaktadır. Bunun için pek
tercih edilmemektedir. Bunun yerine font dosyaları ilgili dilin
karakterlerini içerek büyüklükte tutulmaktadır.

Font ve font büyüklüğü ile yazı yazma:

public partial class Form1 : Form


{
private Font m_font;

public Form1()
{
InitializeComponent();

InstalledFontCollection ifc = new


InstalledFontCollection();

label1.Text = "oiw";

foreach (FontFamily ff in ifc.Families)


comboBox1.Items.Add(ff.Name);
}

private void comboBox1_SelectedIndexChanged(object


sender, EventArgs e)
{
m_font = new Font((string)comboBox1.SelectedItem,
int.Parse((string) comboBox2.SelectedItem));

label1.Font = m_font;
}

private void comboBox2_SelectedIndexChanged(object


sender, EventArgs e)
{
m_font = new Font((string)comboBox1.SelectedItem,
int.Parse((string)comboBox2.SelectedItem));

197
label1.Font = m_font;
}
}

Control sınıfının Font isimli property elemanı, programcıya hazır bir


font vermektedir. Control sınıfının bize verdiği font, Control sınıfının
static default Font propertysi ile belirtilen fonttur. Bu font sistemden
sisteme değişebilmekle beraber WindowsXP sistemlerinde bu font, 8 Point
size’lık Sans Serif fontudur. Control sınıfının Font propertysi sanal bir
propertydir. Yani türemiş sınıflarda bu property override edilmiş olabilir.
Fakat static DefaultFont propertysi hep aynı fontu verir.

DrawString FONKSİYONU

.Net’te pencere içerisine bir yazı yazdırılacaksa bunun tek yolu


Graphics sınıfının DrawString fonksiyonlarını kullanmaktır. Graphics
sınıfının pek çok DrawString fonksiyonlarını vardır.

public void DrawString (string s, Font font, Brush brush,


PointF point)

public void DrawString (string s, Font font, Brush brush,


float x, float y)

Görüldüğü gibi fonksiyonlar koordinat olarak tek bir nokta almaktadır. Bu


nokta yazının ilk karakterinin sol üst köşesini belirtmektedir.

Anahtar Notlar

Alt alta yazı yazma teması ile çok sık karşılaşılmaktadır. Font içerisindeki
karakterlerin yükseklikleri değişebildiğine göre alt satıra geçme duygunu
nasıl verilmelidir? İşte fontun en yüksek karakterine belli bir aralık
eklenerek elde edilmiş bir değer bulunmaktadır. Font sınıfının Height
elemanı bunu vermektedir. O halde programcının yapacağı tek şey
yazdırdığı noktanın y değerine, bu Height değerini eklemektedir.

Alt alta yazma(aslında yazı çizme):

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

int y = 0;

Font mf = new Font("San Serif", 20);

198
for (int i = 0; i < 20; ++i)
{
g.DrawString("Number: " + i.ToString(), mf,
Brushes.Red, 0, y);
y += mf.Height;
}
}

Aşağıdaki DrawString fonksiyonu, yazıyı bir dikdörtgensel alan içerisine


sarmalayarak yazmaktadır.

public void DrawString(string s, Font font, Brush brush,


RectangleF layoutRectangle)

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

Font mf = new Font("San Serif", 20);

g.DrawRectangle(Pens.Red, 100, 100, 200, 100);

g.DrawString("bu bir denemedir evet evet", mf,


Brushes.Black, new RectangleF(100, 100, 200, 100));
}

Aşağıdaki DrawString fonksiyonu ise yazıyı dikdörtgensel alan içerisine


hizalayarak yazmaktadır.

public void DrawString(string s, Font font, Brush brush,


RectangleF layoutRectangle, StringFormat string)

{26.01.2008 Cumartesi}

StringFormat bir enum değil sınıftır. StringFormat sınıfının Aligment


isimli property elemanı yatay hizalama için, LineAligment property
elemanı düşey hizalama için kullanılmaktadır. Bu propertyler
StringAlingment isimli bir enum türündendir. Bu enum türünün Center,
Far, Near isimli üç elemanı vardır. Örneğin; çalışma alanının ortasına yazı
yazma şöyle yapılabilir:

namespace StringFormatSample
{
public partial class Form1 : Form
{
public Form1()

199
{
InitializeComponent();

this.ResizeRedraw = true;
}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

StringFormat sf = new StringFormat();


sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString("This is a test", this.Font,
Brushes.Red, this.ClientRectangle, sf);
}
}
}

Anahtar Notlar

Pen, Brush, Font gibi çizim nesnelerinin her defasında Paint mesajında
yaratılması yerine, onların bir kez yaratılıp sınıfın veri elemanlarında
saklanması daha etkin bir yöntemdir. Tabi bu her zaman mümkün
olmayabilir.

namespace StringFormatSample
{
public partial class Form1 : Form
{
private Font m_font;

public Form1()
{
InitializeComponent();

this.ResizeRedraw = true;
this.DoubleBuffered = true;

m_font = new Font("Times New Roman", 40);


}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

200
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString("This is a test", m_font,
Brushes.Red, this.ClientRectangle, sf);
}
}
}

DrawString fonksiyonunun Rectangle değil de Point parametreli


bir biçimi de vardır.

public void DrawString(string s, Font font, Brush brush,


PointF point, StringFormat format)

Burada StringFormat noktaya göre hizalama yapmaktadır.

namespace StringFormatSample
{
public partial class Form1 : Form
{
private Font m_font;

public Form1()
{
InitializeComponent();

this.ResizeRedraw = true;
this.DoubleBuffered = true;

m_font = new Font("Times New Roman", 40);


}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

Point pt = new Point(200, 200);

g.FillEllipse(Brushes.Blue, new Rectangle(pt.X -


2, pt.Y - 2, 4, 4));

StringFormat sf = new StringFormat();


sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString("This is a test", m_font,
Brushes.Red, pt, sf);
}

201
}
}

Düşey yazı yazdırmakta mümkündür. Bunun için StringFormat sınıfının


FormatFlags property elemanı kullanılır.

private void Form1_Paint(object sender, PaintEventArgs e)


{
Graphics g = e.Graphics;

StringFormat sf = new StringFormat();


sf.Alignment = StringAlignment.Far;
sf.LineAlignment = StringAlignment.Far;
sf.FormatFlags = StringFormatFlags.DirectionVertical;

g.DrawString("This is a test", m_font, Brushes.Red,


new Point(this.ClientSize.Width,
this.ClientSize.Height), sf);
}

EKRAN GÖRÜNTÜSÜNÜN RESİMSEL OLARAK SAKLANMASI

Remote Desktop gibi programlar, ekran görüntüsünü alarak bunu


kullanmaktadır. Ekran görüntüsünün elde edilmesi eskiden zor bir işlemdi.
Fakat Framework 2.0 ile beraber bu işlem oldukça kolaylaştırılmıştır.

Ekranımıza ilişkin bazı önemli değerler Screen isimli sınıf yoluyla


elde edilebilir. Screen sınıfının PrimaryScreen isimli static elemanı birincil
ekrana ilişkin Screen nesnesini verir. Mevcut tüm ekranlar ayrıca Screen
sınıfının AllScreen property elemanı ile elde edilebilir. Screen sınıfının
BitsPerPixel property elemanı renk çözünürlüğünü, WorkingArea isimli
enum türünden Rectangle elemanı, ekranın aktif kullanılan alanını verir.
Bounds property elemanı tüm ekrana ilişkin bir Rectangle verir. Bounds
propertysi yoluyla biz o anda çalıştığımız ekranın çözünürlüğünü elde
edebiliriz.

private void button1_Click(object sender, EventArgs e)


{
MessageBox.Show(Screen.PrimaryScreen.Bounds.ToString());
}

Ayrıca Control sınıfının ekrandaki bir noktanın pencerenin çalışma


alanının neresine düştüğünü ya da çalışma alanındaki bir noktanın ekranın
neresine düştüğünü hesaplayan PointToClient, PointToScreen isimli iki
dönüştürücü fonksiyonu vardır. Örneğin; fare mesajından aldığımız
koordinat çalışma alanı orijinlidir. Peki, ekranın neresine ilişkindir?

202
Graphics sınıfının CopyFromScreen isimli fonksiyonları, ekranın belli
bir dikdörtgensel bölgesini Graphics nesnesinin belirttiği bölgeye
kopyalamaktadır. Örneğin.

public void CopyFromScreen(Point upperLeftSource, Point


upperLeftDestination, Size blockRegionSize)

Fonksiyonun birinci parametresi ekranda bir nokta, ikinci parametresi


hedef Graphics nesnesinde bir nokta ve nihayet üçüncü parametresi
belirtilen noktadan itibaren genişlik ve yükseklik değerlerini belirtmektedir.
Örneğin; tüm ekran görüntüsünü elde etmek için birinci parametre 0,0
üçüncü parametre Screen sınıfından elde edilen Bounds değerleri
biçiminde girilmelidir.

namespace ScreenCapture
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

this.ResizeRedraw = true;
}

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

g.CopyFromScreen(new Point(0, 0), new Point(0,


0), Screen.PrimaryScreen.Bounds.Size);

private void caputreToolStripMenuItem_Click(object


sender, EventArgs e)
{

}
}
}

Bir Bitmap bir dosyadan hareketle yaratılmak zorunda değildir. Boş bir
biçimde de yaratılabilir. Bunun için Bitmap sınıfının iki parametreli
başlangıç fonksiyonu kullanılır.

public Bitmap(int width, inr height)

203
Graphics sınıfının FromImage isimli static fonksiyonu, bir Image nesnesini
parametre olarak alır ve onunla çizim yapabileceğimiz bir Graphics
nesnesi verir.

public static Graphics FromImage(Image image)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace ScreenCapture
{
public partial class Form1 : Form
{
private Bitmap m_bmp;

public Form1()
{
InitializeComponent();

this.ResizeRedraw = true;

m_bmp = new
Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);

private void Form1_Paint(object sender,


PaintEventArgs e)
{
Graphics g = e.Graphics;

g.DrawImage(m_bmp, new Rectangle(0, 0, 300,


300));
}

private void caputreToolStripMenuItem_Click(object


sender, EventArgs e)
{
Graphics g = Graphics.FromImage(m_bmp);

g.CopyFromScreen(new Point(0, 0), new Point(0,


0), Screen.PrimaryScreen.Bounds.Size);
m_bmp.Save("test.jpg", ImageFormat.Jpeg);

204
this.Invalidate();
}
}
}

TIMER İŞLEMLERİ

Belirlenen bir periyotta, belirlediğimiz bir fonksiyonun çağrılmasını


sağlayabiliriz. Bu işlemlere timer işlemleri denir. Timer işlemleri üç adet
farklı Timer sınıfıyla gerçekleştirilebilir.

Birinci Timer sınıfı System.Windows.Forms isim alanı içerisindedir.


Bu sınıf Windows’un WM_TIMER mesajını kullanmaktadır. Windows isteğe
bağlı olarak belirli bir periyotta Thread’ın mesaj kuyruğuna mesaj
bırakabilmektedir. .Net ortamı bu mesajı alarak belirlenen delege
fonksiyonunu çağırabilmektedir. Program mesaj döngüsünde takılırsa, bu
mesajlarda işlenmez.

İkinci Timer sınıfı System.Threading isim alanındadır. Bu sınıf


periyodik çağırmayı kendi içerisinde bir Thread çağırarak sağlar. Bunun
mesaj döngüsü ile bir ilgisi yoktur.

Nihayet üçüncü Timer sınıfı ise System.Timers isim alanı


içerisindedir ve bu bileşen tabanlıdır. Yani araç kutusundaki Timer ikonu,
bu sınıf türünden nesne yaratmaktadır. Bu üç Timer sınıfının kullanımı
birbirine çok benzemektedir. Burada Sytem.Windows.Forms isim alanı
içerisindeki Timer sınıfı içerisinde anlatım uygulanacaktır. Timer kullanımı
şöyle gerçekleştirilir:

1- Timer nesnesi, Timer sınıfının default başlangıç fonksiyonu ile


yaratılır.

2- Sınıfın Interval isimli property elemanı milisaniye cinsinde periyot


değeri yerleştirilir.

3- Sınıfın EventHandler isimli delege türünden Tick isimli event


elemanına, event fonksiyonu girilir. Her periyot dolduğunda bu
fonksiyon çağrılacaktır.

4- Timer sınıfın Start fonksiyonu ile çalışmaya çağrılır. Stop


fonksiyonu ile çalışma geçici bir süre bekletilebilir. Sonra tekrar
devam ettirilebilir. Bu fonksiyonlar ile tamamen eş anlamlı olarak
sınıfın bool türden Enabled isimli property elemanı kullanılabilir.

205
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DigitialClock
{
public partial class Form1 : Form
{
private Font m_font;
private Timer m_timer;

public Form1()
{
InitializeComponent();

m_font = new Font("Times New Roman", 40);


this.ResizeRedraw = true;

m_timer = new Timer();


m_timer.Interval = 1000;
m_timer.Tick += new EventHandler(m_timer_Tick);
m_timer.Start();

{
Graphics g = this.CreateGraphics();
string s = "00:00:00";
SizeF sf = g.MeasureString(s, m_font);

this.ClientSize = sf.ToSize() + new Size(30,


30);
g.Dispose();
}

this.TopMost = true;
}

void m_timer_Tick(object sender, EventArgs e)


{
this.Invalidate();
}

private void Form1_Paint(object sender, PaintEventArgs


e)
{
Graphics g = e.Graphics;

DateTime now = DateTime.Now;

206
string clockText =
string.Format("{0:D2}:{1:D2}:{2:D2}", now.Hour, now.Minute,
now.Second);

StringFormat sf = new StringFormat();


sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString(clockText, m_font, Brushes.Blue,
this.ClientRectangle, sf);
}
}
}

{27.01.2008 Cumartesi}

THREAD İŞLEMLERİ

Unix/Linux ve Windows sistemleri, MultiProsesing ve MultiThreaded


sistemleridir. Program terimi, çalıştırılabilen dosyanın diskteki durumunu
anlatmak için kullanılır. Uygulama (Application), bir işlemi gerçekleştiren
bir grup program ve öğelerin toplamını anlatır. Çalışmakta olan
programlara proses ya da task denilmektedir.

Thread bir prosesin bağımsız bir biçimde çizelgelenen akışlarına


verilen isimdir. Proses akış bakımından thread’lerden oluşur. Her proses
yaratıldığında ana thread denilen tek bir thread’e sahiptir. Diğer
thread’ler programın çalışma zamanı sırasında programcı tarafından
yaratılmaktadır.

İşletim sistemleri prosesleri, thread temelinde zaman paylaşımlı


bir biçimde çalıştırır. İşletim sistemi, bir prosesin thread’ini belli bir süre
çalıştırır. Sonra çalışmasına ara verir. Sıradaki thread’i çalıştırır. Bu
biçimde zaman paylaşımlı ve ara vermeli bir çalıştırma uygulanır. Halbuki
dışarıdan bakıldığında proses ve thread’ler aynı anda çalışıyormuş
sanılmaktadır.

Bir thread’in parçalı çalıştırma süresine quanta süresi ya da


quantum denilmektedir. Windows sistemlerinde tipik 20 milisaniyelik
quanta süresi uygulanır. Bu quanta süresi duruma göre 60 milisaniyeye
kadar uzatılabilmektedir. quanta süresi çok kısa olursa, thread’ler arası
geçiş oransal olarak fazlalaşır. Yani birim zamanda yapılan iş miktarı azalır.
Eğer quanta süresi çok uzun olursa karşılıklı etkileşim azalır. İşletim
sistemlerinin proseslerle ilgili alt sistemine, proses yöneticisi, thread’ler
arası geçiş mekanizmasını oluşturan kısmına ise çizelgeleyici denir.

207
İşletim sistemlerinin proses yöneticileri thread’leri hangi sıraya göre
çalıştırıp dinlendirmektedir? Uygulanan algoritmik yönteme çizelgeleme
algoritması denilmektedir. En adil yöntem döngüsel çizelgeleme
algoritmasıdır. Bu sistemde sırasıyla tüm thread’ler, proses farkı göz
önüne alınmadan tek tek döngüsel bir biçimde ele alınıp çalıştırılır. Bugün
Windows sistemlerinde, öncelik sınıflarına ayrılmış döngüsel çizelgeleme
yöntemi kullanılmaktadır.

Bir thread dışsal bir olaya ilişkin işlemi başlattığında, işletim sistemi
tarafından geçici bir süre çizelge dışına çıkartılır. İşletim sistemi dışsal olayı
artık kendisi izler bu olay gerçekleştiğinde thread’i tekrar çizelgeye alır.
Böylece dışsal bir olayda thread’ın meşgul bir döngü içerisinde CPU
zamanı harcaması engellenir. Bu işleme thread’ın bloke edilmesi denir.
Örneğin; bir dosya işlemi sırasında, klavye işlemi sırasında, mesaj
kuyruğunda hiçbir mesaj olmadığında thread bloke edilmektedir.
Sistemdeki tüm thread’leri o anda çizelgede olmadığına dikkat edilmelidir.

Çalışmakta olan prosesler System.Diagnostic isim alanındaki


Process sınıfı ile temsil edilmektedir. Process sınıfının, ProcessName isimli
elemanı çalışmakta olan Process‘in ismini vermektedir. Process sınıfının
static GetProcesses fonksiyonu, çalışmakta olan programları yani tüm
prosesleri bir dizi halinde vermektedir.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace ProcessList
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs


e)
{
listBox1.Items.Clear();
foreach (Process p in Process.GetProcesses())
listBox1.Items.Add(p.ProcessName);
}

208
}
}

Process modüllerden oluşur. Modüller Process ile birlikte yüklenen


.exe ve .dll dosyalarıdır. Bir Process‘in tüm modülleri ProcessModule
propertysi ile elde edilebilir.

Dosya uzantılı ile birlikte çalışan tüm processler:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace ProcessList
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs


e)
{
listBox1.Items.Clear();
foreach (Process p in Process.GetProcesses())
{
try
{
listBox1.Items.Add(Path.GetFileName(p.Mai
nModule.FileName));
}
catch (Exception)
{

}
}
}
}
}

Soldan seçilen processin kullandığı dll dosyaları gösterme:

209
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace ProcessList
{
public partial class Form1 : Form
{
Process[] m_procList;

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{
listBox1.Items.Clear();
m_procList = Process.GetProcesses();

foreach (Process p in m_procList)


{
try
{
listBox1.Items.Add(Path.GetFileName(p.Main
Module.FileName));
}
catch (Exception)
{
}
}
}

private void listBox1_SelectedIndexChanged(object


sender, EventArgs e)
{
int index = listBox1.SelectedIndex;

Process p = m_procList[index];

listBox2.Items.Clear();

foreach (ProcessModule pm in p.Modules)


try
{

210
listBox2.Items.Add(Path.GetFileName(pm.Fil
eName));
}
catch (Exception)
{

}
}
}
}

Bir Process’i sonlandırmak için, eğer Process bir GUI prosesi ise
öncelikle CloseMainWindow fonksiyonu uygulanmalıdır. Sonra belirli bir
süre beklenmeli eğer Process sonlanmazsa Kill fonksiyonu uygulanarak
zorla sonlanma yapılmalıdır.

Double klikle CloseMainWindow ile uygulamayı sonlandırma:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace ProcessList
{
public partial class Form1 : Form
{
Process[] m_procList;

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{
listBox1.Items.Clear();
m_procList = Process.GetProcesses();

foreach (Process p in m_procList)


{
try
{
listBox1.Items.Add(Path.GetFileName(p.Main
Module.FileName));

211
}
catch (Exception)
{
}
}
}

private void listBox1_SelectedIndexChanged(object


sender, EventArgs e)
{
int index = listBox1.SelectedIndex;

Process p = m_procList[index];

listBox2.Items.Clear();

foreach (ProcessModule pm in p.Modules)


try
{
listBox2.Items.Add(Path.GetFileName(pm.Fil
eName));
}
catch (Exception)
{

}
}

private void listBox1_DoubleClick(object sender,


EventArgs e)
{
int index = listBox1.SelectedIndex;

m_procList[index].CloseMainWindow();

listBox1_SelectedIndexChanged(null, null);
}
}
}

Konsol uygulamasını sonlandırmak için Close fonksiyonu


uygulanabilir. Normal olarak CloseMainWindow fonksiyonu uygulandıktan
sonra belirli bir süre beklenmelidir.

Programı DoubleClick sonlandırma ile:

using System;
using System.Collections.Generic;
using System.ComponentModel;

212
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace ProcessList
{
public partial class Form1 : Form
{
private Process[] m_procList;
private System.Windows.Forms.Timer m_timer;
private Process m_killProc;

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{
listBox1.Items.Clear();
m_procList = Process.GetProcesses();

foreach (Process p in m_procList)


{
try
{
listBox1.Items.Add(Path.GetFileName(p.Main
Module.FileName));
}
catch (Exception)
{
}
}
}

private void listBox1_SelectedIndexChanged(object


sender, EventArgs e)
{
int index = listBox1.SelectedIndex;

Process p = m_procList[index];

listBox2.Items.Clear();

foreach (ProcessModule pm in p.Modules)


try
{

213
listBox2.Items.Add(Path.GetFileName(pm.Fil
eName));
}
catch (Exception)
{
}
}

private void listBox1_DoubleClick(object sender,


EventArgs e)
{
int index = listBox1.SelectedIndex;

m_killProc = m_procList[index];
m_killProc.CloseMainWindow();

m_timer = new System.Windows.Forms.Timer();


m_timer.Interval = 5000;
m_timer.Tick += new EventHandler(m_timer_Tick);
m_timer.Start();
}

void m_timer_Tick(object sender, EventArgs e)


{
m_timer.Stop();
DialogResult dr = MessageBox.Show("Program yanıt
vermiyor? Zorla sonlandırmak ister misiniz?", "Message",
MessageBoxButtons.YesNo);
if (dr == DialogResult.Yes)
{
m_killProc.Kill();
m_killProc.WaitForExit();
button1_Click(null, null);
}

}
}
}

{03.02.2008 Pazar}

THREADLARIN YARATILMASI

Proses çalışmaya tek bir Thread ile çalışmaya başlar. Buna


Thread’ın ana Thread’ı denmektedir. Diğer Thread’ler, System.Threading
isim alanındaki Thread sınıfı ile yaratılır. Bir Thread yaratılırken, Thread
akışının başlatılacağı fonksiyon belirtilir. Framework 1.1’de, Thread akışının
başlatılacağı fonksiyon parametresiz olmak zorundaydı. Neyse ki bu durum
Framework 2.0’da düzeltilmiştir ve artık Framework 2.0’da object
parametreli başlangıç fonksiyonu da bulunmaktadır. Thread sınıfının

214
ThreadStart isimli delege parametreli başlangıç fonksiyonu ile Thread
nesnesi yaratılabilir.

public Thread (ThreadStart start)

ThreadStart isimli delege geri dönüş değeri void olan, parametresi


olmayan fonksiyonları tutabilmektedir. Hâlbuki yeni eklenen başlangıç
fonksiyonu ParameterizedThreadStart isimli delege türündendir. Bu
delege geri dönüş değeri void, parametresi object olan fonksiyonları
tutabilmektedir.

Anahtar Notlar

Thread’ler ile ilgili sınıflar System.Threading isim alanında


bulunmaktadır ve bu sınıfların çoğu fiziksel olarak mscorlib.dll dosyası
içerisindedir.

Örneğin:

class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
}
}

Thread nesnesi yaratıldıktan sonra, Thread sınıfının Start fonksiyonları ile


çalışma başlatılır. İki Start fonksiyonu vardır. Eğer Thread, ThreadStart
delegesi ile yaratılmışsa, parametresiz Start fonksiyonu kullanılır. Eğer
Thread, ParameterizedThreadStart delegesi ile yaratılmışsa, bu
durumda parametreli Start fonksiyonu kullanılmalıdır. Start
fonksiyonuna verilen parametre, Thread çalışırken Thread fonksiyonuna
geçirilmektedir.

Sleeep FONKSİYONLARI

Thread sınıfının static Sleep fonksiyonları, parametresi ile belirtilen


milisaniye kadar fonksiyonu çağıran Thread’ı çizelge dışına çıkartır.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ThreadSample1
{

215
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new
ParameterizedThreadStart(ThreadProc));
t.Start("Other Thread");

for (int i = 0; i < 10; ++i)


{
Console.WriteLine("Main Thread: {0}", i);
Thread.Sleep(1000);
}

}
public static void ThreadProc(object o)
{
string str = (string)o;

for (int i = 0; i < 10; ++i)


{
Console.WriteLine("{0}: {1}", str, i);
Thread.Sleep(1000);
}
}
}
}

ÇOK İŞLEMCİLİ SİSTEMLERDE THREAD ÇİZELGELEMESİ

Eğer bilgisayarımızda tek işlemci varsa, iki Thread gerçekten aynı


anda çalışamaz. Fakat birden fazla işlemci varsa gerçekten
programımızdaki iki farklı Thread aynı anda çalışabilir. Birden çok
işlemcinin bulunması durumunda her işlemcinin ayrı bir çizelge kuyruğu
olduğu düşünülebilir. Ayrıca bilgisayarımızda iki CPU’nun bulunuyor olması,
Thread’lerin farklı CPU’lara atanacağı anlamına gelmemektedir. Fakat
programcı, işletim sistemini buna zorlayabilmektedir.

Birden çok işlemcili sistemler genel mimari bakımından SMP ve


NUMA biçiminde ikiye ayrılmaktadır. Bugün kullandığımız boardlar SMP
mimarisine göre hazırlanmıştır. SMP, birden fazla işlemcinin belleği ortak
kullandığı sistemdir ve böyle bir kullanımda işlemciler RAM’e erişirken
birbirini beklemek zorundadır. İşlemcilerin içsel Cache mekanizmaları
RAM’e erişimi azaltsa da bu kez Cache tutarlığının sağlanması bir sorun
oluşturur. Cache tutarlılığı SMP mimarisinde otomatik ve donanımsal
olarak sağlanmaktadır.

NUMA mimarilerinde RAM çeşitli banklara ayrılmıştır. Her bank


diğerinden bağımsız bir biçimde işlemci tarafından erişilebilir. Her işlemci

216
her banka erişebilir fakat bazı banklara daha yavaş erişebilmektedir. NUMA
sistemi daha hızlı çalışmaya izin verse de başka sorunları vardır. Fakat
yinede bazı sistemlerde tercih edilmektedir.

Bilgisayarımızda iki işlemci bulunuyor olması, toplam işlemci


performansının iki kat artacağı anlamına gelmez. Özellikle SMP sisteminde
ortak belleğe erişirken oluşan kayıplar performansı düşürmektedir. İki
işlemci kullandığımızda toplam CPU performansı ortalama %20
artmaktadır.

İşletim sistemleri Thread temelinde çizelgeleme yapmaktadır. Yani


örneğin bir Thread bir quanta süresince bir işlemciye atanmışsa, artık
quanta süresi boyunca diğer işlemci ile bir ilişkisi kalmaz. Özetle işletim
sistemi işleri, işlemcilere Thread temelinde atamaktadır. Her ne kadar bir
Thread’in bir quanta da bir işlemciye atanmış olması diğer quanta da yine
aynı işlemciye atanmasını zorunlu hale getirmiyorsa da büyük olasılıkla
aynı işlemciye atama yapılacağı söylenebilir.

THREADLERE NEDEN GEREKSİNİM DUYULUR?

1- Thread’ler, arka plan işlerin yürütülmesi için yoğun bir şekilde


kullanılmaktadır. Örneğin; biz bir işlemi yaparken aynı zamanda bir
porttan ya da soketten gelen bilgileri almak isteyebiliriz. Eğer
bekleme yaparsak bilgileri alma şansımız kalmaz. İşte arka plandaki
sürekli yoklama olaylarını Thread’lere bırakabiliriz. Örneğin; bir
Thread ekrana saati çıkartırken diğer Thread yoluna devam edebilir.

2- GUI uygulamalarında mesaj işlemeleri mümkün olduğu kadar çabuk


gerçekleştirilmelidir. Aksi takdirde mesaj döngüsü işlemez ve ekran
donar. Bir mesajda uzun süreli bir işlem yapacaksak bu işlemi
Thread’e bırakmamız gerekir.

3- Thread’ler daha fazla CPU zamanı çekmek için kullanılabilir. Yani


örneğin biz beş Thread yaratırsak diğer proseslere göre toplamda
daha fazla CPU zamanı çekeriz.

4- Thread’ler bir işin toplamda daha kısa sürede yapılmasına yol


açmaktadır. Örneğin; bir algoritmanın bağımsız kısımları 2 ayrı
Thread tarafından yapılabilir. Sonra sonuçlar birleştirilebilir.

THREADLERİN SONLANDIRILMASI

Thread’ler birkaç biçimde sonlandırılabilir:

217
1- Thread fonksiyonunun çalışması bittiğinde, Thread otomatik olarak
sonlandırılır.

2- Environment.Exit fonksiyonu ile proses sonlandırılırsa, tüm Thread


otomatik sonlandırılır.

3- Bir Thread nesnesi ile Abort fonksiyonu çağrılırsa, ilgili Thread


sonlandırılır. Yani örneğin biz herhangi bir Thread’i dışarıdan
sonlandırabiliriz. Abort fonksiyonu aslında ilgili Thread üzerinde
ThreadAbortException isimli bir exception oluşturur. Programcı bu
özel exceptionu try-catch blokları içerisinde ele alabilir. Fakat ele alsa
bile catch bloğunun sonunda kaçınılmaz bir biçimde yeniden throw
yapılmaktadır. Anımsanacağı gibi bir exception ele alınmamışsa,
proses değil ilgili Thread sonlanmaktadır. İşte Abort fonksiyonu ile
sonlandırma, dolaylı bir biçimde exception ile yapılmaktadır. Eğer
programcı ThreadAbortException‘u catch ile yakaladıktan sonra
Thread sınıfının static ResetAbort fonksiyonunu çağırırsa, yeniden
throw işlemi yapılmaz. Böylece Abort işlemi kırılmış olur.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace CSD
{
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new
ParameterizedThreadStart(ThreadProc));

t.Start("Other Thread");

System.Console.ReadLine();

t.Abort();

System.Console.ReadLine();
}

public static void ThreadProc(object o)


{
string str = (string)o;

for (int i = 0; i < 100; ++i)


{
try

218
{
Console.WriteLine("{0}: {1}", str, i);
Thread.Sleep(1000);
}
catch (ThreadAbortException tae)
{
Console.WriteLine("Exception
occured!..");
Thread.ResetAbort();
}
}
}
}
}

4- .Net’te Thread’ler Foreground ve Background olmak üzere ikiye


ayrılır. Thread yaratıldığında otomatik olarak ön plandır. Thread’i
arka plana geçirmek için Thread sınıfının bool türden IsBackground
property elemanına true atamak gerekir. Prosesin son ön plan
Thread’i sonlandığında, CLR tüm arka plan Threadler’i de
sonlandırarak prosesi sonlandırır. C#’ta C/C++ ta olduğu gibi Main
fonksiyonu bittiğinde otomatik Exit işlemi yapılmamaktadır.
Dolayısıyla ana Thread sonlandığı halde diğer ön plan Thread’ler
çalışmaya devam eder. Yani proses Main bittiğinde sonlanmaz. Son
ön plan Thread gittiğinde sonlanır. Eğer programcı ana Thread
bittiğinde prosesin sonlanmasını isterse yarattığı Thread’leri arka
plana çekmelidir.

Anahtar Notlar

.Net’te esneklik ve uyum için işletim sistemi düzeyindeki Thread


kavramı ile, .Net düzeyindeki Thread kavramı birbirinden ayrılmıştır.
System.Threading isim alanındaki Thread sınıfı, .Net düzeyindeki
Thread’leri, System.Diagnostics isim alanındaki, ProsessThread
sınıfı ise işletim sistemi düzeyindeki Thread’leri yönetmek için kullanılır.
Windows ve Linux sistemlerindeki .Net ortamlarında, .Net Thread’leri ile
işletim sistemi düzeyindeki Thread’ler birebir karşılık gelmektedir. Fakat
örneğin; CLR işletim sistemi düzeyinde Thread oluşturup .Net
Thread’lerini o Thread’ler içerisinde zaman paylaşımlı olarak
çalıştırabilirdi.

5- Process sınıfının Kill fonksiyonu çağrıldığında, prosesin tüm


Thread’leri ile sonlandırılır.

219
ÇALIŞMAKTA OLAN THREADİN ELDE EDİLMESİ

Thread sınıfının CurrentThread isimli static property elemanı, o anda


çalışmakta olan Thread’in, Thread nesnesini vermektedir.

{09.02.2008 Cumartesi}

PENCERELİ VE PENCERESİZ THREAD’LER

Eğer bir Thread içerisinde hiçbir pencere açmamışsak, böyle Thread’lere


penceresiz Thread’ler, eğer bir Thread akışı içerisinde en az bir pencere
yaratılmışsa böyle Thread’lere GUI Thread’ler denir.

Win32 ve Win62 sistemlerinde, işletim sistemi Thread başına mesaj


kuyruğu oluşturmaktadır. Yani her Thread’ın ayrı bir mesaj kuyruğu vardır.
Bir Thread tarafından açılan tüm pencerelere ilişkin mesajlar, o Thread’ın
mesaj kuyruğuna Windows tarafından bırakılır. Pencerenin alt pencere ya
da ana pencere olmasının bir önemi yoktur. Application.Run fonksiyonu,
Thread’ın açtığı tüm pencerelere ilişkin mesajları kuyruktan alıp
işlemektedir. Bir Thread sona erdiğinde, o Thread’ın açtığı tüm pencereler,
sistem tarafından otomatik olarak kapatılır. Biz bir Thread yaratmış olalım
ve bu Thread içerisinde bir form oluşturduğumuzu düşünelim. Bizim bu
forma ilişkin mesajları işleyebilmemiz için o Thread’de de
Application.Run fonksiyonunu çağırmamız gerekir. Aksi halde mesajlar
işletilemez.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Collections;

namespace CSD
{
public partial class GUIThreadForm : Form
{
private ArrayList m_threads;

public GUIThreadForm()
{
InitializeComponent();

m_threads = new ArrayList();

220
}

private void m_buttonThread_Click(object sender,


EventArgs e)
{
Thread t = new Thread(new
ParameterizedThreadStart(threadProc));
t.IsBackground = true;
t.Start(null);

m_threads.Add(t);

private void threadProc(object o)


{
OtherThreadForm otf = new OtherThreadForm();

Application.Run(otf);
}

private void m_buttonLongProc_Click(object sender,


EventArgs e)
{
Thread.Sleep(10000);
}
}
}

Özetle her pencereli yarattığımız Thread de ayrıca Application.Run


fonksiyonunun çağrılması gerekir.

THREAD SENKRONİZASYONU

Aslında Thread’ler konusunun ağırlıklı noktasını, Thread senkronizasyonu


oluşturur. Birden fazla Thread’le bir amacı gerçekleştirirken Thread’lerin
birbirini beklemesi gerekebilir. Örneğin; bir Thread porttan bilgileri alırken
diğer bir Thread bunları işliyor olabilir. O halde Thread’lerin birbirini kimi
zaman bekleyerek çalışması gerekebilir. Bu konuya tipik olarak Thread
senkronizasyonu denilmektedir.

KRİTİK KOD BLOKLARININ OLUŞTURULMASI

Windows, Unix/Linux sistemlerde bir Thread’ın quanta süresi


dolduğunda, herhangi bir makine komutunda Thread’ın çalışmasına ara
verilebilir. Şüphesiz daha sonra Thread kaldığı yerden çalışmaya devam
edecektir. İşte çok Thread’li uygulamalarda, Thread’ler ortak kaynağa
erişiyor olabilir. Pek çok ortak kaynak için, aynı anda erişim programın
çökmesine neden olabilir. Örneğin; bilgisayarımız dışarıdaki bir makineyi

221
kontrol ediyor olsun. Makine üzerinde ardışıl adımlarla çeşitli işlemler
yapacak olalım. İşlemimiz bittiğinde makineyi eski halinde bırakacak
olduğumuzu varsayalım. Biz makineyi programlarken, bir aşamada
Thread’ler arası geçiş olursa ve başka bir Thread makineyi yeniden
programlamaya çalışsa, büyük bir olasılıkla program çöker. Çünkü ilk
Thread çalışmaya devam edeceği zaman makineyi son konumunda
sanacaktır. Fakat makine o konumda artık değildir. Ya da örneğin ortak
kullanılan bir ArrayList nesnesine iki Thread’ın Insert işlemi yaptığını
düşünelim. Insert işlemi aslında kaydırma ve yer açma gibi kritik işlemleri
gerektirmektedir. İşte tam Insert işlemi sırasında Thread‘ler arası geçiş
oluşsa ve akış diğer Thread’e geçip o da Insert işlemi yapsa, ArrayList
nesnesi darmadağınık olur. Muhtemelen ArrayList içerisindeki bilgiler
bozulacaktır. Şüphesiz tek Thread’li bir uygulamada böyle bir problem söz
konusu olmayacaktır. İşte bu nedenlerle Thread’lerin senkronize edilmesi
gerekir. Bir Thread ArrayList üzerinde işleme başladı ise Thread’ler arası
geçiş oluşup akış başka bir Thread’e geçse bile, geçilen Thread
ArrayList üzerinde işlem yapmamalı ve önceki Thread’in işini bitirmesini
beklemelidir.

Aynı anda baştan sona tek bir Thread tarafından işletilmesi gereken
koda kritik kod denilmektedir.

Kritik kod tipik olarak Monitor sınıfının Enter ve Exit isimli static
fonksiyonları ile oluşturulur:

Monitor.Enter(o);

//... Kritik kod

Monitor.Exit(o);

Bir Thread akışı, Monitor.Enter fonksiyonundan geçtikten sonra


artık kritik kodu elde etmiş olur. Başka bir Thread akışı Monitor.Enter
fonksiyonuna girmeye çalışırsa blokeye yol açarak çizelge dışına çıkar.
Kritik kodu elde eden Thread Monitor.Exit fonksiyonu ile kritik koddan
çıktıktan sonra bekleyen Thread içeri alınır. Eğer birden fazla Thread
Monitor.Enter içerisinde blokede bekliyorsa hangi Thread’in içeri
alınacağı hakkında Microsoft bir garanti vermemektedir. Fakat adil bir
sistemin uygulandığı varsayılmalıdır.

Monitor.Enter fonksiyonunun parametresi bir object referansıdır.


Monitor.Enter fonksiyonunun parametresi kritik kodları ilişkilendirilen bir
object referansıdır. Programın farklı yerlerinde birbirleri ile ilişkili kritik
kodlar oluşturulabilir. Bu durumda Enter fonksiyonuna aynı nesnenin
referansını vermek gerekir. Fonksiyona verdiğimizi referansın hangi nesne

222
referansı olduğunun bir önemi yoktur. Bağımsız kritik kodlar oluşturmak
için farklı nesne referansları vermek gerekir.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace CriticalSection
{
class Program
{
private static Thread m_t1, m_t2;
private static Random m_rand;
private static object m_obj = new object();

static void Main(string[] args)


{
m_rand = new Random();

m_t1 = new Thread(new


ParameterizedThreadStart(ThreadProc1));
m_t2 = new Thread(new
ParameterizedThreadStart(ThreadProc2));

m_t1.Start();
m_t2.Start();

Console.ReadLine();
}

private static void ThreadProc1(object o)


{
for (int i = 0;i < 5; ++i)
DoSomethingImportand("Thread1");

private static void ThreadProc2(object o)


{
for (int i = 0; i < 5; ++i)
DoSomethingImportand("Thread2");
}

private static void DoSomethingImportand(string str)


{
//...

Monitor.Enter(m_obj);
Console.WriteLine("{0}: 1.Adım", str);
Thread.Sleep(m_rand.Next(500));

223
Console.WriteLine("{0}: 2.Adım", str);
Thread.Sleep(m_rand.Next(500));

Console.WriteLine("{0}: 3.Adım", str);


Thread.Sleep(m_rand.Next(500));

Console.WriteLine("{0}: 4.Adım", str);


Thread.Sleep(m_rand.Next(500));

Console.WriteLine("{0}: 5.Adım", str);


Thread.Sleep(m_rand.Next(500));
Monitor.Exit(m_obj);
//...
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Collections;

namespace CriticalSection
{
class Program
{
private static Thread m_t1, m_t2;
private static ArrayList m_a;

static void Main(string[] args)


{
m_a = new ArrayList();

m_t1 = new Thread(new


ParameterizedThreadStart(ThreadProc1));
m_t2 = new Thread(new
ParameterizedThreadStart(ThreadProc2));

m_t1.Start();
m_t2.Start();
}

private static void ThreadProc1(object o)


{
for (int i = 0; i < 10000; ++i)
{
Monitor.Enter(m_a);

224
m_a.Insert(0, i);

Monitor.Exit(m_a);
}
}

private static void ThreadProc2(object o)


{
for (int i = 0; i < 10000; ++i)
{
Monitor.Enter(m_a);

m_a.Insert(0, i);

Monitor.Exit(m_a);
}
}
}
}

Kritik koda girildiğinde bir exception oluşursa, akış başka bir yere
aktarılabilir. Bu durumda Monitor kilitli kalır. O halde bu olumsuz durumu
engellemek için try-finally bloğundan faydalanabiliriz. Örneğin:

try
{
Monitor.Enter(m_o);

//...
}
finally
{
Monitor.Exit(m_o);
}

Burada exception oluşsa bile finally bloğu çalıştırılacağı için Monitor


bırakılacaktır.

LOCK DEYİMİ

Lock deyimi C#’ın bir deyimidir ve kendi içerisinde Monitor.Enter ve


Monitor.Exit fonksiyonlarını kullanmaktadır. Standartlara göre;

lock (o)
<deyim>

işleminin tamamen eşdeğeri aşağıdaki gibidir:

225
Monitor.Enter(o);

try
{
<deyim>
}
finally
{
Monitor.Exit(o);
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Collections;

namespace CriticalSection
{
class Program
{
private static Thread m_t1, m_t2;
private static ArrayList m_a;

static void Main(string[] args)


{
m_a = new ArrayList();

m_t1 = new Thread(new


ParameterizedThreadStart(ThreadProc1));
m_t2 = new Thread(new
ParameterizedThreadStart(ThreadProc2));

m_t1.Start();
m_t2.Start();

m_t1.Join();
m_t2.Join();
}

private static void ThreadProc1(object o)


{
for (int i = 0; i < 10000; ++i)
{

lock (m_a)
m_a.Insert(0, i);
}
}

226
private static void ThreadProc2(object o)
{
for (int i = 0; i < 10000; ++i)
{

lock (m_a)
m_a.Insert(0, i);
}
}
}
}

ProgressBar KONTROLÜ

ProgressBar kontrolü, tipik olarak arka plandaki bir işlemin bitiş


süresini kullanıcıya yansıtmak için kullanılır. ProgressBar sınıfı ile temsil
edilmiştir. Nesne ProgressBar sınıfının default başlangıç fonksiyonu ile
yaratılır. ProgressBar sınıfının Minimum ve Maximum property elemanları,
kontrolün alt ve üst sınır değerlerini belirtir. Bu propertylere değer
atanmamışsa Minimum=0, Maximum=100 biçimindedir. Sınıfın Value isimli
int türden property elemanına yerleştirilecek değer, ilerleme miktarını
belirtir.

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

namespace SampleProgressBar
{
public partial class Form1 : Form
{
private ProgressBar m_pb;

public Form1()
{
InitializeComponent();

m_pb = new ProgressBar();

m_pb.Bounds = new Rectangle(100, 100, 200, 30);

this.Controls.Add(m_pb);
}

227
private void button1_Click(object sender, EventArgs
e)
{
for (int i = 0; i < 100; ++i)
{
m_pb.Value = i;
System.Threading.Thread.Sleep(300);
}
}
}
}

Aynı örneğin Thread ile yapılışı:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace SampleProgressBar
{
public partial class Form1 : Form
{
private ProgressBar m_pb;
private Thread m_t;

public Form1()
{
InitializeComponent();

m_pb = new ProgressBar();

m_pb.Bounds = new Rectangle(100, 100, 200, 30);

this.Controls.Add(m_pb);

button2.Enabled = false;
}

private void button1_Click(object sender, EventArgs e)


{
m_t = new Thread(new
ParameterizedThreadStart(threadProc));
m_t.IsBackground = true;
m_t.Start();

228
button1.Enabled = false;
button2.Enabled = true;
}

private void threadProc(object o)


{
for (int i = 0; i < 100; ++i)
{
m_pb.Value = i;
System.Threading.Thread.Sleep(100);
}
}

private void button2_Click(object sender, EventArgs e)


{
m_t.Abort();

button1.Enabled = true;
button2.Enabled = false;
}
}
}

Sınıfın Style isimli property elemanı ProgressBarStyle isimli bir


enum türündendir. Default durum Blocks durumudur. Continuous ve
Marquee durumuna çekilebilir.

ProgressBar sınıfının Step isimli property elemanı, her PerformStep


fonksiyonu uygulandığında ne kadar ilerleneceğini belirtir.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace SampleProgressBar
{
public partial class Form1 : Form
{
private Thread m_t;

public Form1()
{
InitializeComponent();

229
button2.Enabled = false;
}

private void button1_Click(object sender, EventArgs e)


{
m_pb.Value = 0;

m_t = new Thread(new


ParameterizedThreadStart(threadProc));
m_t.IsBackground = true;
m_t.Start();

button1.Enabled = false;
button2.Enabled = true;
}

private void threadProc(object o)


{
for (int i = 0; i < m_pb.Maximum; ++i)
{
m_pb.PerformStep();
System.Threading.Thread.Sleep(100);
}
}

private void button2_Click(object sender, EventArgs e)


{
m_t.Abort();

button1.Enabled = true;
button2.Enabled = false;
}
}
}

{10.02.2008 Pazar}

Tab KONTROLÜ

TabControl, Options menülerinde çok karşılaştığımız bir kontroldür.


Birden fazla dialog pencerelerinin toplamından oluşmaktadır.
TabControl’ün her bir sekmesi ayrı bir penceredir ve TabPage sınıfı ile
temsil edilmiştir. Yani TabControl, TabPage’lerden oluşan bir kontroldür.
TabPage sınıfı PanelControl’ünden, TabControl ise Controls sınıfından
türetilmiştir. O halde TabControl programlama yoluyla şöyle oluşturulur:

1- Programcı bir TabControl nesnesini oluşturur.

230
2- Eklenecek TabPage’ler, TabPage sınıfını türünden nesneler yaratılarak
oluşturulur.

3- TabControl sınıfının TabPages isimli property elemanı, TabPage


nesnelerinin tutan bir collection belirtir. Programcı yarattığı TabPage
nesnelerini bu collectiona ekler.

4- Programcı istediği kontrolleri istediği TabPage içerisine ekler.

TabControl aslında basit bir kontroldür. TabPage’lerin eklenmesi


sırasında kontrol, tüm TabPage pencerelerini aynı boyuta getirir ve hangi
sekmeye tıklanırsa ilgili TabPage penceresini Visible yapar. Diğer
pencereler Invisible durumdadır.

Programlama yoluyla TabControl örneği:

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

namespace TabControlSample
{
public partial class Form1 : Form
{
private TabControl m_tc;
private TabPage m_tpage1;
private TabPage m_tpage2;

private Button m_buttonOK;


private TextBox m_textBox;

public Form1()
{
InitializeComponent();

m_tc = new TabControl();


m_tc.Bounds = new Rectangle(0, 0, 500, 300);

m_tpage1 = new TabPage();


m_tpage1.Text = "Foo";

m_tpage2 = new TabPage();


m_tpage2.Text = "Bar";

m_buttonOK = new Button();


m_buttonOK.Text = "&Ok";

231
m_buttonOK.Location = new Point(100, 100);

m_textBox = new TextBox();

m_tpage1.Controls.AddRange(new Control[]
{ m_buttonOK, m_textBox });

m_tc.TabPages.AddRange(new TabPage[] { m_tpage1,


m_tpage2 });

this.Controls.Add(m_tc);
}
}
}

Şüphesiz aslında TabControl’ler doğrudan ana pencerenin üzerinde


değil açılan dialog pencerelerinin üzerinde olur. Dialog pencerelerinde Ok,
Cancel, Apply gibi tuşlarda bulunacaktır.

Dialog penceresi üzerinde Ok tuşuna basan kullanıcı,


TabControl’deki tüm belirlemeleri kabul etmiş olur. O halde programcının
tıpkı dialog pencerelerinde yaptığımız gibi TabControl elemanlarındaki
bilgileri alabilmesi gerekir. Programcı dialog pencere sınıfına yine
TabControl’deki sayfaların içerisindeki kontrollere get ve set yapan
propertyler yerleştirebilir ya da doğrudan Ok tuşu ile çıkıldığında TabPage
nesnelerinden hareketle, TabPage kontrollerinde oluşan bilgileri elde
edebilir. Fakat form editörün sayfalar üzerine yerleştirdiğimiz kontrolleri
ana dialog pencere sınıfının private bölümüne yerleştirdiğine dikkat
etmeliyiz. Programcı isterse bu kontrolleri kendisi public bölüme
yerleştirebilir.

TabControl sınıfının SelectedIndex isimli property elemanı, o anda


hangi sekmenin aktif olduğunu belirtmektedir. Benzer biçimde
SelectedTab isimli property elemanı aktif olan TabPage referansını
vermektedir.

TabControl sınıfının TabCount property elemanı, toplam TabPage


sayısını verir. Kontrolün bool türden Multiline property elemanı tabların
birden fazla satırda yer alıp almayacağını belirtir. Default tek satırlı
durumdur.

Sınıfının Aligment property elemanı, sekmelerin hizalanması için


kullanılır. Default durum yukarıya hizalama durumudur. Aligment, Right
ya da Left biçiminde set edilirse otomatik olarak Multiline true yapılır.

TabControl sınıfının SizeMode property elemanı, otomatik olarak


sekme düzenini sağlamak için kullanılabilir. Default durum normal
durumdur. FillToRight soldan ve sağdan boşluk bırakmayacak biçimde

232
sekmeleri yerleştirir. Bu durumda sekmelerin genişlikleri aritmetik olarak
hesaplanmaktadır. Fixed seçeneğinde tüm sekmelerin genişlikleri aynı
olacak biçimde hesaplama yapılmaktadır.

TabControl sınıfının Appearence isimli property elemanı


TabAppearence isimli enum türündendir. Default durum normal biçimdedir.
Button seçeneği, sekmelere düğme görünümü verir. FlatButtons düz tuş
görüntüsü verir.

TabControl için anlamlı olan bazı eventler de vardır. Örneğin; yeni


bir sekme aktif hakle getirildiğinde SelectedIndexChange eventi tetiklenir.

BÖLME KONTROLÜ

Bu kontrole özellikle IDE ve editör gibi ortamlarda çok sık


rastlanmaktadır. Kontrol SplitContainer sınıfı ile temsil edilir. Kontrol
kendi içerisinde Panel1 ve Panel2 isimli iki alt pencereye sahiptir.
SplitContainer sınıfı dolaylı olarak Control sınıfından türetilmiştir.
Panel1 ve Panel2 pencereleri SplitterPanel sınıfı türündendir.
SplitterPanel, Panel sınıfından türetilmiştir. Kontrolün yaratılması
oldukça basittir. Tek yapılması gereken, SplitContainer sınıfı türünden
bir nesne yaratmaktır. Bölme kontrolü yatay ya da düşey olabilir.
SplitContainer sınıfının Orientation isimli property elemanı,
Orientation isimli enum türündendir. Default durum Vertical biçimdedir.
Horizontal biçimi de uygulanabilir.

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

namespace SplitterSample
{
public partial class Form1 : Form
{
SplitContainer m_sc;

public Form1()
{
InitializeComponent();

m_sc = new SplitContainer();


m_sc.Dock = DockStyle.Fill;
m_sc.Orientation = Orientation.Horizontal;

233
this.Controls.Add(m_sc);
}
}
}

Birden fazla bölme işlemi için Panel1 ya da Panel2 pencerelerinde,


başka bir SplitContainer oluşturulabilir.

SplitContainer sınıfının BorderStyle isimli property elemanı, sınır


çizgilerini belirlemede kullanılabilir.

Visual Studio görüntüsü(3 adet split):

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

namespace SplitterSample
{
public partial class Form1 : Form
{
private SplitContainer m_scVertical;
private SplitContainer m_scHorizontal;

public Form1()
{
InitializeComponent();

m_scVertical = new SplitContainer();


m_scVertical.Dock = DockStyle.Fill;
m_scVertical.Orientation = Orientation.Vertical;
m_scVertical.Panel2.BackColor = Color.Red;

m_scHorizontal = new SplitContainer();


m_scHorizontal.Dock = DockStyle.Fill;
m_scHorizontal.Orientation =
Orientation.Horizontal;
m_scHorizontal.Panel1.BackColor = Color.Blue;
m_scHorizontal.Panel2.BackColor = Color.Yellow;

m_scVertical.Panel1.Controls.Add(m_scHorizontal);
m_scVertical.BorderStyle =
BorderStyle.FixedSingle;

this.Controls.Add(m_scVertical);
}

234
}
}

Programcı diğer kontrolleri SplitContainer kontrolünün Panel1 ya da


Panel2 bölümlerine yerleştirir.

Basit bir csc derleyicisi:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

namespace SplitterSample
{
public partial class Form1 : Form
{

public Form1()
{
InitializeComponent();

private void splitContainer2_Panel1_Paint(object


sender, PaintEventArgs e)
{

private void splitContainer2_Panel1_Resize(object


sender, EventArgs e)
{
tabControl1.Invalidate();
}

private void compileToolStripMenuItem1_Click(object


sender, EventArgs e)
{
FileStream fs = null;
StreamWriter sw = null;

try
{
fs = new FileStream("Test.cs",
FileMode.Create, FileAccess.Write);

235
sw = new StreamWriter(fs);

sw.Write(textBox2.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (sw != null)
sw.Close();
}

try
{

ProcessStartInfo psi = new


ProcessStartInfo(@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.507
27/csc.exe", "Test.cs");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;

Process proc = new Process();


proc.StartInfo = psi;
proc.Start();
StreamReader sr = proc.StandardOutput;

textBox1.Text = sr.ReadToEnd();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}

{16.02.2008 Cumartesi}
VERİTABANI İŞLEMLERİ

Çok eskiden programcılar veritabanı işlemlerini manuel bir biçimde


dosya açıp, okuyup yazma yoluyla yaparlardı. Veritabanı işlemlerinin
anahtar konusu arama işlemleridir. Belli bir koşulu sağlayan kayıtların elde
edilmesine sorgu denir. Sorgulama işlemleri için, aşağı seviyeli
algoritmalar kullanılır. Daha sonra bazı firmalar tarafından veritabanı
kütüphaneleri oluşturulmuştur. Bu kütüphaneleri satın alan kullanıcılar,
firma tarafından yazılmış olan fonksiyonlar sayesinde kayıt ekleme, silme

236
gibi işlemleri yapabilmektedirler. Örneğin; Faircom firmasının Ctreee
kütüphanesi ya da DBVista gibi kütüphaneler, pek çok kullanıcı tarafından
kullanılmıştır. Bu veritabanı kütüphaneleri, profesyonel programcılar için
tasarlanmıştır ve kullanımları yüksek bilinç düzeyini gerektirmektedir.

Aşağı seviyeli data formatlarından bağımsız çalışan, kendi içerisinde


çeşitli araçları barındıran, kullanıcı ile yüksek seviyeli SQL denilen bir dille
iletişim kuran, çok büyük veritabanları üzerinde bile etkin bir kontrol
sağlayabilen, büyük veritabanı sistemlerine veritabanı yönetim sistemleri
(Database Management System) denilmektedir. Örneğin; Microsoft
firmasına ait SQL Server, Oracle firmasına ait Oracle sistemi, Sybase, DB2,
açık bir yazılım sistemi olan MySQL bir veritabanı yönetim sistemleridir.
Office programı içerisinde bulunan Access de, bir veritabanı yönetim
sistemi sayılabilir. Fakat az kayıt içeren küçük ölçekle ofis içi
uygulamalarda kullanılabilir.

Veritabanı yönetim sistemlerinin çoğu, ilişkisel veritabanı kavramını


kullanmaktadır. İlişkisel veritabanlarında, bir veritabanı tablolardan oluşur.
Bir tablo uygulamacı için klasik bir yapıdadır. Sütunlardan ve satırlardan
oluşmaktadır. Veritabanları bir grup tablodan oluşan bir topluluktur.
Uygulamacı, veritabanı yönetim sistemi ile bağlantıyı kurar. SQL denilen bir
yorumlama dili ile tablolar üzerinde yapacağı işlemi veritabanı yönetim
sistemine bildirir. Uygulamacı için veritabanı yönetim sistemi bir karakutu
gibidir. Yani uygulamacı bu tabloların diskte nasıl organize edildiği, hangi
dosyalarda saklandığı ile ilgilenmez. SQL denilen yüksek seviyeli dil ile
derdini anlatır ve birtakım sonuçları veritabanı yönetim sisteminden alır.
SQL dilinin kendisi de veritabanının ilişkisel olduğunu varsaymaktadır.
Şüphesiz uygulamacı veritabanı yönetim sistemine, SQL komutlarını
gönderdiğinde, sistem bunları aşağı seviyeli kodlarla gerçekleştirir. Buna
veritabanı yönetim sisteminin motor kısmı denilmektedir. Bu sistemlerin
motor kısımları Btreee, Hashing gibi algoritmik yöntemler kullanılarak C ya
da C++’da yazılmıştır.

Veritabanı yönetim sistemleri, client-server mimarisine göre


tasarlanmaktadır. Bu mimariye göre uygulama programları birer client
programdır. Veritabanı yönetim sistemleri server programdır. Client
programlar önce server’e bağlanır. Sonra onlardan istekte bulunurlar.
İstekte bulunmak, tipik olarak SQL komutlarının server’e gönderilmesi
biçimindedir. Veritabanı yönetim sistemleri SQL komutlarını çalıştırır ve
sonuçları client durumda olan programa gönderir. Pek çok veritabanı
yönetim sistemi, kendi içerisinde tüm gereksinimleri karşılayacak araçları
içermektedir. Örneğin; tabloların yaratılması, kayıt giriş ekranlarının
düzenlenmesi, raporların alınması standart programlama dilleri
kullanılmadan bu araçlarla yapılabilmektedir. Fakat veritabanı yönetim
sistemleri, dışarıdaki bağımsız programlara da client-server ilişkisi
içerisinde hizmet verebilmektedir.

237
.Net içerisinde veritabanı yönetim sistemi ile bağlantı kuran ona
çeşitli SQL komutlarını gönderen ve onun ürettiği sonuçları geri alan,
bunları depolayan bir grup sınıf vardır. Bu sınıflara ADO sınıfları
denilmektedir. Görüldüğü gibi ADO sınıfları, veritabanı işlemlerini yapan
sınıflar değil veritabanı yönetim sistemleri ile ilişki kuran sınıflardır.

ADO.Net sınıflar, kabaca bağlantısız ve bağlantılı sınıflar olmak üzere


ikiye ayrılmaktadır. Bağlantısız sınıflar veritabanı yönetim sistemlerinden
alınan bilgilerin depolanması için ya da gösterilmesi için kullanılan yardımcı
sınıflardır. Örneğin; Adı Soyadı, Numarası, Yaşı sütunlarına sahip bir
veritabanı tablosundan, SQL yoluyla yaşı 30’dan büyük olanların bilgilerini
çekmek isteyelim. Biz bu isteği SQL kullanarak veritabanı yönetim
sistemine bildiririz. Veritabanı yönetim sistemi de tablonun uygun
satırlarını bize verir. Fakat bu bellekte nereye yerleştirilecektir? İşte
veritabanı tablosunu bellekte temsil edecek bir collection sınıfa ihtiyaç
vardır. İşte bu sınıf DataTable sınıfıdır. Bu sınıf bağlantısız bir sınıftır.
Fakat bazı sınıflar sorgulamanın kendisini yapmaktadır. Yani veritabanı
yönetim sistemleri ile iletişim halinde, SQL komutlarını gönderip sonuç
almada kullanılır. Bunlara bağlantılı ADO sınıfları denilmektedir.

ADO.Net işlemleri yapmak için temel SQL komutlarının kullanımını


bilmek yeterlidir. Örneğin; SELECT komutu çok temel bir komuttur. Bu
komutun örnek kullanımı şöyledir:

SELECT name, no, age FROM Person WHERE age > 30

Görüldüğü gibi WHERE cümlesi koşul belirtmede kullanılmaktadır. FROM


cümlesi tablo belirlemede kullanılır. Veritabanı yönetim sistemi bu SQL
cümlesini, motor kısmında çalıştırıp bir takım koşula uyan satırları client-
server mimarisi içinde ADO sınıflarına bildirir.

Çok kullanılan önemli bir komutta INSERT INTO komutudur. Bu


komut bir tabloya kayıt eklemek için kullanılır. Örnek kullanımı şöyledir:

INSERT INTO Person(name, no, age) VALUES(‘Kaan Aslan’, 123,


42)

Burada Person tablosuna yeni bir kayıt eklenmiştir. Şüphesiz burada bir
algoritmik ekleme söz konusudur.

Diğer bir önemli SQL komutu ise UPDATE komutudur. Bu komutta


mevcut bir kayıtın bilgileri değiştirilir. Komutun örnek kullanımı şöyledir.

UPDATE Person SET name=’ali serçe’, no=123, age=18 WHERE


name=’Kaan Aslan’

238
Kayıt silme için DELETE komutu kullanılır. Komutun örnek kullanımı
şöyledir:

DELETE FROM Person WHERE age > 30

Bazen WHERE cümlesindeki koşul joker karakteri içirebilir. Bunun için


WHERE cümlesine LIKE kısmı eklenir. örneğin:

SELECT * FROM Person WHERE name LIKE ‘K%’

Sorgulamalar sonucunda elde edilen kayıtlar belli bir sütuna göre sıraya
dizili olarak verilebilmektedir. Bunun için ORDER BY cümlesi kullanılır.

DataTable SINIFI

DataTable sınıfı, bir veritabanı tablosunu bellekte temsil etmek için


düşünülmüştür. DataTable sınıfı, mantık olarak ListView sınıfına
benzemektedir. Fakat bir kontrol değildir. Bir collection sınıftır. DataTable
nesnesi, DataTable sınıfının başlangıç fonksiyonları ile yaratılır. Bir
DataTable nesnesinin bir tablo ismi vardır. Tablo ismi string türden
TableName propertysi ile temsil edilmiştir. DataTable sınıfı System.Data
isim alanındadır ve system.data.dll içerisindedir. Tablo ismi DataTable
başlangıç fonksiyonu yoluyla da belirtilebilir. Örneğin:

m_dt = new DataTable();


m_dt.TableName = “Person”;

ile

m_dt = new DataTable(“Person”);

aynı anlamdadır.

Tablo yaratıldıktan sonra tabloya sütun eklemek gerekir. Sütunlar


DataColumn sınıf ile temsil edilmektedir. Programcı DataColumn türünden
nesneler oluşturarak DataTable sınıfının Columns collection elemanı ile
bunları tabloya yerleştirir. Columns propertysi, DataColumnCollection
türündendir. Bu collection sınıfının klasik olarak Add, AddRange gibi
fonksiyonları vardır. DataColumn sınıfının ColumnName isimli property
elemanı, sütunun ismini belirlemede kullanılır. Bu isim DataColumn sınıfının
başlangıç fonksiyonu ile de belirlenebilir. Her sütun belirli türdeki bilgileri
tutar. İşte eklenecek sütunun hangi türden bilgileri tutacağı DataColumn
sınıfının type türünden DataType propertysi ile belirlenmektedir. Bu tür
aynı zamanda DataColumn sınıfının başlangıç fonksiyonu ile de girilebilir.
Örneğin:

239
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DataTableSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

DataTable dt = new DataTable("Person");

DataColumn dtName = new DataColumn("Name",


typeof(string));
DataColumn dtNo = new DataColumn("No",
typeof(int));

dt.Columns.AddRange(new DataColumn[] { dtName,


dtNo });
dataGridView1.DataSource = dt;
}
}
}

DataTable nesnesine sütunlar eklendikten sonra sıra artık satır


eklemeye gelmiştir. Data tablosunun satırlar DataRow ile temsil
edilmektedir. DataTable sınıfının Rows isimli property elemanı,
DataRowCollection isimli collection türündendir. Bu sınıf DataRow
nesnelerini tutmaktadır. O halde programcı, DataRow nesnelerini oluşturup
Rows propertysi ile eklemeyi yapmalıdır. Fakat bir DataRow nesnesi,
tablonun sütunlarına uygun olmak zorundadır. DataRow sınıfının başlangıç
fonksiyonu protected bölümde olduğu için DataRow nesnesi new ile
yaratılamaz. DataRow nesnesi, DataTable sınıfının public NewRow
fonksiyonu ile yaratılmaktadır. NewRow fonksiyonu, DataRow nesnesini
tablonun sütunlarına uygun olarak konfigure ederek vermektedir.

DataRow nesnesi elde edildikten sonra, programcı artık bu nesneye


sütun bilgilerini girmelidir. DataRow sınıfının bu işlem için kullanılan
indeksleyici elemanları vardır. Bu indeksleyici elemanlar “[]” içerisine tablo
sütununun ismini ya da sütunun sıra numarasını alarak get ve set işlemini
yaparlar. Şüphesiz sıra numarası daha hızlı bir erişime yol açacaktır. Bu
indeksleyiciler object türündendir. Yani sütunlara yanlış bilgiler girilirse

240
derleme aşamasından geçilir. Fakat çalışma zamanı sırasında exception
oluşur.

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

namespace DataTableSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

DataTable dt = new DataTable("Person");

DataColumn dtName = new DataColumn("Name",


typeof(string));
DataColumn dtNo = new DataColumn("No",
typeof(int));

dt.Columns.AddRange(new DataColumn[] { dtName,


dtNo });
DataRow dr = dt.NewRow();
dr["Name"] = "Kaan Aslan";
dr["No"] = 123;
dt.Rows.Add(dr);

dataGridView1.DataSource = dt;
}
}
}

Aynı işlem şöyle de yapılabilirdi:

DataRow dr = dt.NewRow();
dr[0] = "Kaan Aslan";
dr[1] = 123;
dt.Rows.Add(dr);

Aslında bazı kısaltmalar söz konusudur:

1- Önce DataColumn nesnelerini oluşturup sonra Columns propertysi ile


ekleme yapmak yerine doğrudan DataColumnCollection sınıfının
(Columns propertysi bu türdendir.) Add fonksiyonları yoluyla

241
eklemeyi tek hamlede yapabiliriz. Örneğin; string ve type
parametresi alan Add fonksiyonu, kendi içerisinde DataColumn
nesnesi oluşturup eklemeyi yapmaktadır. Bu durumda sütun
eklemesi şöyle de yapılabilir:

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

namespace DataTableSample
{
public partial class Form1 : Form
{
DataTable dt = new DataTable("Person");

public Form1()
{
InitializeComponent();

dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("No", typeof(int));

DataRow dr = dt.NewRow();
dr["Name"] = "Kaan Aslan";
dr["No"] = 123;
dt.Rows.Add(dr);

dataGridView1.DataSource = dt;
}
}
}

2- Aynı kolaylık DataRow eklemesi sırasında da uygulanabilir. DataTable


sınıfının Rows isimli property elemanı DataRowCollection
türündendir. Bu collection sınıfın diğer bir Add isimli fonksiyonu
params object dizi parametresine sahiptir. O halde biz satırları da
doğrudan Add fonksiyonuyla da ekleyebiliriz. Örneğin:

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

242
namespace DataTableSample
{
public partial class Form1 : Form
{
DataTable dt = new DataTable("Person");

public Form1()
{
InitializeComponent();

dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("No", typeof(int));

dt.Rows.Add("Kaan Aslan", 123);


dt.Rows.Add("Ali Serçe", 345);

dataGridView1.DataSource = dt;
}
}
}

O halde pratik olarak bir DataTable sınıfı aşağıdaki gibi oluşturulup kayıt
eklenebilir:

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

namespace DataTableSample
{
public partial class Form1 : Form
{
DataTable dt = new DataTable("Person");

public Form1()
{
InitializeComponent();

dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("No", typeof(int));

dt.Rows.Add("Kaan Aslan", 123);


dt.Rows.Add("Ali Serçe", 345);

dataGridView1.DataSource = dt;
}
}
}

243
DATAROW NESNESİNİN DURUMLARI

Bir DataRow nesnesi çeşitli durumlara girip çıkmaktadır. Nesnenin


durumu DataRow sınıfının RowState property elemanı ile elde edilir. Bu
property eleman read only’dir. Yalnızca değeri elde edilebilir. RowState
DataRowState isimli enum türündendir. DataRow nesnesinin durumları
şöyledir:

Detached: DataRow nesnesi NewRow fonksiyonu ile yaratılmıştır. Belki sütun


elemanlarına atama da yapılmıştır. Fakat henüz tabloya eklenmemiştir.

Added: DataRow nesnesi tabloya eklenmiştir fakat henüz AcceptChanges


fonksiyonu çağrılmamıştır ve tabloya ekleme işleminden sonra nesnenin
sütunlarında hiçbir değişiklik yapılmamıştır.

Modified: DataRow nesnesi tabloya eklenmiş durumdadır ve ekleme


işleminden sonra sütunlarında değişiklik yapılmıştır. Fakat henüz
AcceptChanges çağrılmamıştır. (fakat tabloya eklendikten sonra
AcceptChanges çağrılmış durumdadır)

Unchanged: DataRow nesnesi tabloya eklenmiş durumdadır ve


AcceptChanges uygulanmış durumdadır. Bundan sonra nesne de bir
değişiklik yapılmamıştır.

Deleted: DataRow nesnesi Delete fonksiyonu çağrılarak tablodan


silinmiştir. Tabi nesnenin silinmiş olması demek çöp toplayıcı tarafından
seçilebilir olması için yeterli koşulu sağlamayabilir. Biz DataRow nesnesinin
referansını tutup onu yeniden ekleyebiliriz.

DataRow nesnesinin çeşitli işlemlerdeki durumu şöyledir:

1- DataRow nesnesi, NewRow fonksiyonu ile yaratılmıştır. Sütunlarına


değer girilmiş olabilir. Henüz tabloya eklenmemiştir. Şuan Detached
durumdadır.

2- DataRow nesnesi tabloya Add fonksiyonu ile eklenmiştir. Şimdi nesne


Added durumdadır.

3- AcceptChanges fonksiyonu çağrılmadan ya da Deleted ile


silinmediği sürece Added durumdadır.

4- DataRow nesnesi üzerine AcceptChanges fonksiyonu uygulanmıştır.


Bu durumda nesne Unchanged duruma gelir.

244
5- Nesnenin sütunları üzerinde değişiklik yapılmıştır. Nesne Modified
durumdadır.

6- Nesneye AcceptChanges uygulanmıştır. Bu durumda nesne


Unchanged durumdadır.

7- Nesne delete fonksiyonu ile nesne silinmiştir. Nesne Deleted


duruma gelmiştir.

{17.02.2008 Pazar}
DataRow NESNESİNİN VERSİYONLARI

Aslında bir DataRow nesnesi, sütunların bir tek versiyonunu tutmaz.


Birkaç versiyonunu tutar. DataRow nesnesinin sütun bilgilerine iki
parametreli indeksleyici ile erişilir. Anımsanacağı gibi DataRow nesnesinin
string ve int parametre alan iki ayrı indeksleyicisi vardı. Yine DataRow
sınıfının birinci parametresi int ve string olan ikinci parametresi
DataRowVersion isimli enum türünden olan indeksleyicileri de vardır. O
halde bu indeksleyicinin kullanımı aşağıdaki gibidir:

dr[“Name”, DataRowVersion.Current] = “Kaan Aslan”;

ya da

dr[0, DataRowVersion.Current] = “Kaan Aslan”;

DataRow nesnesinin versiyonları şöyledir:

Current: Sütun bilgilerinin o anki güncel değerlerine ilişkin versiyondur.

Original: DataRow nesnesinin sütunlarının bu versiyonu başlangıçta


yaratılmaz. DataRow tabloya eklenip AcceptChanges uygulandıktan sonra
bu versiyon oluşturulur ve henüz Current versiyonla aynıdır. Fakat daha
sonra DataRow üzerinde değişiklik yapıldığında artık Current versiyon ile
Original versiyon birbirinden farklı olur. Örneğin; nesnesinin name
sütununda “Kaan Aslan” bilgisi bulunuyor olsun. Biz bu nesneyi tabloya
ekleyip AcceptChanges fonksiyonunu çağıralım. Şimdi Original versiyon
yaratılır ve Current ile aynıdır. Şimdi biz name sütunundaki “Kaan Aslan”
bilgisini “Ali Serçe” olarak değiştirmiş olalım. Artık Current versiyon
“Ali Serçe”, Original versiyon “Kaan Aslan” olacaktır. Bundan sonra
yeniden AcceptChanges uygularsak Original versiyon ile Current
versiyon aynı olur. Yani iki versiyonda da “Ali Serçe” vardır.

245
Default: Default versiyon eğer DataRow nesnesinin durumu Edit
Modified ya da Deleted ise Current, Detached ise Proposed
versiyondur.

Proposed: DataRow üzerinde BeginEdit fonksiyonu çağrıldığında


Proposed versiyon yaratılır ve artık yapılan değişiklikler Proposed
versiyonla yansır. EndEdit fonksiyonu uygulandığında Proposed versiyonu
Current versiyonu haline gelir. RejectChange versiyonu uygulandığında
Proposed versiyonu yok edilir.

VERİTABANI BAĞLANTISININ SAĞLANMASI

Veritabanı yönetim sistemleri genellikte client-server mimarisine


uygun yazıldığına göre işlemleri yapmadan önce bir bağlantı
gerekmektedir.

Bağlantı işlemi veritabanı yönetim sistemi ile doğrudan ilgilidir.


Veritabanı yönetim sistemlerinin ayrı bağlantı sınıfları vardır. Bağlantı
oluşturmak için kullanılan sınıflar XXXConnection biçiminde
isimlendirilmiştir. Microsoft Access Jet motoruna, MySQL’e ve ODBC
sistemine bağlanmak için OleDbConnection sınıfı, ODBC arayüzü ile
bağlantı yapmak için OdbcConnection, SQL Server’a bağlanmak için
SqlConnection, Oracle veritabanına bağlanmak için OracleConnection
sınıfı kullanılır. Aslında bu sınıflar birbirinden farklı olmasına rağmen ortak
elemanlar içermektedir. Hatta bu zorunlu tutulmuştur. Microsoft tüm
connection sınıfların ortak elemanlarını IdbConnection arayüzünde
toplamıştır. Yani bir veritabanı yönetim sistemi için connection sınıf
yazılacaksa bu arayüz desteklenmelidir ve programcı mecburen bu
arayüzün elemanlarını yazmak zorundadır. DbConnection isimli sınıf
IdbConnection arayüzünü desteklemektedir. Fakat DbConnection sınıfında
arayüz fonksiyonları yazılmadan abstract olarak bırakılmıştır. Fakat
DbConnection sınıfına başka faydalı fonksiyonlar da yerleştirilmiştir. Bu
durumda yeni bir bağlantı sınıfı yazacak olan kişi IdbConnection
arayüzünü desteklemek yerine DbConnection sınıfından türetme de
yapabilir. Nasıl olsa IdbConnection arayüzündeki fonksiyonlar,
DbConnection sınıfında abstract olarak belirtilmiştir. DbConnection
sınıfından türetme yapan kişi, bu fonksiyonları override etmek
zorundadır. O halde Microsoft kendisinin sağladığı bağlantı sınıflarının
türetme durumu şöyledir:

IdbConnection

DbConnection

246
XXXConnection
Özetle bu connection sınıflarının pek çok ortak elemanları vardır. Bu
nedenle OleDbConnection ile SqlConnection sınıflarının kullanımı çok
benzerdir.

Veritabanı yönetim sistemlerine bağlanmak, bir dosya açma işlemine


benzer. Bağlantı sırasında en azından veritabanının ismi, çeşitli yetkiler
belirtilmelidir. Bağlantıda kullanılacak tüm bu özellikler, bir yazı biçiminde
ifade edilip tek bir yazı biçiminde XXXConnection sınıfına verilir. Buna
ConnectionString denilmektedir. ConnectionString her Veritabanı
yönetim sistemi için bulundurulmak zorundadır. Fakat formatı Veritabanı
yönetim sistemine göre değişmektedir. Burada Access Jet motoruna ilişkin
ConnectionString üzerinde durulacaktır. Aslında ConnectionString
Visual Studio sihirbazları ile görsel biçimde oluşturulabilmektedir.
ConnectionString IdbConnection arayüzünden gelen read/write string
türden public bir propertydir. Dolayısıyla tüm connection sınıflarında bu
eleman vardır.

OleDbConnection için ConnectionString en basit olarak Provider


ve Data Source belirtilerek oluşturulur. Elemanlar arasında “ ; ” ile ayırma
yapılmalıdır. Access Jet motoru için Provider
“Privider=Microsoft.Jet.OLEDB.4.0” Data Source ilgili .mdb dosyasının
yol ifadesi biçiminde olmalıdır. Örnek; bir ConnectionString şöyledir:

“Privider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\bin\LocalAccess.mdb”

Bağlantı işlemi OleDbConnection sınıfının IdbConnection arayüzünden


gelen Open fonksiyonu ile yapılır. Close fonksiyonu ile kapatılır. Ancak bir
exception oluşması durumunda akış başka yere giderse bağlantının
kapatılması gerekeceğinden dolayı açma ve kapatma işlemlerinin try-
finally bloklarında yapılması önerilir.

OleDbConnection dbCon = new OleDbConnection();


DbCon.ConnectionString = “...”;

try
{
dbCon.Open();
//...
}
finally
{
dbCon.Close();

247
}

OleDbConnection sınıfının string parametreli başlangıç fonksiyonu,


bağlantı yazısını zaten parametre olarak almaktadır. Örneğin:

OleDbConnection dbCon = new OleDbConnection(“...”);

OleDbConnection sınıfı, Dispose arayüzünü destekler. Dispose fonksiyonu


kendi içerisinde Close fonksiyonunu çağırmaktadır. Yani Close fonksiyonu
yerine Dispose fonksiyonu da çağrılabilir. using deyimi bir exception
oluştuğunda ya da bloktan çıkıldığında Dispose fonksiyonunun
çağrılmasına yol açacağından, bağlantının açılması ya da kapatılması
using deyimi ile de şöyle yapılabilir.

using (OleDbConnection dbCon = new OleDbConnection(“...”))


{
dbCon.Open();
//...
}

Programcı connection bir sınıfı veri elemanı yaparsa ve nesneyi başlangıç


fonksiyonunda yaratırsa daha etkin bir erişim sağlar. Fakat bağlantının
sürekli açık bırakılması iyi bir teknik değildir.

Bir Access dosyasına bağlanma:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AdoSample1
{
public partial class Form1 : Form
{
private OleDbConnection m_dbCon;

public Form1()
{
InitializeComponent();

m_dbCon = new OleDbConnection();


m_dbCon.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0; Data
Source=E:\DotNetAppBasic\AdoSample1\AdoSample1\Person.mdb";

248
}

private void button1_Click(object sender, EventArgs


e)
{
try
{
m_dbCon.Open();
//...
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}
}

KAYIT GERİ DÖNDÜRMEYEN SQL KOMUTLARININ ÇALIŞTIRILMASI

SQL komutlarını ADO.Net bağlamında, kayıt geri döndüren komutlar ve


kayıt geri döndürmeyen komutlar olmak üzere iki bölüme ayırabiliriz.
Örneğin; SELECT komutu, kayıt geri döndüren bir komuttur. Kayıt geri
döndüren komutlar ayrı bir başlıkta değerlendirilecektir. Çünkü onların geri
döndürdükleri kayıtların depolanması konusu, göreli olarak daha
karmaşıktır. Hâlbuki INSERT, UPDATE, DELETE, COUNT gibi komutlar kayıt
geri döndürmemektedir. Bunların çalıştırılması oldukça kolaydır.

Bir SQL komutu, düz bir metin olarak değil bir sınıfla temsil
edilmektedir. Dolayısıyla bizden SQL komut yazının kendisi değil bu sınıf
istenecektir. SQL komutları XXXCommand sınıfları ile temsil edilmektedir.
Örneğin OleDbCommand, OdbcCommand, SqlCommand gibi. Yine XXXCommand
sınıfları DbCommand isimli abstract sınıftan türetilmiştir. DbCommand sınıfı
da IdbCommand arayüzünü desteklemektedir.

IdbCommand

DbCommand

XXXCommand
249
Böylece bir veritabanı yönetim sistemi için comment sınıfı yazacak olan kişi
ya IdbCommand arayüzünü desteklemeli ya da DbCommand sınıfından
türetme yapmalıdır. OleDbCommand sınıfının CommandText isimli string
türden property elemanı IdbConnection arayüzünden gelmektedir.
Çalıştırılacak SQL cümlesi, bu property elemanınma girilmelidir. Tabi nesne
string parametreli başlangıç fonksiyonu ile de yaratılabilir. Örneğin:

OleDbCommand cmd = new OleDbCommand();


cmd.CommandText = “INSERT INTO Info(PersonName, PersonNo)
VALUES(‘Kaan Aslan’, 123)”;

OleDbCommand sınıfının Connection isimli property elemanına connection


nesnesinin referansı girilmelidir. Görüldüğü gibi command nesnesi kendi
içerisinde connection nesnesini de tutmaktadır. Zaten OleDbCommand
sınıfının bu belirlemeleri hemen set eden string ve OleDbConnection
parametreli başlangıç fonksiyonuda vardır.

OleDbCommand sınıfının ExecuteNonQuery parametresiz fonksiyonu,


SQL cümleciğini veritabanı yönetim sistemine ileterek komutun
çalıştırılmasını sağlar. ExecuteNonQuery fonksiyonunun geri dönüş değeri,
işlemden etlikenen kayıt sayısını vermektedir. Başarısızlık durumunda
çeşitli exceptionlar oluşmaktadır. (ExecuteNonQuery yanlış isimlendirilmiş
bir fonksiyondur. Halbuki bir SQL cümlesinin kayıt geri döndürüp
döndürmemesi ile sorgulama konusunun bir ilgisi yoktur.)

Örnek bir Access dosyasına kayıt ekleme şöyle yapılabilir:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AdoSample1
{
public partial class Form1 : Form
{
private OleDbConnection m_dbCon;

public Form1()
{

250
InitializeComponent();

m_dbCon = new OleDbConnection();


m_dbCon.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0; Data
Source=E:\DotNetAppBasic\AdoSample1\AdoSample1\Person.mdb";
}

private void button1_Click(object sender, EventArgs


e)
{
try
{
m_dbCon.Open();

OleDbCommand cmd = new OleDbCommand();


cmd.CommandText = "INSERT INTO
Info(PersonName, PersonNo) VALUES('Ali Serce', 345)";
cmd.Connection = m_dbCon;
cmd.ExecuteNonQuery();

//...
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}
}

Örneğin; kayıt silme işlemini yaptığımızda eğer koşula uygun kayıt


bulunamazsa ExecuteNonQuery fonksiyonu “ 0 ” ile geri döner.

KOMUT YAZISININ OLUŞTURULMASI

SQL komut yazısının oluşturulması önemli bir problemdir. Örneğin;


bir kayıt silinecekse DELETE komutunda kayıtın tüm sütunlarına ilişkin
koşullar AND operatörü ile birleştirilmelidir ya da örneğin UPDATE işleminde
güncellenecek kayıt ile güncellenecek değerler komut yazısında
belirtilmelidir.

Genellikle programcılar kayıt eklemek için, güncelleme yapmak için


formlar oluşturur. Buradan bilgilerin alınarak SQL yazısının oluşturulması
gerekir. Bu işlemler için çeşitli kolaylaştırıcı işlemler bulundurulmuştur.

251
Komut yazısının kolay bir biçimde oluşturulması için parametre
yöntemi düşünülmüştür. Bu yöntemde programcı SQL komutunda
değişebilecek yerlere, yer tutucular yerleştirir. Bu yer tutuculara karşılık
gelecek değerleri de OleDbCommand sınıfının Parameters isimli
collectionuna girer. Parameters collectionu DbCommand sınıfından
gelmektedir. Parameters isimli collection property, XXXParameters
türünden nesneleri tutmaktadır. OleDb için bu sınıf OleDbParameters
sınıfıdır. OleDb uygulamalarında yer tutucu olarak “ ? ” kullanılır. Örneğin:

cmdText = "INSERT INTO Info(PersonName, PersonNo) VALUES(?,?)”;

Burada programcı henüz komutu işletmeden Parameters collectionuna iki


tane değer girmelidir. Böylece ExecuteNonQuery uygulandığında
OleDbCommand sınıfı bu yer tutucular yerine Parameters collectionundaki
bilgileri yerleştirerek sorgulamayı yapar. Böylece örneğin; kayıt ekleme
ekranında programcı TextBox kontrollerindeki yazıları alarak Parameters
collectionuna eklemek zorundadır. Yer tutucu olarak SQL Server
uygulamalarında “ @ “ karakteri ile başlayan isimler kullanılmaktadır.
Örneğin:

cmdText = "INSERT INTO Info(PersonName, PersonNo) VALUES(@Name,@No)”;

SQL Server’da eşleştirme isim yoluyla yapılmaktadır. Hâlbuki OleDb de sıra


yoluyla yapılmaktadır. Yani XXXParameters sınıfının bir Name elemanı
vardır. SQL Server’da o Name elemanına, yer tutucularda kullanılan
değerler girilmelidir. Halbuki OleDb’de “ ? ” işaretleri ile eşleme
Parameters collectionundaki sıraya göredir.

{23.02.2008 Cumartesi}

OleDb veritabanları için parametre sınıfı OleDbParameters sınıfıdır.


Parametreler OleDbCommand sınıfının OleDbParameterCollection
türünden Parameters collectionuna yerleştirilir. Yani Parameters isimli
property elemanı OleDbParameterCollection isimli collection
türündendir. Bu collection ise OleDbParameters nesnelerini tutmaktadır. O
halde programcı komuttaki yer tutucu sayısı kadar OleDbParameters
nesnesi oluşturup, bunu Parameters collectionuna yerleştirir.

OleDbParameters sınıfının ParameterName isimli property elemanı,


ilgili parametrenin ismini almaktadır. Parametre isimleri OleDb‘de
kullanılmaz. Fakat SQL Server’da kullanılır. Sınıfın object türünden Value
isimli property elemanı, ilgili parametrenin değerini içermektedir. Bu Value
propertysinin türü, ayrıca OleDbType propertysi ile belirlenmelidir. Bunların
dışında OleDbParameters sınıfının önemli başka propertyleri de vardır. Bu

252
propertyler ileride kullanılacaktır. OleDbParameters sınıfının bazı
başlangıç fonksiyonları, zaten bu propertyleri otomatik set etmektedir.

Örneğin:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AdoSample1
{
public partial class Form1 : Form
{
private OleDbConnection m_dbCon;

public Form1()
{
InitializeComponent();

m_dbCon = new OleDbConnection();


m_dbCon.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0; Data
Source=E:\DotNetAppBasic\AdoSample1\AdoSample1\Person.mdb";
}

private void addRecordToolStripMenuItem_Click(object


sender, EventArgs e)
{
AddRecordForm arf = new AddRecordForm();

if (arf.ShowDialog() == DialogResult.OK)
{
string cmdText = "INSERT INTO Info(PersonName,
PersonNo) VALUES(?,?)";

try
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = cmdText;

OleDbParameter param1 = new


OleDbParameter("Name", OleDbType.VarChar);
param1.Value = arf.PersonName;

OleDbParameter param2 = new


OleDbParameter("No", OleDbType.Integer);
param2.Value = arf.PersonNo;

253
cmd.Parameters.Add(param1);
cmd.Parameters.Add(param2);

cmd.Connection = m_dbCon;

m_dbCon.Open();
cmd.ExecuteNonQuery();

//...
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}
}
}

Aslında Parameters propertysinin ilişkin olduğu collection sınıfın Add


fonksiyonları, zaten kendi içerisinde OleDbParameters nesnesini yaratıp
eklemeyi yapmaktadır. O halde aynı işlem şöyle de yapılabilir:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AdoSample1
{
public partial class Form1 : Form
{
private OleDbConnection m_dbCon;

public Form1()
{
InitializeComponent();

m_dbCon = new OleDbConnection();

254
m_dbCon.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0; Data
Source=E:\DotNetAppBasic\AdoSample1\AdoSample1\Person.mdb";
}

private void addRecordToolStripMenuItem_Click(object


sender, EventArgs e)
{
AddRecordForm arf = new AddRecordForm();

if (arf.ShowDialog() == DialogResult.OK)
{
string cmdText = "INSERT INTO Info(PersonName,
PersonNo) VALUES(?,?)";

try
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = cmdText;

OleDbParameter param;

param = cmd.Parameters.Add("Name",
OleDbType.VarChar);
param.Value = arf.PersonName;

param = cmd.Parameters.Add("No",
OleDbType.Integer);
param.Value = arf.PersonNo;

cmd.Connection = m_dbCon;

m_dbCon.Open();
cmd.ExecuteNonQuery();

//...
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}
}
}

255
DataSet SINIFI

DataTable veritabanındaki bir tabloyu bellekte temsil etmek için


düşünülmüştür. DataSet ise, bir grup tabloyu temsil etmekte kullanılır.
Yani DataSet nesnesi data tablolarından oluşmaktadır. DataSet sınıfının
Tables isimli collection property elemanı, DataTableCollection isimli
collection türündendir. Yani bu propertynin elemanları DataTable nesnesini
oluşturmaktadır.

VERİTABANI KAYITLARININ ELDE EDİLMESİ

Veritabanından kayıtları elde etmek için temel olarak SELECT SQL


komutu kullanılmaktadır. Bu komut bize bir grup kayıt geri döndürür. Kayıt
elde etmek için birkaç strateji vardır. Bunlardan en basiti, kayıtların tek tek
elde edilmesidir. Bu yöntemde programcı sorgulamayı yapar. Sonra elde
edilen kayıtları bir döngü içerisinde dolaşır. Her fonksiyon çağırmasında
koşulu sağlayan kaydı elde eder ve bunları biriktirir.

Kayıtların tek tek elde edilmesi için XXXDataReader sınıfı


kullanılmaktadır. OleDb işlemi için söz konusu olan sınıf OleDbDataReader
sınıfıdır.

OleDbDataReader sınıfı ile kayıtların tek tek elde edilmesi şöyle


gerçekleştirilir:

1- Programcı bir OleDbCommand nesnesi oluşturur ve SELECT komutunu


bu command nesnesine yerleştirir.

2- OleDbCommand sınıfının static olmayan ExecuteReader fonksiyonu


çağrılır. ExecuteReader fonksiyonu SQL cümlesini veritabanı yönetim
sistemine uygulatır ve bir OleDbDataReader nesnesi ile geri döner.
Görüldüğü gibi XXXDataReader nesneleri programcı tarafından new
operatörü ile yaratılmaz XXXCommand sınıflarının ExecuteReader
fonksiyonu tarafından verilir. ExecuteReader fonksiyonu çağrılmadan
önce veritabanı Open fonksiyonu ile açılmalıdır.

3- Programcı artık kayıtları tek tek elde etmelidir. Programcı


OleDbDataReader sınıfının Read fonksiyonu ile, sıradaki kaydı elde
eder. Son kayıtta okunduktan sonra bu fonksiyon false değerini
döndürür. O halde tüm kayıtların gözden geçirilmesi aşağıdaki döngü
ile sağlanabilir:

while(dr.Read())
{
//...
}

256
4- XXXDataReader sınıflarının iki indeksleyici elemanları vardır. String
parametreli indeksleyici, veritabanı tablosunun sütun bilgisini alarak,
o andaki kaydın sütun bilgisini verir. Int parametreli indeksleyici ise,
tablonun sütun indeks numarasını alarak, okunmuş olan kaydın
sütun numarasını verir. O halde örneğin; SELECT edilmiş kayıtların
elde edilmesi şöyle yapılabilir:

while(dr.Read())
{
string name = (string) dr[“PersonName”];
string no = (int) dr[“PersonNo”];

//...
}

Hocanın Örneği(SelectForm.cs):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb;

namespace AdoSample1
{
public partial class SelectForm : Form
{
private OleDbConnection m_dbCon;

public SelectForm(OleDbConnection dbCon)


{
InitializeComponent();

m_dbCon = dbCon;
}

private void button1_Click(object sender, EventArgs e)


{

string cmdText = "SELECT PersonNo, PersonName FROM


Info WHERE " + textBox1.Text;

try
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = cmdText;

257
cmd.Connection = m_dbCon;

m_dbCon.Open();
OleDbDataReader dr = cmd.ExecuteReader();
listView1.Items.Clear();

while (dr.Read())
{
string name = (string)dr["PersonName"];
int no = (int)dr["PersonNo"];
ListViewItem lvi = new ListViewItem(new
string[] {name, no.ToString()});
listView1.Items.Add(lvi);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}

}
}
}

OleDbDataReader sınıfının bool türden HasRows isimli property


elemanı, sorgulama sonucunda kayıt elde edilip edilemediğini kontrol eder.

Anımsanacağı gibi okunmuş olan kaydın sütun bilgileri


indeksleyicilerle elde edilmektedir. İndeksleyiciler bize bilgiyi, object
olarak vermektedir. Bu nedenle, değer türleri için kutuyu açma
dönüştürmesi(unboxing conversation) uygulamak gerekir. Bu durum
zaman kaybına yol açacağı için XXXDataReader sınıflarına GetYYY isimli
(YYY burada bir tür belirtir) fonksiyonlar yerleştirilmiştir. Örneğin; sınıfın
GetInt32 fonksiyonu, doğrudan bize int değeri verir. Biz de aşağıya doğru
dönüştürme uygulamak zorunda kalmayız. Örneğin:

while (dr.Read())
{
string name = dr.GetString(0);
int no = dr.GetInt32(1);
ListViewItem lvi = new ListViewItem(new string[] {name,
no.ToString()});
listView1.Items.Add(lvi);
}

258
SORGULAMA SONUCUNDA ELDE EDİLEN KAYITLARIN DATASET VE
DATABLE NESNELERİNE YERLEŞTİRİLMESİ

XXXDataReader sınıfları ile kayıtları tek tek elde edebiliyoruz. Bazı


uygulamalarda bu yöntem uygun bir yöntemdir. Fakat pek çok
uygulamalarda, bu şekilde tek tek elde etme yöntemi tercih
edilmemektedir. İşte XXXDataAdapter sınıfları, sorgulanan kayıtları
DataTable ya da DataSet nesnelerinin içerisine doldurmaktadır. OleDb
veritabanları için adapter sınıf OleDbAdapter sınıfıdır. XXXDataAdapter
sınıflarının kullanımı biraz daha karışıktır.

XXXDataAdapter sınıfı türünden nesne yaratılırken, en azından


programcının sınıfa bir connection nesnesini bir de SQL SELECT komutunu
vermesi gerekmektedir. XXXDataAdapter sınıflarının SelectCommand isimli
property elemanı, XXXCommand sınıfı türündendir. Programcı bu propertye
SQL SELECT komutunu yerleştirdiği command nesnesini girmektedir.
Connection nesnesi sınıfın başlangıç fonksiyonu ile verilir. Programcı
isterse XXXDataAdapter sınıfının başlangıç fonksiyonunda SQL SELECT
komutunun yazısını verebilir. Bu durumda sınıfın kendisi command nesnesi
oluşturup SelectCommand‘a yerleştirir. O halde XXXDataAdapter nesnesi
şöyle yaratılabilir:

OleDbDataAdapter da = new OleDbDataAdapter(“SELECT * FROM


Info”, m_dbCon);

XXXDataAdapter sınıfının Fill isimli fonksiyonları, sorgulama işlemini


yaparak elde edilen kayıtları bir DataSet içerisine ya da DataTable
içerisine yerleştirir. Örneğin:

private void fillToolStripMenuItem_Click(object sender,


EventArgs e)
{
try
{
OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT PersonName, PersonNo FROM Info WHERE
PersonNo < 50000", m_dbCon);

DataTable dt = new DataTable();

da.Fill(dt);

FillDialog fd = new FillDialog(dt);

fd.ShowDialog();
}
catch (Exception ex)
{

259
MessageBox.Show(ex.Message);
}
}

Fill fonksiyonları, DataReader ile kayıtların tek tek elde edilmesinden


daha hızlıdır. Aynı işlem DataSet nesnesi ile şöyle yapılabilir:

private void fillToolStripMenuItem_Click(object sender,


EventArgs e)
{
try
{
OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT PersonName, PersonNo FROM Info WHERE
PersonNo < 50000", m_dbCon);

DataSet ds = new DataSet();

da.Fill(ds);

FillDialog fd = new FillDialog(ds.Tables[0]);

fd.ShowDialog();
}
catch (Exception ex)
{

MessageBox.Show(ex.Message);
}
}

Fill fonksiyonu DataTable ile birlikte kullanıldığında, fonksiyon


DataTable nesnesinin TableName propertysine dokunmaz. Fakat DataSet
ile kullanıldığında, kendi oluşturduğu ilk tabloya “Table”, sonraki tabloya
“Table1” gibi isimler vermektedir. Eğer sorgulamadan tek bir tablo elde
ediliyorsa bunun ismini Table değil de kendi istediği bir isim olmasın
isteyebilir:

DataSet ds = new DataSet();

da.Fill(ds, “Info”);

Burada artık Fill fonksiyonunun DataSet içerisinde yarattığı tablonun


ismi Info olacaktır.

{24.02.2008 Pazar}

DATASET YA DA DATATABLE ÜZERİNDE YAPILAN


DEĞİŞİKLİKLERİN VERİTABANINA YANSITILMASI

260
SELECT cümlesi ile bir DataSet ya da DataTable üzerine taşınmış
olan bilgilerde değişiklik yaptığımızda, bu durum yapılan değişikliklerin
diskteki veritabanına yansıtılacağı anlamına gelmez. Programcı çoğu kez
bellekte gerçekleştirilen değişikliklerden veritabanının etkilenmesini ister.
Örneğin; biz Fill fonksiyonu ile kayıtları bir DataTable içerisine
doldurduktan sonra, DataGridView kontrolü ile, kayıtları tek hamlede
DataGridView kontrolü üzerinde görüntüleyebiliriz. Bunun için tek
yapacağımız şey DataGridView kontrolünün DataSource elemanına,
DataTable nesnesini atamaktır. Bu işlemden sonra biz DataGridView
üzerinde değişiklik yaptığımızda, bu işlemlerden DataTable içerisindeki
satırlar etkilenir. Fakat veritabanı etkilenmez. Görüntüleme işlemi
DataGridView‘lar yerine TextBox’larla da yapılabilirdi. Aynı durum onun
için de geçerlidir. Şüphesiz programcı bu kontroller üzerindeki güncelleme
işlemlerini en azından bir tuşa basıldıktan sonra veritabanına yansıtılmasını
isteyecektir. DataTable ya da DataSet bellekte, veritabanından bağımsız
bir collection nesnesidir. Fill fonksiyonu ile biz kayıtları bu nesnelere
doldurduktan sonra veritabanı ile bu nesneler arasında bir bağlantı yoktur.

XXXDataAdapter sınıfları yalnızca veritabanından belleğe aktarım


yapmaz. Aynı zamanda bellekten de veritabanına geri aktarım yapar. Biz
yalnızca XXXDataAdapter sınıfının Fill fonksiyonunu görmüş durumdayız.
Anımsanacağı gibi Fill fonksiyonu bağlantıyı kendisi açarak işlemini
yürütür ve bağlantıyı keser.

XXXDataAdapter sınıfları, aslında dört farklı command nesnesi


tutmaktadır. Bu nesneler SelectCommand, UpdateCommand, InsertCommand
ve DeleteCommand property elemanlarıdır. Biz XXXDataAdapter sınıfı için
yalnızca SelectCommand nesnesini kullandık. Aslında sınıfın başlangıç
fonksiyonunda verilen SQL cümlesi de command nesnesine dönüştürülerek
sınıfının SelectCommand property elemanına atanmaktadır.

XXXDataAdapter sınıflarının Update isimli fonksiyonları, veritabanını


güncellemek için kullanılır. Sınıfın iki önemli Update fonksiyonu, DataSet
ve DataTable parametresi alan fonksiyonlardır. Programcı bellekte
değişiklik yaptığı DataSet ya da DataTable nesnesini Update
fonksiyonuna vermelidir.

Programcı XXXDataAdapter sınıflarının Update fonksiyonlarını


uygulamadan önce kesinlikle InsertCommand, UpdateCommand ve
DeleteCommand property elemanlarını, command nesnelerine girmiş
olmalıdır. SelectCommand nesnesinin, Update fonksiyonu ile bir ilgisi
yoktur. Fill fonksiyonu ile ilgisi vardır. İşte XXXDataAdapter sınıflarının
Update fonksiyonu, tablo satırlarını gözden geçirmektedir. Bu satırların

261
RowState elemanlarına bakar. Eğer RowState elemanı Edit ise
InsertCommand nesnesini, RowState elemanı Modified ise UpdateCommand
ve RowState elemanı Deleted ise DeleteCommand nesnelerini kullanarak
ExecuteNonQuery fonksiyonunu uygular. Bunu durumu değişmiş tüm
satırlar için yapmaktadır.

Peki, programcı bu command nesnelerindeki SQL komutlarını nasıl


girmelidir? Komutlarda değişmiş satırlara ilişkin bilgiler bulunmalıdır. İşte
programcı bu komutları parametrik bir biçimde soru işaretlerini kullanarak
oluşturmalıdır. Ayrıca programcı bu yer tutucular için parametre
nesnelerini de oluşturmalı ama yalnızca parametrelerin yerlerini “ ? “ ile
boş bırakmalıdır. Parametrelerin gerçek değerleri, Update fonksiyonu
tarafından satır bilgilerinden alınarak oluşturulacaktır. Örneğin; kayıt
güncelleme için kullanılacak SQL cümlesi aşağıdaki gibi olabilir:

Update Info SET PersonName=?, PersonNo=? WHERE PersonName=?


AND PersonNo=?

Görüldüğü gibi komutta dört parametre vardır. Bizim de


UpdateCommand nesnesine dört parametre nesnesi eklememiz gerekir.
Update fonksiyonu, birinci parametre için Value değeri olarak, satırın
PersonName sütununa ilişkin Current versiyonunun değerini ataması
gerekir. İkinci soru işareti için satırın PersonNo sütununa ilişkin Current
değerini ataması gerekir. Üçüncü soru işareti için satırın PersonName
sütununun Original versiyonunu, dördüncü soru işareti içinse satırın
PersonNo sütununa ilişkin Original versiyonunu ataması gerekir. Peki,
Update fonksiyonu her “ ? “ için bu bilgilerin yerleştirileceğini nereden
bilmektedir? İşte parametre nesnelerinin SourceColumn ve
SourceVersion isimli elemanları, gereksinim duyulan bu bilgileri
içermektedir.

Şimdi yapılacak işlemi Update fonksiyonunun gözüyle bakalım. Yani


Update fonksiyonu nasıl yazılmıştır?

1- Fonksiyon tek tek satırları gözden geçirmektedir. Değişmemiş satırlar


üzerinde işlem yapmamaktadır.

2- Update fonksiyonu bir satırda değişiklik görürse, uygun command


nesnesini alır ve onun parametrelerine bakar. Parametre
nesnelerindeki SourceColumn ve SourceVersion bilgilerini dikkate
alarak Value değerlerini buna göre yerleştirir.

262
3- Artık komutun parametreleri tam olarak oluşturulmuştur ve bu
komut nesnesi ile ExecuteNonQuery fonksiyonu uygulanır.

SİLİNMİŞ SATIRLAR İÇİN DELETECOMMAND NESNESİNİ


OLUŞTURULMASI
Person veritabanı için yukarıda açıklanan biçimde DeleteCommand nesnesi
aşağıdaki gibi oluşturulabilir:

Fonksiyon olarak oluşturulması:

private OleDbCommand CreateDeleteCommand()


{
OleDbCommand deleteCmd = new OleDbCommand();
deleteCmd.Connection = m_dbCon;
deleteCmd.CommandText = "DELETE FROM Info WHERE
PersonName=? AND PersonNo=?";

OleDbParameter param;

param = deleteCmd.Parameters.Add("Name",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Original;

param = deleteCmd.Parameters.Add("No",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Original;

return deleteCmd;
}

Fill de kullanımı:

private void fillToolStripMenuItem_Click(object sender,


EventArgs e)
{
try
{
OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT PersonName, PersonNo FROM Info", m_dbCon);

DataSet ds = new DataSet();


da.Fill(ds, "Info");

FillDialog fd = new FillDialog(ds.Tables[0]);

if (fd.ShowDialog() == DialogResult.Yes)
{
da.DeleteCommand = CreateDeleteCommand();

263
da.Update(ds.Tables["Info"]);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

KAYIT EKLEME İÇİN INSERTCOMMAND NESNESİNİN


OLUŞTURULMASI

Fonksiyon olarak oluşturulması:

private OleDbCommand CreateInsertCommand()


{
OleDbCommand insertCmd = new OleDbCommand();
insertCmd.Connection = m_dbCon;
insertCmd.CommandText =
"INSERT INTO Info(PersonName, PersonNo) VALUES(?,?)";

OleDbParameter param;

param = insertCmd.Parameters.Add("Name",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Current;

param = insertCmd.Parameters.Add("No",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Current;

return insertCmd;
}

Fill de kullanımı:

private void fillToolStripMenuItem_Click(object sender,


EventArgs e)
{
try
{
OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT PersonName, PersonNo FROM Info", m_dbCon);

DataSet ds = new DataSet();

da.Fill(ds, "Info");

FillDialog fd = new FillDialog(ds.Tables[0]);

264
if (fd.ShowDialog() == DialogResult.Yes)
{
da.DeleteCommand = CreateDeleteCommand();
da.InsertCommand = CreateInsertCommand();

da.Update(ds.Tables["Info"]);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

KAYIT GÜNCELLEME İÇİN UPDATECOMMAND NESNESİNİN


OLUŞTURULMASI

Fonksiyon olarak oluşturulması:

private OleDbCommand CreateUpdateCommand()


{
OleDbCommand updateCmd = new OleDbCommand();
updateCmd.Connection = m_dbCon;
updateCmd.CommandText =
"UPDATE Info SET PersonName=?, PersonNo=? WHERE
PersonName=? AND PersonNo=?";

OleDbParameter param;

param = updateCmd.Parameters.Add("NameCurrent",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Current;

param = updateCmd.Parameters.Add("NoCurrent",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Current;

param = updateCmd.Parameters.Add("NameOriginal",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Original;

param = updateCmd.Parameters.Add("NoOriginal",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Original;

return updateCmd;
}

265
Fill içinde kullanımı:

if (fd.ShowDialog() == DialogResult.Yes)
{
da.DeleteCommand = CreateDeleteCommand();
da.InsertCommand = CreateInsertCommand();
da.UpdateCommand = CreateUpdateCommand();

da.Update(ds.Tables["Info"]);
}

Komple Form1.cs dosyası:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AdoSample1
{
public partial class Form1 : Form
{
private OleDbConnection m_dbCon;

public Form1()
{
InitializeComponent();

m_dbCon = new OleDbConnection();


m_dbCon.ConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0; Data
Source=E:\DotNetAppBasic\AdoSample1\AdoSample1\Person.mdb";
}

private void addRecordToolStripMenuItem_Click(object


sender, EventArgs e)
{
AddRecordForm arf = new AddRecordForm();

if (arf.ShowDialog() == DialogResult.OK)
{
string cmdText = "INSERT INTO Info(PersonName,
PersonNo) VALUES(?,?)";

try
{
OleDbCommand cmd = new OleDbCommand();

266
cmd.CommandText = cmdText;

OleDbParameter param;

param = cmd.Parameters.Add("Name",
OleDbType.VarChar);
param.Value = arf.PersonName;

param = cmd.Parameters.Add("No",
OleDbType.Integer);
param.Value = arf.PersonNo;

cmd.Connection = m_dbCon;

m_dbCon.Open();
cmd.ExecuteNonQuery();

//...
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}

private void
deleteRecordToolStripMenuItem_Click(object sender, EventArgs
e)
{
DeleteRecordForm drf = new DeleteRecordForm();

if (drf.ShowDialog() == DialogResult.OK)
{
string cmdText = "DELETE FROM Info WHERE " +
drf.Condition;

try
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = cmdText;
cmd.Connection = m_dbCon;

m_dbCon.Open();
if (cmd.ExecuteNonQuery() == 0) {
MessageBox.Show("Record not found");
}

267
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}

private void selectToolStripMenuItem_Click(object


sender, EventArgs e)
{
SelectForm sf = new SelectForm(m_dbCon);

if (sf.ShowDialog() == DialogResult.OK)
{

}
}

private void
randomRecordsToolStripMenuItem_Click(object sender, EventArgs
e)
{
RandomRecordsForm rrf = new RandomRecordsForm();

if (rrf.ShowDialog() == DialogResult.OK)
{
try
{
m_dbCon.Open();
string cmdText = "INSERT INTO
Info(PersonName, PersonNo) VALUES(?,?)";
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = cmdText;

for (int i = 0; i < rrf.RecordCount; ++i)


{
Person per = getRandomRecord();

OleDbParameter param;

cmd.Parameters.Clear();

param = cmd.Parameters.Add("Name",
OleDbType.VarChar);
param.Value = per.Name;

268
param = cmd.Parameters.Add("No",
OleDbType.Integer);
param.Value = per.No;
cmd.Connection = m_dbCon;
cmd.ExecuteNonQuery();

//...
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
m_dbCon.Close();
}
}
}

private Random m_r = new Random();

private Person getRandomRecord()


{
Person per = new Person();

char [] name = new char[32];

for (int i = 0; i < 32; ++i)


{
name[i] = (char) (m_r.Next(26) + 'A');
}

per.Name = new string(name);


per.No = m_r.Next(1000000000);

return per;
}

private struct Person


{
public string Name;
public int No;
}

private void fillToolStripMenuItem_Click(object


sender, EventArgs e)
{
try
{
OleDbDataAdapter da = new OleDbDataAdapter(

269
"SELECT PersonName, PersonNo FROM
Info", m_dbCon);

DataSet ds = new DataSet();

da.Fill(ds, "Info");

FillDialog fd = new FillDialog(ds.Tables[0]);

if (fd.ShowDialog() == DialogResult.Yes)
{
da.DeleteCommand = CreateDeleteCommand();
da.InsertCommand = CreateInsertCommand();
da.UpdateCommand = CreateUpdateCommand();

da.Update(ds.Tables["Info"]);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

private OleDbCommand CreateDeleteCommand()


{
OleDbCommand deleteCmd = new OleDbCommand();
deleteCmd.Connection = m_dbCon;
deleteCmd.CommandText = "DELETE FROM Info WHERE
PersonName=? AND PersonNo=?";

OleDbParameter param;

param = deleteCmd.Parameters.Add("Name",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Original;

param = deleteCmd.Parameters.Add("No",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Original;

return deleteCmd;
}

private OleDbCommand CreateInsertCommand()


{
OleDbCommand insertCmd = new OleDbCommand();
insertCmd.Connection = m_dbCon;
insertCmd.CommandText =
"INSERT INTO Info(PersonName, PersonNo)
VALUES(?,?)";

OleDbParameter param;

270
param = insertCmd.Parameters.Add("Name",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Current;

param = insertCmd.Parameters.Add("No",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Current;

return insertCmd;
}

private OleDbCommand CreateUpdateCommand()


{
OleDbCommand updateCmd = new OleDbCommand();
updateCmd.Connection = m_dbCon;
updateCmd.CommandText =
"UPDATE Info SET PersonName=?, PersonNo=?
WHERE PersonName=? AND PersonNo=?";

OleDbParameter param;

param = updateCmd.Parameters.Add("NameCurrent",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Current;

param = updateCmd.Parameters.Add("NoCurrent",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Current;

param = updateCmd.Parameters.Add("NameOriginal",
OleDbType.VarChar, 0, "PersonName");
param.SourceVersion = DataRowVersion.Original;

param = updateCmd.Parameters.Add("NoOriginal",
OleDbType.Integer, 0, "PersonNo");
param.SourceVersion = DataRowVersion.Original;

return updateCmd;
}
}
}

COMMAND NESNELERİNİN VE PARAMETRELERİNİN OTOMATİK


OLUŞTURULMASI

Şüphesiz command nesnelerinin yukarıdaki gibi manuel


oluşturulması daha esnek bir yöntemdir. Fakat veritabanının şemasını
inceleyerek yukarıdakine benzer command nesnelerinin otomatik olarak
oluşturulması da mümkündür. Bunu için XXXCommandBuilder sınıfları
kullanılmaktadır.

271
XXXCommandBuilder sınıfları, bir DataAdapter nesnesini alır ve
otomatik bir biçimde command nesnelerini oluşturur. Sınıfın DataAdapter
property elemanı vardır. Fakat atama, başlangıç fonksiyonu yoluyla
yapılabilir. Programcının yapacağı tek şey XXXCommandBuilder nesnesini
oluşturmaktır. XXXDataAdapter nesnesini alan sınıf, bütün bu işlemleri
otomatik olarak yapmaktadır. Yaratılan XXXCommandBuilder nesnesinin
kullanılmasına gerek yoktur. Çünkü komutların üretilmesi bazı event
işlemleri sırasında yapılmaktadır. Yani komutların üretilmesi
XXXCommandBuilder sınıfının başlangıç fonksiyonu yoluyla değil
XXXDataAdapter sınıfındaki bazı eventlerin gerçekleştirilmesi yoluyla
yapılmaktadır. O halde Update işlemi şöyle yapılabilir:

if (fd.ShowDialog() == DialogResult.Yes)
{
OleDbCommandBuilder cmdBuilder = new
OleDbCommandBuilder(da);

//da.DeleteCommand = CreateDeleteCommand();
//da.InsertCommand = CreateInsertCommand();
//da.UpdateCommand = CreateUpdateCommand();

da.Update(ds.Tables["Info"]);
}

Sınıf kendi referansını, DataAdapter içerisindeki bazı eventlere


yazdığına göre aslında DataAdapter yaşadıkça nesne de seçilebilir duruma
gelmeyecektir. O halde aslında new operatörü ile elde edilen referansın bir
referansa da atanmasına gerek yoktur. Örneğin:

if (fd.ShowDialog() == DialogResult.Yes)
{
new OleDbCommandBuilder(da);

da.Update(ds.Tables["Info"]);
}

Görüldüğü gibi otomatik komut üretme oldukça basittir. Fakat esnek


değildir. Otomatik üretilen komutlarda tüm sütun bilgileri garantilik
sağlamak amacıyla kullanılmaktadır. Ayrıca programcı SQL komutlarını çok
daha yetenekli olacak, başka şeylerde yapacak şekilde düzenleyebilir.
Örneğin; kayıt eklenirken başka bir veritabanına başka eklemeler
yapılabilir.

ELDE EDİLEN KAYITLARIN SUNUMU

272
DataGridView kontrolü, profesyonel uygulamalarda kullanıcılar için
yeterli bir arayüz sunmamaktadır. Kayıtlar görüntülenirken daha çok ileri
geri tuşları olan TextBox, CheckBox gibi kontrollerin kullanıldığı bir sunum
tercih eder. Örneğin; kullanıcı kişinin ismini ve numarasını bir TextBox’da
görüntüleyerek ileri geri işlemleri yapabilmelidir. Böyle bir uygulamayı
yapabilmek için manuel bir biçimde, kayıtların önce DataTable içerisine
yerleştirilip ileri geri tuşlarında bir aktif kayıt tutulabilir. Fakat bu işlemler
çok yorucudur. İşte bu işlemleri kolaylaştırmak için Microsoft veri bağlama
kavramını (data binding) geliştirmiştir. Veri bağlama sayesinde TextBox,
CheckBox gibi kontroller, veritabanı tablosunun çeşitli sütunları ile
ilişkilendirilebilir ve kayıt dolaşım işlemleri az bir çabayla
gerçekleştirilebilir.

MDI UYGULAMALARI

Eskiden MDI uygulamaları çok tercih ediliyordu. Microsoft’ta ilk


zamanlar kendi yazılımlarını MDI biçiminde oluşturuyordu. Fakat daha
sonra Microsoft MDI uygulamalarına yönelik cesareti kırmıştır. Kendisinde
Word ve Excel gibi uygulamaları klasik SDI olarak oluşturmuştur. Fakat
yine de önemli pek çok uygulama MDI arayüzü ile tasarlanmaktadır.
Özellikle programlama editörleri, IDE’ler bu arayüzü yoğun olarak
kullanmaktadır.

Bir MDI uygulamasında, uygulamanın bir ana penceresi vardır. Bu


ana pencereye frame penceresi denilmektedir. Frame penceresinin,
çalışma alanını kaplayan ve genellikle gri renkte olan penceresine client
penceresi denilmektedir. Yani client penceresi, frame penceresinin alt
penceresi durumundadır. Nihayet kullanıcının doğrudan etkileştiği birden
fazla yaratılabilen pencerelere doküman penceresi denilmektedir.
Doküman pencereleri, client pencerenin alt penceresi durumundadır.

.Net ortamında MDI penceresi oluşturmak çok kolaydır. Bir MDI


uygulaması kabaca şu adımlardan geçirilerek oluşturulur:

1- Programcı önce frame penceresini normal bir form olarak oluşturur.


Bu formu MDI yapmak için, Form sınıfının bool türden
IsMdiContainer property elemanına true girilmelidir. Bu property
true’ye çekildiği zaman, client penceresi otomatik olarak oluşturulur.

2- Doküman pencereleri de Form sınıfı ile oluşturulur. Fakat doküman


pencerelerini oluşturan programcı, Form sınıfının MdiParent
elemanına, frame penceresinin referansını atamalıdır. Böylece
yaratılan doküman penceresine ait frame penceresi belirlenmiş olur.

{02.03.2008 Pazar}

273
3- Yeni bir doküman penceresi yaratmak için, doküman penceresine
ilişkin Form sınıfı türünden new operatörü ile nesne yaratmak ve
Show fonksiyonunu uygulamak gerekir.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MDISample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void newToolStripMenuItem_Click(object


sender, EventArgs e)
{
Form2 fr = new Form2();
fr.MdiParent = this;

fr.Show();
}

private void exitToolStripMenuItem_Click(object


sender, EventArgs e)
{
this.Close();
}
}
}

MDI uygulamalarında genellikle bir Window menüsü bulunur ve bu


menü içerisinde, doküman pencerelerinin yerleşim düzenini sağlayan
elemanlar bulunur. Doküman pencerelerinin konumlandırılması işlemi için,
Form sınıfının LayoutMdi fonksiyonu kullanılır. LayoutMdi fonksiyonu
MdiLayout isimli enum türden bir parametre alır. Bu enum türünün
Cascade, TileHorizontal, TileVertical ve ArrangeIcons isimli
elemanları vardır. ArrangeIcons, doküman pencerelerini, MDI
pencerelerinin içerisine çeker. Fakat her Windows sisteminde geçerli
değildir.

274
Doküman pencerelerinden bir tanesi aktif durumdadır. Aktif
doküman penceresinin başlık kısmı default mavi yapılmıştır. Aktif
pencereler arasında dolaşmak için Ctrl+F6 ya da Ctrl+Tab tuşları
kullanılabilir. Fare ile herhangi bir pencerenin üzerine tıklanarak aktif hale
getirilebilir. Form sınıfının ActivateMdiChild isimli protected fonksiyonu,
parametre olarak bir doküman penceresi alır ve programlama yoluyla bu
doküman penceresini aktif hale getirir. Form sınıfının public
ActiveMdiChild isimli property elemanı read-only’dir ve o anda aktif olan
doküman penceresin vermektedir. Tabi bu property doküman penceresini
Form sınıfı biçiminde verir. Programcının aşağıya doğru dönüştürme
yapması gerekir.

Doküman pencereleri aynı Form sınıfından yaratıldığına göre


tamamen aynı davranışa sahiptir. Örneğin; Form sınıfı bir yazboz tahtası
uygulaması ise bütün doküman pencereleri yazboz tahtası şeklinde
davranacaktır.

Şüphesiz bir MDI uygulamasında, tek bir pencere tipi olmak zorunda
değildir. Örneğin; bir doküman penceresi fotoğrafları gösterirken diğeri
başka bir şeyleri edit edebilir. Bunların hepsi Frame penceresinde birer
form gibi saklanmaktadır.

Programcı MDI uygulamalarının menüsünü daha değişik bir biçimde


düzenlemek isteyebilir. Örneğin; tipik olarak bir doküman penceresi aktif
olduğunda, ana menüye o doküman penceresine özgü yeni bir popup
pencere ekleniyor olabilir ya da örneğin; hiçbir doküman penceresi
yaratılmamışsa Save gibi bir menü elemanı görüntülenmeyebilir ya da
pasif durumda görüntülenebilir.

MDI uygulamalarında, menü elemanlarının eklenmesi ve çıkarılması


optimize edilebilir. Bunun için menü birleştirme işlemleri yapılır. Menü
birleştirmesinde Frame penceresinin menüsü ile doküman penceresinin
menüsü, ToolStripItem sınıfının MergeAction ve MergeIndex propertyleri
kullanılarak birleştirilir. Menü birleştirme işleminin bazı ayrıntıları vardır.
Fakat özetle programcı doküman pencerelerine menü yerleştirdiğinde, bu
menüler ana menüyle birleştirilmektedir. Default durum sona ekleme
şeklindedir. Fakat çok değişik işlemler yapılabilir. Örneğin; doküman
penceresinin bir popup menüsünü Frame penceresinin ikinci menüsü
şeklinde insert etmek isteyelim. Bunun için doküman penceresindeki
popup menünün MergeAction elemanına Insert girilmeli, MergeIndex
elemanına 2 girilmelidir.

Frame penceresine ilişkin tüm doküman pencereleri Form sınıfının


MdiChildren propertysi ile elde edilebilir. Örneğin; programcı Close All
işleminde bu formların hepsine Close All uygulayıp kapatmak isteyebilir.

275
Frame penceresinde MDI uygulamaları için MdiChildActivate isimli
bir event eleman da vardır. Herhangi bir doküman penceresi aktif hale
geldiğinde, bu event tetiklenir. Programcı bu eventte bazı işlemleri manuel
yapmak isteyebilir.

DocumentView MİMARİSİ

DocumentView mimarisi, özellikle MFC uygulamalarında çok revaçta


bir kavramdı. Bu kavram .Net MDI uygulamalarında da kullanılabilir.
DocumentView mimarisine göre, doküman sınıfları bir Form sınıfıdır ve
yalnızca bir View kavramını içerir. Doküman kavramı ise bu View
pencereleri üzerinde oluşmuş bilgilerdir. Programcı View üzerinde oluşan
bilgileri doküman penceresinin form penceresinde doğrudan saklamamalı.
Ayrı bir data saklayan sınıf oluşturmalıdır. Yani görüntü işlemleri ile data
işlemleri birbirinden ayrılmalıdır. Bu işlemin önemli faydaları vardır.
Örneğin; yazboz tahtası uygulamasında oluşturulan pencere, yalnızca bir
çizim penceresidir. Çizilen bilgilerin dataları ayrı bir sınıfta saklanmalıdır.
DocumentView mimarisi uygulanmasa da yine işler yürütülebilir.

Anahtar Notlar:

File sınıfının static WriteAllText isimli fonksiyonu, dosyanın yol


ifadesini ve bir yazıyı alarak tek hamlede bunu text olarak dosyaya
yazar.

NESNELERİN SAPTANMASI VE GERİ ALINMASI(Serialization)

Bir sınıf nesnesinin tüm veri elemanları, bir dosyada saklanır ve


sonra oradan geri alınıp yeniden veri elemanlarına yerleştirilirse nesne
saklanıp geri alınmış olur. Yani bir sınıf nesnesinin belirli bir andaki
durumu, onun veri elemanlarındaki değerlerle belirlenir. Örneğin; bir
Person sınıfının içerisinde kişinin ismi, numarası gibi bilgiler olsun. Bizim
bu nesneyi saklayabilmemiz için tüm bu veri elemanlarını saklamamız
gerekir. Benzer biçimde bu nesneyi yeniden oluşturmak için, bizim bu
nesneyi yeniden yaratıp, bu değerleri sakladığımız ortamdan geri alıp, veri
elemanlarına yerleştirmemiz gerekir. Bu işlemlere genellikle İngilizce
“object Serializable” ya da “object persistanig” denir. Seri hale
getirme işlemi duyulduğu kadar basit bir işlem değildir.

Seri hale getirme işleminin zor olan tarafı, sınıfın başka sınıf
türünden veri elemanlarına sahip olması durumu ile ilgilidir. Örneğin; A
sınıfı B sınıfı türünden bir referansa sahipse, B sınıfı da C sınıfı türünden
bir referansa sahipse yalnızca A sınıfının veri elemanlarının saklanması
yetmez. Bütün bu referans zincirlerine ilişkin veri elemanlarının saklanması

276
gerekir. Geri alım sırasında benzer biçimde tüm bu elemanların okunması
ve bu nesnelerin tekrar yaratılması gerekir.

Seri hale getirme işlemi otomatik ya da manuel yapılabilir. Bir sınıf


ya da yapının seri hale getirilebilmesi içinö o sınıf ya da yapıya
Serializable öznitelik bilgisinin yerleştirilmesi gerekir. Örneğinş

[Serializable]
class Sample
{
//...
}

Bir sınıf ya da yapının seri hale getirilebilmesi için, onun bütün veri
elemanlarının ve taban sınıfının Serializable özniteliği ile
özniteliklendirilmesi gerekir.

Anahtar Notlar

Öznitelik sınıfları geleneksel olarak XXXAttribute biçiminde


isimlendirilmektedir. Fakat “ [] “ içerisine yazarken yalnızca XXX
yazılabilir ya da XXXAttribute yazılabilir. Yani biz [Serializable]
biçiminde de [SerializableAttribute] seklinde özniteliklendirilebilir.

.Net içerisindeki temel tüm sınıf ya da yapılar [Serializable] ile


özniteliklendirilmiştir. Örneğin object, int32, string gibi.

Bu durumda biz int, long, string gibi elemanlara sahip olan bir
sınıfı seri hale getirebiliriz.

Pek çok collection sınıf Serializable ile özniteliklendirilmiştir. Yani


biz bir ArrayList nesnesini tek hamlede diske yazabiliriz. Onu yeniden
diskten okuyarak bellekte oluşturabiliriz. Bu durumda ArrayList tuttuğu
tüm elemanları diske yazacaktır. Tabi ArrayList sınıfının tuttuğu
elemanlarında Serializable olması gerekir.

Seri hale getirme mekanizması kendi içerisinde döngüsel durumları


çözmektedir. .Net’in otomartik seri hale getirme mekanizması oldukça
yeteneklidir. Bu işlem sırasında döngüsel durumlar otomatik bir biçimde
çözülmekte, aynı nesneye referans etme durumunda aynı nesne gereksiz
bir biçimde fazla kez yazılmamaktadır. Öreğin; A sınıfının x ve y elemanları
aynı nesneyi gösteriyor olsun. Bu durumda seri hale getirme işlemi
sırasında bu nesnelerden sadece bir kopya diske yazılacaktır. A nesnesinin
B nesnesini gösteren bir veri elemanı, B nesnesinin C nesnesini gösteren
bir veri elemanı ve C nesnesinin de tekrar A nesnesini gösteren bir veri

277
elemanı varsa, bu döngüsellik fark edilmekte ve sonsuz döngü
oluşmamaktadır.

SERİ HALE GETİRME İŞLEMİNİ GERÇEKLEŞTİRİLMESİ

278

You might also like