Uefa Maçları Canlı İzle
RSS
 

Sanal Hesap Makinesi

15 Tem

Uygulamanın Planlanması

calculator-256x256

Bu dersimizde rakam ve işlem tuşlarıyla bir de ekranı bulunan hesap makinesi şeklinde bir form yaratacağız. Üstelik bu formu tasarım görünümünde kontrolleri elle yerleştirerek değil, kod yazarak oluşturacağız. Visual Studio’nun varsaydığı parametrelerle yarattığı standart formu yalnızca kod içindeki komutlar aracılığıyla şekillendireceğiz.

Uygulamanın Planlanması

Daha önceki derslerde geliştirdiğimiz uygulamalara göre daha kapsamlı olacak olan bu uygulama dikkatli bir planlama gerektirecektir. Öncelikle sanal hesap makinemizin nasıl çalışacağına karar vermeliyiz.

Hesap makinemizin (0’dan 9’a) 10 rakam tuşu, 4 işlem tuşu (+, −, * ve /) ve bir eşittir tuşu olsun istiyoruz; demek ki bu hesap makinesini temsil eden form üzerinde bu tuşları temsil eden toplam 15 düğme olacak. Bu tuşların gerçek hesap makinelerinde olduğu gibi gruplanmasını istiyoruz; yani formumuzda rakam tuşlarını temsil eden düğmeler bir grup, işlem ve eşittir tuşlarını temsil eden düğmeler ayrı bir grup oluşturmalı. Bu iki ayrı düğme grubu iki ayrı Panel kontrolü içine yerleştirmek en mantıklı çözümmüş gibi gözüküyor.

Hesap makinesi ekranı tabi ki bir TextBox kontrolüyle temsil edilmeli. Yalnız sanal hesap makinemiz eyalnızca tuşlara basarak bilgi girişine izin vermeli; bu nedenle ekranı temsil eden TextBox kontrolünü bir şekilde kilitlemeli ve yalnızca sonuç göstermek için kullanmalıyız.

Ekranı temsil edecek TextBox kontrolü ile düğme gruplarını barındıracak Panel kontrollere formun iç alanını hep eşit oranda paylaştırarak uygulamamızın güzel görünmesini sağlayabiliriz.

Sanal hesap makinemiz basit olsun diye yalnızca tamsayılarla işlem yapacak, her hesap için en fazla iki sayı girişine izin verecektir. Rakam tuşlarına basıldığı sürece o rakamları o anda girilen sayıya ekleyecek, bir işlem tuşuna basılması halinde birinci sayının girilmesinin tamamlanmış olduğunu varsayacak, sonra girilen rakamları ikinci sayının rakamları olarak kabul edecek, eşittire basılınca da sonucu ekrana yazacaktır.

Yapılan işlemlerin ekranda aynı elle kağıda yazılıyormuş gibi yazılmasını istiyoruz: 54 + 21 = 75 gibi. Bir hesabın sonucu gösterildikten sonra yeniden bir rakam tuluna basılması halinde ekran yenilenecek ve yeni bir hesaba geçilecektir.

Şekil 31  Sanal hesap makinesinin çalışan hali

Değişken Tanımları

Formu tasarım görünümünde kontrolleri elle yerleştirerek oluştursaydık, Visual Studio kullandığımız kontrollerin her biri için bir değişken yaratır ve kontrol için belirlediğimiz özellikleri (renk, büyüklük, vb.) kod içinde o değişkene verirdi. Bütün bunlar formun kod dosyasında, yaratıcı fonksiyon içinde çağrısını gördüğümüz (ama kendisini görmediğimiz) InitializeComponent() fonksiyonu içinde yapılırdı. Bu fonksiyon Visual Studio’nun bizim için yarattığı, ama bize güvenmeyip bizden sakladığı .Designer.cs kod dosyasında bulunabilir. Ama bu kez formun görünümünü düzenlemeyi Visual Studio’ya bırakmayıp kendimiz yapacağımıza göre, kontrollere ait değişkenleri kendimiz tanımlayıp kendimiz yaratmalıyız. Peki değişken tanımlarını nereye koymalıyız?

C ve C++’da değişken tanımları için doğru olan kurallar C# için de doğrudur: Her değişken içinde tanımlandığı kod bloku içinde mevcuttur. O kod bloku bitince ortadan kaybolur. Örneğin, herhangi bir fonksiyon içinde tanımlanıp yaratılmış her değişken o fonksiyon biter bitmez silinip gider. Evet, gerçek anlamda hafızadan silinmesi için hafıza çöpçüsünün (garbage collection) gelip ortalığı toparlaması gerekir, ama fonksiyonun çalışması sona erdikten sonra o fonksiyon içinde tanımlanıp kullanılmış değişkenlere erişmek artık mümkün değildir.

