Sürükle-bırak tekniği daha çok benzer nesneler arasında kopyalama veya taşıma işlemi yapılırken kullanılmaktadır. Bu konuda bilgi vermek için forma 2 ListBox yerleştirdim ve ilk ListBox'a bir basketbol takımının 10 oyuncusunun adını aktardım. Sürükle-bırak tekniği ile bu 2 ListBox arasında eleman taşımak istediğim için Properties penceresinde her 2 ListBox'ın AllowDrop özelliğini true yaptım. Bu özellik false iken söz konusu nesne sürükle-bırak işlemine izin vermez.

Tahmin edeceğiniz gibi benzer sürükle-bırak örneğine birçok kaynakta rastlamanız mümkündür. Benim bildiğim ilk Visual Basic sürümünden bu yana birçok yazar bu örneği kitaplarında kullanıyor. Tabii şimdi birileri çıkıp ListBox'lar arası eleman sürükleyip bırakma benim keşfim derse gülüp geçiniz. Siz gülüp geçin ama bütün çabama rağmen birisi prof unvanlı 3 bilgisayar mühendisine sürükle-bırak tekniği hakkında bilgi vermek için forma 2 ListBox yerleştirmeyi 2003 yılında ilk olarak yerli bir yarar tarafından akıl edildiği iddiasının gülünç olduğunu anlatamadım.
Sürükle-bırak işlemi ilk ListBox'ta istenen seçeneğin tıklanması ile başlayacağı için ilk ListBox'ın MouseDown olayını temsil eden metodu aşağıdaki gibi düzenledim. Bu metotta kullandığım "sira" değişkenini diğer metotlarda kullanmak istediğim için metotlarında dışında Class bloğu içinde tanımladım.
int sira;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
Point nokta = new Point(e.X, e.Y);
sira = listBox1.IndexFromPoint(nokta);
if (e.Button == MouseButtons.Left)
listBox1.DoDragDrop(listBox1.Items[sira].ToString(),
DragDropEffects.All);
}
Bu kod hakkında bilgi vermeden önce burada yapılan işlem hakkında 2003 yılı sonunda özel bir Türkçe programcılık kitabı yayınlanmadan önce dünyada kimsenin bilgisi yoktu(!) tespitini yapalım. Her ne kadar .NET'le ilgili bütün ayrıntılar 2001 yılında yayınlanmış olsa bile burada yapılan işlem yerli yazarlara aittir(!). Yani bu Türkçe C# kitabı yayınlanmadan önce bırakın Memik Yanık'ı bu konuda Türkiye'de kimsenin bir şeyler yazması ihtimal dışıdır.
Bu kodda yararlandığım IndexFromPoint() metodu ListBox'ın tıklanan elemanın sıra numarasını öğrenmemizi sağlıyor. Bu metot Point tipinde bir parametreye sahip olduğu için farenin o anki koordinatlarından yola çıkıp Point nesnesi hazırladım. Bu kodda tanımlanan değişkenlere ve nesnelere verilen adlar herkesin aklına gelebilme ihtimali karşısında bu kodu bir de aşağıdaki gibi düzenledim. Bu sayede kendimi değişken adı çalma suçlamasından belki bu kez kurtarırım. Tabii seçilen değişkenlerin adları güzel olmadığında yazılan kodun yavaş çalışması(!) sorununu hoş görmek gerek.
int sira_sira;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
Point kor_nokta = new Point(e.X, e.Y);
sira_sira = listBox1.IndexFromPoint(kor_nokta);
if (e.Button == MouseButtons.Left)
listBox1.DoDragDrop(listBox1.Items[sira_sira].ToString(),
DragDropEffects.All);
}
Bu örnekte kullandığım ilk ListBox elemanlarla dolu olduğu için eleman olmayan bir yerini tıklama imkânı yoktur. Her ihtimale karşılık kullanıcının kaynak ListBox'ın eleman olmayan bir yerini tıklayıp tıklamadığını aşağıdaki gibi kontrol edebilirsiniz. ListBox sınıfının MouseDown olayını temsil eden metodun "e" adındaki 2. parametresinin MouseEventArgs tipinde olduğuna dikkatinizi çekmek istiyorum. Her ne kadar MouseEventArgs tipindeki bu parametreye "e" adının verilmesi Visual Studio'nun, dolayısıyla Microsoft'un tercihi olsa bile sizler değişken adı çalmış olmamak için "e" yerine bir hayvan adını parametre adı olarak kullanabilirsiniz.
int sira_sira;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
Point kor_nokta = new Point(e.X, e.Y);
sira_sira = listBox1.IndexFromPoint(kor_nokta);
if (sira_sira == -1)
{
MessageBox.Show("Eleman olmayan bir yeri tıkladınız");
return;
}
if (e.Button == MouseButtons.Left)
listBox1.DoDragDrop(listBox1.Items[sira_sira].ToString(), DragDropEffects.All);
}
Bu metotta ilk olarak Point tipinde bir değişken tanımlanarak ListBox1'in tıklanan yeri öğrenilmektedir. Ardından Point tipindeki "kor_nokta" değişkeninden yararlanıp üzeri tıklanan elemanın sıra numarası öğrenilip "sira_sira" değişkenine aktarılmaktadır.
Bu kodda en son olarak DoDragDrop() metodu ile ListBox'ın söz konusu elemanı alınmakta, başka bir deyişle sürükle-bırak işlemi başlatılmaktadır. Sürükleme pozisyonundaki fare işareti 2. ListBox'ın üzerinde götürüldüğü zaman meydana gelecek DragOver olayını temsil eden metodu aşağıdaki gibi düzenledim.
Başka bir deyişle aşağıda verdiğim kod kullanıcı istediği elemanı sürükleme pozisyonunda ilk ListBox'tan alıp 2. ListBox'ın üzerine götürdüğü zaman işletilecektir. Bu metotla ilgili olarak dikkatinize sunmak istediğim önemli bir nokta var: ListBox sınıfının DragOver olayını temsil eden metodunun 2. DragEventArgs tipindeki parametresidir.
private void listBox2_DragOver(object sender, DragEventArgs e)
{
if (e.KeyState == 1)
e.Effect = DragDropEffects.All;
}
DragOver olayı meydana geldiği zaman farenin sol tuşu basılı durumda ise DragEventArgs tipindeki "e" parametresinin KeyState özelliği 1 değerini içermektedir. Farenin sol yerine sağ tuşu basılı durumda olsaydı KeyState özelliği bu kez 2 değerini içerirdi. Fare ile ilk ListBox'tan 2. ListBox'a eleman taşırken kullanıcı sol tuş ile birlikte ayrıca Shift tuşunu basılı durumda tutuyorsa KeyState özelliği 1+4 yani 5 olur.
Benzer şekilde Ctrl tuşu basılı iken sürükle-bırak işleminin yapılmasını istiyorsanız ListBox2'nin DragOver olayını temsil eden metotta DragEventArgs tipindeki "e" parametresinin KeyState özelliğinin 1+8=9 olup olmadığını kontrol etmelisiniz.
private void listBox2_DragOver(object sender, DragEventArgs e)
{
if (e.KeyState == 9)
e.Effect = DragDropEffects.All;
}
Bu kod başka türlü yazılamayacağı için değişken, metot ve nesne adı çalma suçlamasıyla karşılaşmamak için tek çare Visual Studio'nun seçtiği metot ve parametre adını bağlı kalmamaktır. Bu nedenle Visual Studio'nun her form için hazırladığı ikinci kod dosyasında ayarlama yapmak şartıyla bu metodun adını aşağıdaki düzenleyebilirsiniz. Böylece bu iki karşılaştıran öğretim üyesinin hışmından kendimizi kurtarmış oluyoruz.
private void fenerbahceyi_surukle(object sender, DragEventArgs e)
{
if (e.KeyState == 9)
e.Effect = DragDropEffects.All;
}
Tabi sizler bu değişikliği biraz daha ileriye bu götürüp metodun parametrelerine aşağıda yapıldığı gibi farklı adlar verebilirsiniz. Böylece parametre hırsızı olma olasılığı biraz daha azalmış olur.
private void fenerbahceyi_surukle(object recep_ivedik, DragEventArgs cem_yilmaz)
{
if (cem_yilmaz.KeyState == 9)
cem_yilmaz.Effect = DragDropEffects.All;
}
Farenin sol tuşu ile birlikte hem Ctrl hem de Alt tuşu basılı iken sürükleme yapılmasına izin vermek istiyorsanız KeyState özelliğinin 1+8+32=41 olup olmadığını kontrol etmelisiniz. Geriye bir tek kullanıcı farenin sol tuşunu ListBox2'nin üzerinde iken serbest bıraktığında işletilecek metotta söz konusu elemanı 2. ListBox'a dahil etmek kaldı.
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
listBox2.Items.Add(listBox1.Items[sira_sira]);
}
2. ListBox'a aldığınız elemanı 1. ListBox'tan silmek istiyorsanız bu metoda 2. bir satır yazıp Remove() metodu ile bu elemanı ilk ListBox'tan silmelisiniz. Her ne kadar aşağıda verilen kod sayesinde ListBox'lar arası eleman taşıma işlemi yapılsa bile bu gerçek bir sürükle bırakma işlemi değildir.
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
listBox2.Items.Add(listBox1.Items[sira_sira]);
listBox1.Items.Remove(listBox1.Items[sira_sira])
}
Formdaki ilk ListBox'ın elemanlarını sürükleyip 2. ListBox'ın üzerine bırakma işlemini özetlemek gerekirse; öncelikle ListBox'ların AllowDrop özelliklerini true yaptık. Ardından İlk ListBox'la ilgili olarak MouseDown olayı meydana geldiğinde yani kullanıcı ilk ListBox'ı tıkladığında seçtiği elemanın ListBox'ın kaçıncı elemanı olduğunu öğrenip daha önce tanımladığım "sira_sira" adlı int tipindeki değişkene aktardım.
İlk ListBox'ın sürüklenip 2. ListBox'a aktarılacak elemanın hangisi olduğunu başka metotlarda iken bilmem gerekeceği için bu değişkenin Class'ın her yerinde yaşamasını sağladım. Yukarıda yapılan bütün bu hazırlıklara rağmen fare işareti henüz ilk ListBox'ın üzerinde iken taşınma işleminin yapıldığı işaret edilmez. Bu sorunu gidermek için ilk ListBox'a ait DragEnter olayını temsil eden metodu aşağıdaki gibi düzenledim.
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
if (e.KeyState == 1)
e.Effect = DragDropEffects.Move;
}
Şimdi aynı örneği başka bir teknikle yapacağım. Bu tekniğin farklılığı sürükle-bırak işlemi sırasında panodan yararlanıyor olmamdır. Doğru olanı da sürükle-bırak işlemini yaparken panodan yararlanmaktır. Çünkü sürükleme işlemi boyunca sürüklenen nesne geçici olarak panoya alınmaktadır.
Başka bir anlatımla ilk ListBox'a seçilip sürüklenen eleman Windows tarafından otomatik olarak panoya alındığı için ilk ListBox'ın MouseDown olayını temsil eden metotta yukarıda "sira" adını verdiğim değişkeni tanımlamaya gerek yoktur.
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
Point kor_nokta = new Point(e.X, e.Y);
int sira_sira = listBox1.IndexFromPoint(kor_nokta);
if (sira_sira == -1)
{
MessageBox.Show("Eleman olmayan yeri tıkladınız");
return;
}
if (e.Button == MouseButtons.Left)
listBox1.DoDragDrop(listBox1.Items[sira_sira].ToString(), DragDropEffects.All);
}
2. ListBox'ın DrogOver olayını temsil eden metotta bir değişiklik yapmayacağım için direk DragDrop olayını temsil eden metoda geçtim. MouseDown olayı ile sürükle-bırak işlemi başlatıldığında sürüklenen nesnenin(burada ListBox'ın ilgili elemanı) bir kopyası panoya alındığı için panodan okuma yapıp 2. ListBox'a yeni eleman olarak eklemek gerekiyor. Bu işlemin nasıl yapıldığını aşağıda görebilirsiniz.
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
string hos_eleman=Convert.ToString(e.Data.GetData(DataFormats.Text));
listBox2.Items.Add(hos_eleman);
listBox1.Items.Remove(hos_eleman);
}
Hedef nesne durumundaki ListBox2'nin DragDrop olayını temsil eden metodun DragEventArgs tipindeki "e" parametresinin Data özelliği panoyu temsil etmektedir. Panodan okuma yapılırken GetData() metodunu kullanmak gerekmektedir. Bu metot panodan okunacak bilginin tipini parametre olarak almaktadır. Panodan okuyacağım bilgi text tipinde olduğu için parametre olarak DataFormats.Text sabitini kullandım. Lütfen dikkat: .NET'i internet kelimesinin üç harfi sanan Ankaralı öğretim üyesinin birisi sizi "e" adlı parametrenin adını çalmakla suçlayabilir.