C# hemen hemen bütün
uygulamaların geliştirilmesinde kullanılabilir düzeyde gelişmiş bir dildir.
Buna karşın C#’ı kullanabileceğimiz önemli alanlardan birisi de bileşenler
geliştirmedir. Öyle ki C# ve .NET Framework bileşenler üzerinde tasarlanmış ve
çok kolay bir şekilde kavrayıp kullanabileceğimiz şekilde basitleştirilmiştir.
Daha önceden COM ya da COM+ gibi terimler size bazı sıkıntıları anımsatıyorsa
C# ile bileşen geliştirmenin kolaylığı sizleri rahatlatacaktır. COM ile
hazırlanmış bir bileşen kullanıldığında,bilşenin bellekte kalması için referans
sayımı yapılır, AddRef(), Release() fonksiyonları çağrılır ve bunlar birçok
hatanın kaynağı olabilir. C# da ise bellek yönetimi otomatik yapıldığından,
bileşenimizi kullanan bir referans olduğu sürece bileşen bellekte kalır, aksi
durumda ise bellekten (garbage collector) anlamsız verilerin toplanmasını
sağlayan mekanizma sayesinde silinir.
Bundan sonraki kısımlarda C# ile bileşen geliştirirken temel olarak bilememiz
gereken konuları ele alıp,bu bilgilerimizi kullanarak çeşitli özelleştirilmiş
kontrolleri (custom controls) geliştirmeye başlayacağız.
Neden Özelleştirilmiş Kontroller ?
Dikkat ettiğimizde göze güzel görünen programlar hakkında her zaman iyi
fikirler edinmişizdir. Bunun bilincinde olan yazılım firmaları kendilerine
özel, daha güzel görünen kendi kontrollerini geliştirerek yazılımlarında
kullanırlar. Bu sayede daha modern görünümlü kullanıcı arayüzleri oluşturup
kullanıcıyı cezbetmeyi başarırlar (Windows 98 ile Windows XP’nin görünüşlerini
kıyasladığımızda daha kolay anlayabiliriz.) Özelleştirilmiş kotrollerin
geliştirilmesinin nedenlerinin başında standart Windows kontrollerinde
bulunmayan, kullanılışı kolay ve daha fazla fonksiyonel araçlar üretmektir. Biz
de kendi projelerimizde kullanabileceğimiz kontrollerimizi oluşturarak, daha
işlevsel uygulamalar geliştirebiliriz. İzleyeceğimiz yol ise, önce temel
bilgileri edineceğiz ve bu bilgilerimizle basit bir uygulama geliştireceğiz.
Daha sonra ayrıntılara girerek edindiğimiz bilgileri basit uygulamamıza
ekleyeceğiz ve kontrolümüzü adım adım daha fonksiyonel hale getireceğiz. Şimdi
gelin önce .NET Framework kullanarak, özelleştirilmiş kontroller
geliştirebilmemiz için gerekli olan ön hazırlığımızı yapalım.
Yukarıdaki şekil, bize kontrol hiyerarşisini göstermektedir. Bu hiyerarşiyi
kullanarak, bir kotrol geliştirmeye başlamdan önce o kontrol için hangi sınıfı
taban sınıf olarak alacağımıza karar veremede kullanabiliriz. Unutmayalım ki VS
.NET ile bize sunulan bütün kullanıcı kontrolleri bu hiyerarşiye göre
türetilmiştir. Şimdi bu sınıfların açıklamalarına bakalım :
Component : Bütün kontrol tabanlı sınıflar Component
(System.ComponentModel.Component) sınıfından türetilmiştir. System.Windows.Forms
isim uzayı altındaki bütün sınıflara Component sınıfı taban sınıf vazifesi
yapmaktadır. Eğer Component sınıfını kullanarak bir kontrol oluşturuyorsak ve
kontrol ile sistem kaynaklarını kullanan bir işlem yapıyorsak (bir dosyaya
yazma, okuma gibi) Dispose() methodunu kendimiz yazmalıyız ve bu method içinde
taban sınıfın Dispose() metodunu çağırmalıdır. (base.Dispose() gibi) Çünkü
kullanıcı kodu görmediği için sistem kaynaklarının kullanılıp kullanılmadığını
bilemez.
Control : Bir kontrol geliştirirken, temel ve çok önemli bazı
işleri bu sınıf bize sunar. Örneğin, kontrollerimizin mesaj döngüsü, klavye ve
mouse işlemleri, kontrolün büyüklüğü ve konumu gibi işlemler bu sınıf içinde
hazır olarak bulunur. Aslında bir çok dökümanda, kontrol için yazacağımız
sınıflar direk olarak Control taban sınıfından türetilmemesinin yerine
UserControl sınıfının kullanılması gerektiğinden bahseder. Bunun nedeni ise,
basit bir şekilde kontrol geliştirilebilemesi ve kontrol geliştirirken
yapılması gereken aşağı seviyeli (low-level) işlemleri bizim yerimize yapabilir
olmasıdır. Buna karşın Windows.Forms isim uzayı altındaki kontrollerin hepsi
taban sınıf olarak Control sınıfını kullanır.Örneğin, DataGrid, DateTimePicker,
Label, GroupBox, ListControl ve daha fazlası Windows.Forms.Control sınıfından
türetilmiştir.
ScrollableControl : İçeriği scroll edilebilir kontroller için
taban sınıf olarak kullanılır. Bu tür kontrollerin AutoScroll özelliği true
olması scroll edilebilme özelliğini kazandırır. Örneğin Panel kontrolü bu
sınıftan türütelirek oluşturulmuştur.
ContainerControl : Bu kontrol, button, label gibi diğer
kontrollerden bir yada daha fazlası kullanılarak oluşturacak kontrolleri
geliştirebilmemiz için taban sınıf olarak kullanabiliriz. Örneğin UserControl,
PropertyGrid ve Form gibi kontroller bu sınıfdan türetilerek oluşturulmuştur.
ContainerControl sayesinde, oluşturduğumuz kontroller arasında Tab tuşu ile
dolaşma, kontrollerin odaklanması (focus), kontrollere kısayol tuşlarının
atanması gibi özellikleri kontrolümüze kazandırmak kolaylaşır.
UserControl : .NET’deki UserControl, Visual Basic’in daha
önceki versiyonlarında bulunan User kontrollerine çok benzemektedir. Bir yada
daha fazla kontrol kullanılarak karmaşık kontroller oluşturulması için
UserControl sınıfını kullanmak kolay bir yöntemdir. Çünki bu sınıf,
ContainerControl sınıfından türetilmiş olup, birden fazla kontrol kullanılarak
oluşturacağımız kontrollerde, alt (child) kontrollerin yönetilmesi daha
kolay olacaktır. Bu gibi kontroller esasında kendi başlarına bağımsız
kontroller olduğundan, iş uygulamalarında verilerin geçerliliğini kontrol eden
veya database erişim için hazır kontrollerin geliştirilmesi için rahatlıkla
kullanılabilir.
Control sınıfını kullanarak bir kontrol oluşturduğumuzda, kontrolümüz hazır bir
çok özelliğe (property) sahip olur.Örneğin, Text, Size, ForeColor,
BackColor, Visible, Width, Height ve birçok değişik özellik gibi. Toplam
özellik sayısı 59 olup bu özellikleri MSDN’de "Control Members" isimli konu
başlığı altında ayrıntılı olarak inceleyebilirsiniz.Yeni bir kontrol
oluşturduğumuzda kontrolün kendi özelliklerine ek olarak kontrolümüze
fonksiyonellik kazandırmak için ek özellikler de ekleyebiliriz.
Bir kontrol geliştirebilmemiz için gerekli olan temel bilgilere değindik. Şimdi
basit bir kullanıcı kontrolü oluşturup bilgilerimizi bu örnek üzerinde
uygulamaya başlayalım. Amacımız, kullanıcının belirleyebileceği bir iconu
üzerinde gösterebilen bir pushbutton örneği oluşturmak. Buradaki önemli olan
nokta kontrolümüzü hangi sınıftan türeteceğimize karar vermek. O zaman
kendimize şu sorularu soralım! Soru :"pushbutton başka kontroller eklenerek
oluşturulacak mı yoksa sadece tek bir kontrolden mi oluşturulacak?" Cevap
:"Sadece tek bir kontrolden oluşturulacak!". O halde ContainerControl sınıfını
da tercih etmeyeceğiz. Soru : "pushbutton kontrolümüz scroll özelliğini
destekleyecek mi?" Cevap : "Hayır". O halde ScrollableControl sınıfını tercih
etmeyeceğiz. (Dikkat ederseniz şekildeki hiyerarşik yapıda üste doğru
çıkıyoruz) O zaman taban sınıf olarak kullanabileceğimiz sınıflardan en uygunu
System.Windows.Forms.Control sınıfıdır. Bu sınıfı kullanarak pushbutton
kontrolümüz için gerekli tüm özellikleri ve fonksiyonellikleri bize
sağlayacaktır. Böylelikle işimize yaramayacak bir çok özelliği (scroll
edilebilme vs.) geliştireceğimiz kontrol için saf dışı bırakmış oluruz.
Projemizi oluşturmak için aşağıdaki adımları uygulamaya başlayalım.
1- File->New->Project menüsünü kullanarak "New Project" dialog
penceresinden "Templates" bölümünden "Class Library" şablonunu şeçlim.Proje
ismi olarak da IconButton ismini verelim.
2- Oluturduğumuz projeye System.Windows.Forms.dll ve System.Drawing.dll
referanslarını ekleyelim.
3- Class1.cs dosya ismini IconButton.cs olarak değiştirelim. Kaynak
dosyamızdaki sınıf ismini ve varsayılan yapıcıyı (Class1 yerine) IconButton
olarak değiştirelim.
"Class Library" yerine "User Control" seçeneğini de kullanabilirdik. Ama bu
seçenek ile projemize gereksiz referanslar ve dosyalar ekleneceği için Class
Library şablonunu şeçtik. Aşağıda kodlarımızı yazarken gerekli açıklamalarıda
kod içinde yapacağız.
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace CSharpNedirCom.Bilesenler
{
//Sınıfımız için bir açıklama yapıyoruz
[System.ComponentModel.Description("C#Nedir? Button
Kontrolü")]
public class IconButton : System.Windows.Forms.Control
{
protected static int EDGE_PENDING =
4; //Iconun sol köşeye olan uzaklığı
//Kullanacağımız
değişkenler tanımlıyoruz
protected ButtonState buttonState;
//buttonun durumunu tutacak
protected Icon buttonIcon;
// button üzerindeki icon
protected int iconDrawWidth;
//iconun yüksekligi
protected bool mousePressed;
// buttonun basılı olup olmadığını belirlemek için
//Kullanıcıyı
bilgilendirmek için nitelikler tanımlıyoruz.
//Properties
penceresinde Icon özelliği için kullanıcıya bilgi veriyoruz
[System.ComponentModel.Description("Seçeceğiniz icon button üzerinde
görünecektir"),
//Icon
özelliğinin Properties penceresinde hangikategoride yer alacağını belirliyoruz
System.ComponentModel.Category("Özellikler"),
//Icon özelliğine
varsayılan değeri veriyoruz
System.ComponentModel.DefaultValue(null)]
//Kullanıcının
button için icon belirlemesi için Icon isimli bir özellik tanımlıyoruz
public Icon Icon
{
get {return
buttonIcon;}
set
{
buttonIcon
= value;
Invalidate();
// kontrolümüze paint mesajını gönderiyoruz
Update(); //kontrolümüzün çizim işlemini yapıyoruz
}
}
//varsayılan
yapılandırıcı
public IconButton()
{
InitializeComponent();
}
//kontrolümüz
için varsayılan değerleri veriyoruz
private void InitializeComponent()
{
buttonIcon =
null;
buttonState
= ButtonState.Normal;
mousePressed
= false;
}
//Focus olayı
gerçekleştiği zaman tekrar paint mesajı gönderiyoruz
protected override void
OnGotFocus(EventArgs e)
{
Invalidate();
base.OnGotFocus (e);
}
//Konrolümüz aktifliğini
kaybettiğinde paint mesajı gönderiyoruz
protected override void
OnLostFocus(EventArgs e)
{
Invalidate();
base.OnLostFocus
(e);
}
//Kontolümüz üzerindeki
yazı değiştiğinde kontrolümüzü güncelliyoruz
protected override void
OnTextChanged(EventArgs e)
{
Invalidate();
base.OnTextChanged
(e);
}
//Kontrolümüzün
boyutları değiştiğinde kontrolümüzü güncelliyoruz
protected override void
OnSizeChanged(EventArgs e)
{
Invalidate();
base.OnSizeChanged
(e);
}
//Kullanıcı mouse ile
button tıkladığında gerçeleşmesini istediğimiz işlemleri belirliyoruz
protected override void
OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) //Eğer sol tuş ise
{
Focus(); //kontrolü seçili hale getir
Capture = true;
buttonState = ButtonState.Pushed;//buttonu basılı duruma bilgisini
tut
mousePressed
= true; //mouse ile tıklandığı bilgisini tut
Invalidate();
Update();
}
else
//değilse taban sınıf ön tanımlı işlemleri yapsın
{
base.OnMouseDown (e);
}
}
protected override void
OnMouseUp(MouseEventArgs e)
{
if (mousePressed
&& (e.Button == MouseButtons.Left))
{
Capture = false;
mousePressed = false;
buttonState = ButtonState.Normal;
Invalidate();
Update();
}
else
{
base.OnMouseUp
(e);
}
}
//Kontrole paint
mesajı geldiği zaman buttonun tekrar boyanmasını ve çizilmesini sağlamak için
protected override void
OnPaint(PaintEventArgs e)
{
Draw(e.Graphics); //bu fonksiyon ile button için gerekli tüm çizim ve boyama
işlemlerini yapacağız
base.OnPaint
(e);
}
//parametre olarak
kontrolümüzün Graphics nesnesini veriyoruz
protected virtual void
Draw(Graphics g)
{
DrawButton(g);
if
(buttonIcon != null)
DrawIcon(g);
DrawText(g);
if
(base.Focused)
DrawFocusClues(g);
}
//Buttonumuzu
çiziyoruz
protected virtual void
DrawButton(Graphics g)
{
Rectangle rcButton = new Rectangle(0, 0, this.Width, this.Height); //button
çizimi için alan belirliyoruz
ControlPaint.DrawButton(g, rcButton, buttonState);
}
//Konrolümüzün
üstündeki yazıyı çizdiriyoruz
protected virtual void
DrawText(Graphics g)
{
//yazının sol kenara olan uzaklığını belirliyoruz
int left =
(buttonIcon == null)? EDGE_PENDING : iconDrawWidth + EDGE_PENDING;
int
width = Width - left;
int top =
EDGE_PENDING;
int height =
Height - (2 * EDGE_PENDING);
RectangleF
layoutRect = new Rectangle(left,top ,width , height);
if
(buttonState == ButtonState.Pushed)
layoutRect.Offset(1, 1);
//Yazının kontrol üzerindeki konumunu belirliyoruz
StringFormat
fmt = new StringFormat();
fmt.Alignment
= StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Center;
SolidBrush
textBrush = new SolidBrush(this.ForeColor);
g.DrawString(Text, Font, textBrush, layoutRect, fmt);
textBrush.Dispose();
}
//Konrolümüz
üzerine icon çizdiriyoruz
protected virtual void
DrawIcon(Graphics g)
{
int
top = (Height / 2) - (buttonIcon.Height / 2);
int height =
buttonIcon.Height;
int
width = buttonIcon.Width;
if ((top +
height) >= Height)
{
top = EDGE_PENDING;
int
drawHeight = Height - (2 * EDGE_PENDING);
float scale = ((float)drawHeight / (float)height);
width = (int)((float)width*scale);
height = drawHeight;
}
Rectangle
iconRect = new Rectangle(EDGE_PENDING, top, width, height);
if
(buttonState == ButtonState.Pushed)
iconRect.Offset(1,1);
g.DrawIcon(buttonIcon, iconRect);
iconDrawWidth
= iconRect.Width;
}
//kontrolümüz
aktif hala geldiğinde üzerine noktalı dikdörtgen (seçili olduğunu
belirten) çizdiriyoruz
protected virtual void
DrawFocusClues(Graphics g)
{
Pen p = new
Pen(Color.Black, 1f);
Rectangle
frameRect = new Rectangle(0, 0, Width, Height);
g.DrawRectangle(p,
frameRect);
p.DashStyle
= DashStyle.Dot;
frameRect =
new Rectangle(2,2,Width - 6, Height - 6);
if
(buttonState == ButtonState.Pushed)
frameRect.Offset(1,1);
g.DrawRectangle(p, frameRect);
p.Dispose();
}
}
}
|
Hazırladığımız kontrolümüzü test etmek için aşağıdaki adımları uygulayalım.
1- Projemizdeki "IconButton Solution"a sağ tıklayıp açılan menüden "Add"
menüsünü ve onun altında da "New Project" menüsü seçelim. Yeni bir Windows
Application oluşturun ve ismini DemoApp olarak değiştirin. DemoApp projesine
sağ tıklayın ve "Set as StartUp Project" menüsüne tıklayın.
2- ToolBox’dan "My User Controls" bölümünü seçin ve boş alana sağ tıklayıp
açılan menüden "Add/Remove Items" menüsünü tıklayın.Açılan dialog penceresinde
"Browse" butonunu tıklayın ve oluşturduğumuz "IconButton.dll" i seçin. Artık
kontrolümüzü ToolBox’a yerleşmiş olarak görebileceksiniz.Konrolü kullanmak için
sürükleyip formun üzerine koyabilirsiniz.Kontrolümüz seçili iken Properties
penceresinde, Icon özelliğine tıkladığınızda, Properties penceresinin alt
kısmında "Seçeceğiniz icon button üzerinde görünecektir" yazısının belirdiğini
göreceksiniz.
Görmüş olduğunuz gibi bir kontrol geliştirmek aslında o kadar da zor değil.
Herhangi bir kontrolü geliştirirken yukardaki standart adımlar her zaman
uygulanır.Bu yazımızda kontrol oluşturmayı ve test etmeyi iceledik. Ama
unutmayın ki kontrolümüzü daha da fonksiyonel kılabiliriz. Bir sonraki
yazımızda tasarım zamanında kontrollerimize ne gibi özellikler vereceğimizi
inceleyeceğiz.
Yazıda geçen uygulamanın kaynak kodlarını ve demo uygulamasını buradan indirebilirsiniz
Makale:
Bileşen Tasarımı ve Kullanıcı Kontrolü Geliştirme - 1 C#, Visual C# ve .NET Oğuz Yağmur
|