Halbuki bir form üzerinde yer alacak kontroller form mevcut olduğu sürece mevcut olmalıdırlar. Demek ki onları forma ait herhangi bir alt fonksiyonda tanımlayamayız. Değişken tanımları formun kod dosyası içinde, ama fonksiyonların dışında yer almalıdır. Böylece onlar form dosyası içinde birer “global” değişken olacaklardır. Form kod dosyasındaki her fonksiyon onları kullanabilecektir. Aşağıda ismini AnaForm olarak değiştirdiğimiz formumuzun kod dosyasına koyduğumuz kontrol değişkenleri tanımlarını görüyouz.

public partial class Ders5_AnaForm : Form

{
public Ders5_AnaForm()
{
InitializeComponent();
}

#region Üye değişkenler
Button[] m_buttonRakamlar;
Button[] m_buttonIslemler;
Button m_buttonEsittir;
TextBox m_txbEkran;
Panel m_panelRakamlar;
Panel m_panelIslemler;
const int m_padding = 5;
#endregion
}
Forma ait kontroller (rakam ve işlem düğmelerini temsil eden düğmelerle ekranı temsil eden TextBox) forma ait üye değişkenlerdir. O yüzden kod dosyasında “Üye değişkenler” diye adlandırılan bir alt kısıma yerleştirilmişlerdir. C#’da yazılmış kod dosyalarında #region terimi ile başlayıp bir #endregion terimi ile biten alt kısımlar bir fonksiyon veya bir döngü, bir kontrol ifadesi vb. değildirler. Yalnızca dosyadaki kodun düzenlenmesini kolaylaştırırlar. Kod dosyasını bir bilgi dolabı gibi düşünürsek, #region’lar da dolabın çekmecelerini temsil ederler. Yukarıdaki değişken tanımlarını formun kod dosyasına yazarsanız, kod editörünün #region ve #endregion terimlerini soldan bir dikey çizgiyle birleştirdiğini, ve istenirse çizginin başındaki + veya – işaretlerinin tıklanmasıyla bu kısmı gösterdiğini ve gizlediğini göreceksiniz (tıpkı bir dolabın çekmecelerinin açılıp içindekilerin görülmesi, veya kapanıp içindekilerin gizlenmesi gibi). Visual Studio kod editörünün bu kod gizleme (code collapsing) yeteneği aynı fonksiyonun kod bloku içinde de yer alan çok sayıda komutun birbirleriyle ilgili alt gruplara ayrılıp gerekmedikçe göze gözükmemelerini sağlar, yani kalabalığı azaltır.

Bu arada C# dilinde dizilerin C veya C++’dakinden farklı şekilde tanımlanıyor olması dikkatinizi çekmiş olabilir. Örneğin, rakam tuşlarını temsil edecek olan düğmeler dizisi C’de olabileceği gibi
Button m_buttonRakamlar[10];
şeklinde değil,
Button[] m_buttonRakamlar;
şeklinde tanımlanmıştır.Tanımlanan değişkenin bir dizi olduğu gerçeği değişken adından önce belli ediliyor yani. Bu dizi tanımlama şeklinin bir başka farklı yanı da dizi boyutunun (eleman sayısının) verilmemiş olmasıdır. Buna bakarak aslında bu dizinin bu tanımla yaratılmadığını düşünmüşseniz, haklısınız. Bir sonraki kısımda bunu açıklıyoruz.

Değişkenlerin Yaratılması

C# dilinde, basit sayısal değişkenler (char, int, double, decimal türü değişkenler) hariç hiç bir değişken tanımlandığında yaratılmaz. Bir değişken tanımı yalnızca değişkenin hangi türden olduğunu belirtir ve değişken yaratıldığında onun hafızada nerede olduğunu bilecek bir referans değişkeni yaratır. Bu açıdan bakacak olursak, bir C# değişken tanımının bir C++ adres değişkeni (pointer) tanımından farklı olmadığını söyleyebiliriz. C++’da da bir adres değişkeni tanımladığımızda, o değişkene mevcut bir değişkenin adresi atanana kadar, veya hafızada yeni bir yer açılıp adresi bu adres değişkenine aktarılana kadar, adres değişkeninin bir anlamı yoktur. C#’da da durum böyledir; tanımı yapılan değişken (basit bir int ya da double değilse) ancak new terimiyle hafızada ona uygun bir yer yaratıldığında yaratıldığında hayat bulur, veya mevcut bir değişkene referans yapmak için kullanılabilir.

Hesap makinesi formumuzda gözükecek olan düğmeler ve diğer kontrollerin form mevcut olduğu sürece mevcut olmaları gerektiğine göre, form yaratılınca yaratılmaları gerekir. Bu yüzden onları yaratacak komutları formun yaratıcı (constructor) fonksiyonuna koymalıyız:

