Windows projelerini oluştururken genellikle bir MDI (multiple document interface) form (parent) ve onun içinde açılan çocuk (child) formlar şeklinde yapmaya çalışırız. Bu makalede bu şekilde bir uygulamada inheritance ve singleton patternin nasıl uygulanacağını göreceğiz.
Öncelikle yapmaya çalıştığımız projedeki ana formda (MDI) solda bir ağaç (tree), üstte bir menü ve altta da durum çubuğu (statusbar) olsun. Ağaç yapımızda şöyle olsun:

Buradaki her alt düğüm (node) bir ekranı (formu) temsil etmektedir ve bu küçük uygulamamızda 6 child fom bulunmaktadır. Tabi ki bu tip projelerde child form sayısı çok daha fazla olmaktadır. Ayrıca bu child formlar birbirlerine benzediklerinden (örneğin her child formda üstte menu olması, altta statusbar olması, ekranı kaplamış bir groupbox olması, zemin renginin yeşil olması gibi bir takım ayarlar...) her formu tek tek projemize ekleyip bu ayarları hepsinde tek tek yapmak hem zaman kaybı olacaktır hem de ileride bakımını ve yönetimini zorlaştıracaktır. Bu sebeple projemizde interitance kullanacağız. Inheritance temel olarak bir sınıfın özelliklerini kalıtsal olarak başka bir sınıfa aktarmaktan ibarettir. (Detaylı bilgi için bakınız.) Bunun için bir tane temel form yaratıp, bu forma her ekranda olmasını istediğimiz özellikleri vereceğiz ve diğer tüm formları bu temel formdan inherit edeceğiz. Bunun için projemize yeni bir form ekliyoruz istediğimiz özellikleri veriyoruz.

Yeni yaratacağımız formları ise Project > Add New Item > Inherited Form şeklinde oluşturacağız. Standart form eklemeden farklı olarak eklediğimiz formun hangi formdan türediğini soracak ve listeden temel formumuzu seçeceğiz. Peki ya formlarınız daha önceden eklenmişse ve siz sonradan bu şekilde bir yapıya geçiyorsanız ne olacak? O zaman da formunuzu tanımladığınız yerin hemen altında inherit kodunu yazmanız yeterli olacaktır:
|
Public Class FrmKategoriTanim
Inherits FrmBase |
Buraya kadar yaptığımız girişten sonra asıl makale konumuza geçelim. Ana ekranda gördüğümüz gibi her alt nodeda child formumuzu çağırıyoruz:
|
Dim frm As New FrmUrunTanim frm.MdiParent = Me
frm.Show() |
Örneğin ürün tanım ekranı açıkken, tekrar aynı ekranı açmak istediğimizde yeni bir ürün tanım ekranı açılacaktır. Bu şekilde her tıklamada yeni bir ekran açılacaktır, ancak biz istiyoruz ki child form hiç açılmamışsa bir kez açılsın, eğer açıksa bize açık ekranı getirsin. Bunu aslında mdi formun ActiveMdiChild propertysi (çağrıldığı andaki açık olan child formları verir) içinde basit bir for..next döngüsü kurup açmak istediğimiz form zaten açık mı değil mi diye bakarak anlayabiliriz. Ancak biz burada farklı bir yöntem uygulayacağız ve singleton design pattern ile inheritance karışımı bir çözüm üreteceğiz. Singleton’da genel prensip bir instance’nın (burada, “New FrmUrunTanim” oluyor) daha önceden oluşturulup oluşturulmaıdığını tespit etmek, instance yoksa yenisini yaratmak varsa, eskisini döndürmek şeklindedir. Temel formumuz olan FrmBase üzerinde yaptığımız işlemlerin diğer tüm formları da etkilediğini söyledik. Bu anlamda örneğin her formun load eventi altında standart bir işlem yapıyorsak bunu sadece FrmBase formunun load eventina yazmamız yeterli olacaktır. Bu sebeple bizde singleton ile ilgili kodlarımızı her forma yazmaktansa FrmBase altına yazacağız:
|
Private _FormLoaded As Boolean
Private _instance As Form
Public Function GetInstance(ByVal willLoadedForm As Type) As Form
If _FormLoaded = False Then
_instance = CType(Activator.CreateInstance(willLoadedForm), Form)
AddHandler Me._instance.Closed, AddressOf Me.WhenFormClosed
_FormLoaded = True
End If
_instance.Focus()
Return _instance
End Function
Private Sub WhenFormClosed(ByVal sender As Object, ByVal e As EventArgs)
Dim closingForm As Form = CType(sender, Form)
RemoveHandler closingForm.Closed, AddressOf Me.WhenFormClosed
_FormLoaded = False
End Sub |
Ana formda ise kodlarımızı şu şekilde düzenliyoruz:
|
Private Sub mainTree_DoubleClick(ByVal sender As Object, ByVal e As EventArgs)
Dim nd As TreeNode = Me.mainTree.SelectedNode
If ((Not nd Is Nothing) AndAlso (nd.Nodes.Count = 0)) Then
Dim frm As Form = Nothing
Select Case nd.Name
Case "kisi"
frm = FrmKisiTanim.GetInstance(GetType(FrmKisiTanim))
Case "kategori"
frm = FrmKategoriTanim.GetInstance(GetType(FrmKategoriTanim))
Case "urun"
frm = FrmUrunTanim.GetInstance(GetType(FrmUrunTanim))
Case "satis"
frm = FrmUrunSatis.GetInstance(GetType(FrmUrunSatis))
Case "stok"
frm = FrmStokDurumu.GetInstance(GetType(FrmStokDurumu))
Case "satis_raporu"
frm = FrmSatisRaporu.GetInstance(GetType(FrmSatisRaporu))
End Select
If (Not frm Is Nothing) Then
frm.MdiParent = Me
frm.Show()
End If
End If
End Sub |
FrmBase altında yazdığımız kodlarda _FormLoaded ile formumuzun yüklenip yüklenmediği bilgisini, _instance ile de yüklenen form bilgisini tutuyoruz. Formlarımızın yeni bir instancenı almak için Activator sınıfından yararlandık. CreateInstance metodu ile Type cinsinden verilen bir nesnenin instance alınabilmektedir. Burada tüm formlarımızın ilave constructorı olmadığını varsaydık. Eğer parametre ile formlarımızı açmamız gerekiyorsa CreateInstace metoduna ikinci parametre olarak bu constructor parametrelerini array olarak geçebiliyoruz. Formumuzun kapatıldığını anlamak için de _instance’ın FormClosed eventini yakalayıp _FormLoaded = false yapmamız gerekmektedir. (Bu kodu doğrudan FrmBase’in FormClosed eventine yazamıyoruz, çünkü aynı formu tekrar açmak istediğimizde dispose edilmiş bir forma ulaşmaya çalıştığımıza dair bir hata alırız.) Formlarımızı ana formdan çağırırken FrmBase yerine ilgili formdan çağırdık, sanki kodları oraya yazmışız gibi J