m_txbEkran = new TextBox();

m_panelRakamlar = new Panel();

m_buttonRakamlar = new Button[10];

m_panelIslemler = new Panel();

m_buttonIslemler = new Button[4];

İlk komut ekranı temsil edecek TextBox kontrolünü, ikincisi rakam tuşlarını temsil edecek düğmeleri barındıracak olan Panel kontrolünü, üçüncüsü rakam tuşlarını temsil edecek düğmeler dizisini, … yaratıyor. İsteyen okuyucularımız hemen bu tanımları yaratıcı fonksiyon içine sokup programı çalıştırabilir; hiç yeni bir şey görmeyeceklerdir form üzerinde. Bu kontrollerin boyutları ve görünüşleri ile ilgili hiç bir düzenleme yapmamış olmamız bir yana, daha onları tam yaratmadık bile! Düğme dizilerini yaratmış olmak düğmeleri yaratmış olmak değildir. Button türü 10 elemanlı bir dizi yaratmışsak Button türü 10 referans değişkeni yaratmışız demektir. O referans değişkenlerinin referans yapacağı Button türü değişkenleri yaratmak için ayrı ayrı komutlar yazmak durumundayız. Aşağıdaki döngülerin eklenmesiyle rakam ve işlem tuşlarını temsil edecek düğmeleri gerçek anlamda yaratmış oluruz:

for (i = 0; i < 10; i++)
{
m_buttonRakamlar[i] = new Button();
}

for (i = 0; i < 4; i++)
{
m_buttonIslemler[i] = new Button();
}

Tabi bu döngülerin çalışabilmesi için döngü sayaç değişkeni i’nin int türü bir değişken olarak döngünün yer aldığı fonksiyonda tanımlanmış olması gerekir.

Sabırsız okuyucularımız bu eklemeyi de yapıp programı çalıştırsınlar. Yine hiç bir şey gözükmeyecektir form üzerinde. Sebep şu ki, yaratılan kontroller formun kullanımna sunulmamışlardır. Kime ait oldukları belli değildir. Kontrolleri temsil eden değişkenlerin yaratılmaları onları form üzerine yerleştirmez. Form üzerinde gözükmeleri için yaratılan kontroller onları barındıracak olan formlara veya panellere tanıtılmalıdırlar. Örneğin, yarattığımız iki panel formun kontroller listesine aşağıdaki gibi eklenmelidirler:

this.Controls.Add(m_panelRakamlar);

this.Controls.Add(m_panelIslemler);

Tuşları temsil edecek düğmeler ise kendilerini barındıracak olan panellerin kontrol listelerine sokulmalıdır:

m_panelRakamlar.Controls.AddRange(m_buttonRakamlar);

m_panelIslemler.Controls.AddRange(m_buttonIslemler);

Dikkat edilirse, tek bir kontrol eklerken Add() üye fonksiyonunu kullanıyor ve kontrolü temsil eden değişkenin adını veriyoruz; bir kontrol dizisi eklerken AddRange() üye fonksiyonunu kullanıyoruz ve kontrol dizisinin adını veriyoruz.

Form Kod Dosyasının Düzenlenmesi

Formun görünümünü ayarlamak için arka plan rengi, boyutları vb. bir takım komutlarla ayarlanabilir. Bu komutlar yaratıcı fonksiyon içine, InitializeComponent() çağrısından sonraki satırlara konabilir. Ama yaratıcı fonksiyonu fazla kalabalıklaştırmamak için FormBaslangic() diye bir fonksiyon yazıp form özelliklerini değiştiren komutları orada toplayabiliriz:

void FormBaslangic()
{
// Formun fazla küçülmemesi için
this.MinimumSize = new Size(300, 300);
// Arka plan rengını ayarla
this.BackColor = Color.Beige;
// Yazı boyutu form boyunu etkilemesin diye
this.AutoScaleMode = AutoScaleMode.None;
// Yazı türü ve büyüklüğünü belirle
this.Font = new Font(“Times New Roman”, 14);
// Ana form başlığındaki yazıyı değiştir
this.Text = “Ders5 Ana Form”;
}

Tabi bu fonksiyondaki komutların işleme konabilmesi için bu fonksiyonun yaratıcı fonksiyon içinde (evet, InitializeComponent()’den sonra) çağrılması gerekir:

public Ders5_AnaForm()
{
InitializeComponent();
FormBaslangic();
DugmeleriYarat();
DugmeleriDuzenle();
}
Panel kontrolleri ve onların içlerindeki düğmeleri yaratacak komutları da yukarıda adı geçen DugmeleriYarat() fonksiyonu içine koyabiliriz. Tabi kontrolleri yaratmak yetmez; onların boyutlarını da belirlememiz gereklidir. Bunu yapacak komutları da DugmeleriDuzenle() dediğimiz ayrı bir fonksiyona koyabiliriz.

Bu fonksiyonların gerekli olduğu veya bu şekilde adlandırılmaları gerektiği sanılmasın. Önemli olan form üzerinde gözükecek kontrolleri gözükmeleri gereken zamandan önce işleme konacak komutlarla tanımlayıp yaratmak ve düzenlemektir. Form mevcut olduğunca mevcut olması gereken kontroller için bu komutların yaratıcı fonksiyon içine konması en mantıklı yoldur; biz yalnızca komut kalabalığı olmasın diye birbirleriyle ilgili komutları kendi yarattığımız ve kendi mantığımıza göre adlandırdığımız fonksiyonlarda topluyoruz.

Komut gruplarını ayrı fonksiyonlara yazmanın program organizasyonunu kolay anlaşılır kılmaktan başka yararları da vardır. Örneğin, form üzerindeki düğmeleri ve diğer kontrolleri yerleştirecek komutlar ayrı bir fonksiyona konmuşsa, form boyutlarının değişmesi halinde bu fonksiyon çağrılarak formun yeniden düzenlenmesini sağlanabilir.

Form Görünümünün Düzenlenmesi

Ekranı temsil edecek olan TextBox kontrolünü formun üst kısmında tutmak için kolay yolu seçip onu Dock özelliğini kullanabiliriz:

m_txbEkran.Dock = DockStyle.Top;

Dock özelliği belli sayısal değerlere karşılık gelen sınırlı sayıda seçenek arasından seçilmelidir; bu yüzden enum türü bir değişkendir. Bu enum türünün adı da DockStyle’dır. DockStyle enum’unun bize sunduğu seçenekleri sayısal değerler olarak bilmemiz gerekmez. Kod yazarken DockStyle teriminden sonra koyacağımız nokta operatöründen sonra çıkacak isim listesinden doğru olanı seçebiliriz.

Panel kontrolleri yerleştirirken TextBox kontrolün boyutlarını kullanırız. Şöyle ki, panellerin her ikisi de m_txbEkran adını verdiğimiz kontrolün hemen altında yer alacaktır. Dolayısıyla iki panelin de üst konumu (Top üye değişkenlerinin değerleri) m_txbEkran’ın alt konumu (Bottom üye değişkeninin değeri) ile aynı olmalıdır:

m_panelRakamlar.Top = m_panelIslemler.Top = m_txbEkran.Bottom;

Tabi eğer paneller TextBox’a yapışık gözükmesin istiyorsak araya bir miktar boşluk koymak için bir kaç piksel ekleyebiliriz sağdaki toplama. Bıraktığımız boşluklar her komutta aynı olsun diye bu boşluk genişliğini temsil edecek sabit değerli bir değişken tanımlamak daha akla yakın olacaktır. Şöyle bir tanım değişken tanımları arasına sokularak bu iş halledilebilir:

const int m_padding = 5;

Bu değişken tanımındaki const terimi değişkenin değerinin hiç değişmeyeceğini belli ediyor. Bu şekilde tanımlanmış bir değişken hemen tanım olur olmaz yaratılacaktır ve ilk değerini de bu tanım içinde almak zorundadır; daha sonra bu değişkenin değerini değiştirecek bir komut olamaz:

m_padding = 10; // Yanlış! Derleme hatası verir!

Ekranı temsil eden TextBox kontrolünün genişliği Dock özelliği sayesinde otomatik olarak formun iç genişliği ile aynı olacaktır. Kenarlarda boşluk kalması için formun Padding özelliği şu şekilde değiştirilebilir:

this.Padding = new Padding(m_padding);

Tabi bu komut m_txbEkran’ın Dock özelliği değiştirilmeden önce işleme konmalıdır. Ekran bir TextBox ile temsil edildiğine göre yüksekliğini ayarlamak gerekmez; TextBox’un yüksekliğini belirleyen parametre yazı boyutudur (En azından o tür ayarlarla oynamadığımız sürece) Panellerin yükseklikleri de formun iç alanından hesaplanabilir. Ekran yüksekliğinden geri kalan kısmı paneller dolduracaktır:

Rectangle clrect = this.ClientRectangle;
// Önce panel genişlik ve yüksekliklerini ayarla
m_panelRakamlar.Height =
clrect.Height – m_txbEkran.Height – 3 * m_padding;
m_panelIslemler.Height = 3 * m_panelRakamlar.Height / 4;
Rakamlar paneli geri kalan form yüksekliğini tamamen alıyor, ama işlem düğmeleri paneli rakam paneli gibi dört sıra değil, yalnızca üç sıra düğme içerdiği için onun yüksekliği diğerininkinin dörtte üçü olarak hesaplanıyor.

Düğmelerin boyut ve konumları da içinde bulundukları panellerin boyutlarına göre hesaplanabilir. Örneğin, rakamlar paneli içinde 1, 2, ve 3 rakamlarını taşıyacak düğmeler üst sırada yer aldıkları için onların üst konumları (rakam panelinin iç koordinat sisteminde) 0 olacaktır. Düğmeler paneli tam doldursun diye de her birinin genişliği panelin iç genişliğinin üçte biri olmalıdır. Dört sıra düğme olduğuna göre her birinin yüksekliği panelin iç yüksekliğinin dörtte biri olacaktır. Sağ ve alt sıralardaki düğmelerin konumları işte bu düğme genişliği ve düğme yüksekliği değerlerinden hesaplanabilir. Bu düzenleme işini yapan fonksiyonun kodu aşağıdaki gibi olacaktır:

void DugmeleriDuzenle()
{
int i, j, butonno, butongenislik, butonyukseklik;
Rectangle clrect = this.ClientRectangle;
// Önce panel genişlik ve yüksekliklerini ayarla
m_panelRakamlar.Height =
clrect.Height – m_txbEkran.Height – 3 * m_padding;
m_panelIslemler.Height = 3 * m_panelRakamlar.Height / 4;
m_panelRakamlar.Width = 3 * (clrect.Width – 3 * m_padding) / 5;
m_panelIslemler.Width = 2 * (clrect.Width – 3 * m_padding) / 5;
// Şimdi onları yerleştir
m_panelRakamlar.Left = m_padding;
m_panelIslemler.Left = 2*m_padding + m_panelRakamlar.Width;
m_panelRakamlar.Top = m_panelIslemler.Top =
m_txbEkran.Bottom + m_padding;

// Rakam düğmelerini düzenle
clrect = m_panelRakamlar.ClientRectangle;
butongenislik = clrect.Width / 3;
butonyukseklik = clrect.Height / 4;
// 1′den 9′a kadar olan rakamlar 3×3′lük bir blok oluşturacaklar
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
butonno = 3 * i + j + 1;
m_buttonRakamlar[butonno].Width = butongenislik;
m_buttonRakamlar[butonno].Height = butonyukseklik;
m_buttonRakamlar[butonno].Left = j * butongenislik;
m_buttonRakamlar[butonno].Top = i * butonyukseklik;
}
}
// 0 düğmesi panelin alt sırasında
m_buttonRakamlar[0].Width = butongenislik;
m_buttonRakamlar[0].Height = butonyukseklik;
m_buttonRakamlar[0].Left = 0;
m_buttonRakamlar[0].Top = 3 * butonyukseklik;

// İşlem düğmelerini düzenle
for(i=0; i<4; i++)
{
m_buttonIslemler[i].Width = butongenislik;
m_buttonIslemler[i].Height = butonyukseklik;
}

m_buttonIslemler[0].Top = m_buttonIslemler[1].Top = 0;
m_buttonIslemler[2].Top = m_buttonIslemler[3].Top = butonyukseklik;
m_buttonIslemler[0].Left = m_buttonIslemler[2].Left = 0;
m_buttonIslemler[1].Left = m_buttonIslemler[3].Left = butongenislik;

m_buttonEsittir.Left = butongenislik;
m_buttonEsittir.Top = 2 * butonyukseklik;
m_buttonEsittir.Width = butongenislik;
m_buttonEsittir.Height = butonyukseklik;
}

Mesaj Yanıtlayıcı Fonksiyonlar

Varsayalım ki sabırlı okuyucularımız insanüstü bir gayret gösterip bunca kodu yazdılar. Şimdi programı çalıştırıp en azından formun istedikleri gibi gözüküp gözükmediğine bakmak isteyeceklerdir. Bundan önce bir de form boyutları değiştiğinde yukarıdaki DugmeleriDuzenle() fonksiyonunu çağırarak panellerin ve düğmelerin yeni boyutlara göre düzenlenmesini sağlayacak bir mesaj yanıtlayıcı bir fonksiyon yaratalım. Bu mesaj fonksiyonu forma ait SizeChanged mesajını yanıtlamalıdır. Mesaj yanıtlayıcı bir fonksiyon yaratmak biraz dikkat ister; rasgele her fonksiyon her istediğimiz mesajı yanıtlayamaz. Fonksiyon adı konusunda (gelenekler hariç) bir sınırlama yoksa da, mesaj yanıtlayıcı bir fonksiyon belli türden paremetreler gerektirecektir. Hangi parametrelerin gerektirdiğini bilmek için daha önceki derslerde Visual Studio’nun bizim için yaratmış olduğu fonksiyonlara bakmak yararlı olacaktır. Sonuçta şöyle bir fonksiyon işi görecektir:

private void OnFormSizeChanged(object sender, EventArgs e)
{
DugmeleriDuzenle();
}

Baştaki private terimi bu fonksiyon tanımını dışarıdan gizler, ama onu koymasak da olurdu; public mi private mi olacağı belirtilmemiş fonksiyon veya değişkenlerin private olduğu varsayılacaktır zaten. Önemli olan fonksiyonun türünün void olması –ki busonuç göndermeyen bir fonksiyon olması demektir, object türü bir sender parametresi (mesajı gönderen nesneye at bir referans değişkenidir bu) ve mesajla ilgili ayrıntıları içeren, EventArgs türü bir parametre olmasıdır.

Şimdi deneme yapmadan önce bir küçük iş daha kaldı: Forma SizeChanged mesajı yayınladığında bu fonksiyonu çağırmasını söylemeliyiz. Bunu formun özelliklerini tanımlayan FormBaslangic() fonksiyonu içinde, belki son satırda yapabiliriz. Ama yapılış tarzı biraz tuhaf gelebilir:

this.SizeChanged += new EventHandler(this.OnFormSizeChanged);

Formun yayınlayacağı mesaj adı forma ait bir üye değişken adı gibi gözüküyor burada. Sanki bu mesaj bir değişkenmiş de biz onun değerine bir sayı ekliyormuşuz gibi bir komut yazdık. Aslında yaptığımız şey biraz önce elle yazmış olduğumuz fonksiyonu bir mesaj yanıtlayıcı fonksiyona (event handler) dönüştürdük, ve formun SizeChanged mesajını yanıtlayan fonksiyonlar listesine yarattığımız bu mesaj yanıtlayıcı fonksiyonu ekledik. Tabi şu anda adı geçen mesajı yanıtlayan tek bir fonksiyon var, ama istedikçe başka fonksiyonlar da yaratabiliriz aynı mesajı yanıtlamak için. Bazı durumlarda birden fazla fonksiyonun aynı mesajı yanıtlaması gerekebilir; biz o tür durumlarla ilgilenmiyoruz.

Bu aşamada sanal hesap makinemiz hesap yapmıyorsa da hesap makinesi görünümüne sahiptir. İlk denemeler için hazırdır. Okuyucularımız programı çalıştırıp formun şeklinin değişmesi halinde düğmelerin ve panellerin de forma göre boyut ve yer değiştirdiğinden emin olabilirler.

Hesap Yapma Yeteneği Eklenmesi

Sanal hesap makinemizi yalnızca iki argümanı olan aritmetik işlemler yapacak şekilde tasarlamak istiyoruz. Form ilk açıldığında herhangi bir rakam düğmesine basıldığında ilk argümanın girildiğini varsayacağız. Bir işlem düğmesine basıldığında hangi işlemin yapılacağı belirtilmiş olacak, bundan sonra girilecek rakamlar ikinci argümanın rakamları olarak kabul edilecektir. Eşittir düğmesine basıldığında ise istenen hesap yapılmalı ve sonuç da ekrandaki yazıya eklenmelidir. Bundan sonra bir rakam düğmesine basılması halinde yeni bir hesap başlayacağı varsayımıyla ekran temizlenmeli ve yeni hesap ekrana yazılmaya başlanmalıdır.

Demek ki bir hesap üç aşamada gerçekleşiyor: Birinci argümanın girilmesi, ikinci argümanın girilmesi, sonucun gösterilmesi. Herhangi bir anda bu aşamalardan hangisinde olduğumuz belirtmek için yalnızca 1, 2, ve 3 değerlerini alabilecek bir tamsayı değişkene ihitiyacımız vardır. Ama sıradan bir tamsayı değişken kullanırsak –belki bu küçük program için sorun olmayacaktır- o değişkene yanlışlıkla başka değerler aktarma veya onu bilerek ya da bilmeyerek başka amaçlarla kullanmamız mümkündür. Bu tür istenmeyen durumlar olmasın diye aşamaları sayan değişkenimizi enum türü bir değişken olarak tanımlayabiliriz:

enum HesapAsamasi
{
BirinciArguman,
IkinciArguman,
Sonuc
};

Bu enum türünün üç üyesi var gibi gözükse de aslında tamsayı değer alabilen bir tek üyesi vardır. Tanımdaki üç ad bu tek üyenin alabileceği tamsayı değerleri –ki onlar da genellikle 0’dan başlayıp birer artan değerlerdir- temsil etmektedirler. Aşağıdaki değişken tanımı formumuza bu enum türünden bir değişken tanımı ekler:

HesapAsamasi m_hesapasamasi;

Bu değişken herhangi bir anda hesabın hangi aşamada olduğunu söyleyecekltir. Form ilk açıldığında daha başlamamış olan ilk hesap birinci aşamada olacağına göre bu değişkenin başlangıç değeri de birinci aşama olarak belirtilmelidir. Aşağıdaki komutları formun başlangıç özelliklerini belirleyen FormBaslangic() fonksiyonunu içine veya formun yaratıcı fonksiyonu içine eklemeliyiz:

m_hesapasamasi = HesapAsamasi.BirinciArguman;

m_BirinciArguman = 0;
m_IkinciArguman = 0;
m_Sonuc = 0;

Son üç komut da hesapta kullanılan argüman ve sonucu taşıyacak tamsayı değişkenlere ilk değerlerini veriyor.

Şimdi iş hesap makinemizi temsil eden formun düğmelerinine işlerlik kazandırmaya geldi. Rakam düğmelerinden herhangi birine basıldığında o düğmenin tamsil ettiği rakam o anda girilen argümanın sayı değerine eklenecektir. Demek ki rakam düğmeleri basılınca çalışacak bir mesaj yanıtlayıcı fonksiyona ihtiyacımız var.

private void OnRakamDugmeClick(object sender, EventArgs e)
{
// Gönderen düğmenin üzerindeki sayı değerini al
Button dugme = (Button) sender;
int rakam = int.Parse(dugme.Text);

switch (m_hesapasamasi)
{ // Hesabın birinci aşamasındaysak birinci argümana rakam ekle.
case HesapAsamasi.BirinciArguman:
m_BirinciArguman = 10 * m_BirinciArguman + rakam;
break;
// Hesabın ikinci aşamasındaysak ikinci argümana rakam ekle.
case HesapAsamasi.IkinciArguman:
m_IkinciArguman = 10 * m_IkinciArguman + rakam;
break;
// Sonuç aşamasında rakam girilmişse yeni bir hesaba başlanıyor
case HesapAsamasi.Sonuc:
m_txbEkran.Text = “”;
m_hesapasamasi = HesapAsamasi.BirinciArguman;
m_BirinciArguman = 0;
m_IkinciArguman = 0;
m_BirinciArguman = 10 * m_BirinciArguman + rakam;
break;
}
// Her halukarda yeni basilan rakami ekrandaki yaziya ekle
m_txbEkran.Text += dugme.Text;
}

Bu fonksiyon içindeki switch kod bloku hesap aşamasının mümkün olan üç değerine göre farklı adımlar atıyor. Birbirlerinden ayrık değerler alan bir tamsayı değişken (veya ayrık değerleri adlarla temsil eden bir sum değişken) söz konusu olunca ardışık if/else if/else blokları yerine bir switch() bloku kullanmak daha mantıklıdır.

Basılan rakamın o an girilmekte olan argümanın sayı değerine nasıl eklendiğine dikkat edin. Mevcut sayı değeri 10 ile çarpılıp yeni eklenen rakamın sayı değeri ekleniyor. Benzer bir ekleme ekrandaki yazı için yapılıyor; rakam karakteri (dugme.Text özelliğine bakılarak) m_txbEkran’daki karakter dizisine ekleniyor.

Bu fonksiyon herhangi bir rakam düğmesi basıldığında (yani onlardan herahngi biri Click mesajı yayınladığında) işleme konmalıdır. Dolayısıyla rakam düğmeleri yaratılırken onların Click mesajını yanıtlayacak fonksiyon olarak bu fonksiyonu tanıtmalıyız:

// 10 rakam düğmesi gerekli
m_buttonRakamlar = new Button[10];
// Rakam düğmelerini ayrı ayrı yarat
for (i = 0; i < 10; i++)
{
m_buttonRakamlar[i] = new Button();
m_buttonRakamlar[i].Text = i.ToString();
m_buttonRakamlar[i].Click += new EventHandler(OnRakamDugmeClick);
}

Tabi birden fazlka düğmenin Click mesajını aynı fonksiyon yanıtlayınca hangi düğmenin mesaj yanıtlayıcı fonksiyonu çağırdığını bilmek gerekebilir. Fonksiyonun başındaki şu komut mesajı yayınlayan kontrolü tanımlayan sender parametresininin Button kimliğini alıyor:

Button dugme = (Button) sender;

Bundan sonraki komutlarda da düğmenin Text özelliğine bakılarak düğme üzerinde hangi rakamın yazılı olduğu anlaşılıyor.

Aşağıdaki fonksiyon da işlem düğmelerinin her birinin Clik mesajını yanıtlayacak hangi işlemin seçilmiş olduğunu beliryecektir.

private void OnIslemDugmeClick(object sender, EventArgs e)
{
Button dugme = (Button) sender;
m_Islem = dugme.Text[0];
// Eğer birinci argüman girilmekteyse artık onun girilmesi tamamdır.
if (m_hesapasamasi == HesapAsamasi.BirinciArguman)
{
m_hesapasamasi = HesapAsamasi.IkinciArguman;
m_txbEkran.Text += dugme.Text;
}
// İkinci argüman girilmekteyse bir işlem belirtilmiş demektir;
// ikinci bir işlem daha belirtilemeyeceği için hiç bir şey yapma.
}

Seçilmiş olan işlem char (karakter) türü m_Islem adlı bir değişkende saklanıyor. İşlemi temsil eden karakter işlem düğmesinin üzerindeki yazıdan (düğmenin Text özelliğine bakılarak) alınıyor. Düğme yazısı tek karakterli olsa da bir karakter değil, karakter dizisidir; o yüzden sıra nosu 0 olan ilk karakteri aktarıyoruz m_Islem’e.

Birinci argüman girilirken bir işlem düğmesine basılmışsa hesabın ilk aşamasının bitip ikinci aşamasının başladığına, yani artık ikinci argümanın girileceğine karar veriliyor. Yalnız bu durumda bir şeyler yapılması gerektiği için bu kez bir switch bloku yerine tek bir koşul ifadesi kullanılmıştır.

Yalnız önemli bir nokta var: Yukarıdaki fonksiyon yalnızca dört işlemi temsil eden m_buttonIslemler düğme dizisinin elemanlarının Click mesajlarını yanıtlayacaktır:

// 4 işlem düğmesi gerekli
m_buttonIslemler = new Button[4];
// İşlem düğmelerini ayrı ayrı yarat
for (i = 0; i < 4; i++)
{
m_buttonIslemler[i] = new Button();
m_buttonIslemler[i].Click += new EventHandler(OnIslemDugmeClick);
}

Eşittir düğmesine basılması halinde farklı işlemler gerekeceği için o düğme için farklı bir mesaj yanıtlayıcı fonksiyon kullanmalıyız:

private void OnEsittirClick(object sender, EventArgs e)
{
// İkinci argümen girilmekteyse onun girilmesi bitip
// sonuç ekrana yazılmalıdır.
if (m_hesapasamasi == HesapAsamasi.IkinciArguman)
{
m_hesapasamasi = HesapAsamasi.Sonuc;
switch (m_Islem)
{
case ‘+’:
m_Sonuc = m_BirinciArguman + m_IkinciArguman;
break;
case ‘-’:
m_Sonuc = m_BirinciArguman – m_IkinciArguman;
break;
case ‘*’:
m_Sonuc = m_BirinciArguman * m_IkinciArguman;
break;
case ‘/’:
m_Sonuc = m_BirinciArguman / m_IkinciArguman;
break;
}
// ekrana = koyup sonucu ekle.
m_txbEkran.Text += ‘=’;
m_txbEkran.Text += m_Sonuc.ToString();
}
}

Bu fonksiyonda seçilen işlem türüne göre bir switch bloku içinde farklı hesaplar yapılıyor ve sonuç ekrandaki yazıya ekleniyor. Tabi sonuç bir sayısal değer olduğu için ekrandaki yazıya eklenmesi için ToString() üye fonksiyonu çağrılarak sayı değeri karakter dizisine dönüştürülüyor.

Visual C# programlama dilinde her değişken türünün, içindeki değeri karakter dizisine dönüştüren ToString() adlı bir üye fonksiyonu vardır. int, double gibi basit sayısal değişkenler için de bu böyledir. Kendi tanımladığımız sınıf değişkenleri için bu dönüştürmeyi bizim tanımlamamız gerekirdi, ama programlama dilinde mevcut değişkenlerin bu dönüştürme yeteneği zaten vardır.

Kod dosyasının verilen bilgilere göre tamamlanmasıyla sanal hesap makinemiz çalışır duruma gelecektir.

Uygulamayı İndir


 
4 Comments

Posted in C#, Projelerim

 

Tags: , , ,

Leave a Reply

 

 
  1. yuhubaa

    23 Ekim 2009 at 16:09

    Kullananların ve deneyenlerin yorum ve önerilerini dinlemek isterim

     
  2. caner

    13 Kasım 2009 at 09:29

    merhaba,

    güzel bir çalışma olduğunu düşünerek indirdim; ancak rar şifresi nedir?

    selamlar.

     
  3. arcenciel

    15 Aralık 2009 at 10:45

    Caner’e bende katılıyorum güzel bir çalışma olduğunu düşünüyorum fakat şifre :)

     
  4. ozgur

    15 Şubat 2011 at 06:54

    şifre mifre diyosunuz vazgeçtim indirmekten bizi boza daha basit biryoluyokmu :)