MVVM konusunda tonla İngilizce makale olmasına rağmen Türkçe içeriğin (her zaman ki gibi) eksik olması ve Türkiye'de bilinirliğini arttırmak amacıyla MVVM hakkında bir makale yazmayı düşünüyordum. .NET Framework teknolojileriyle ilgili yazılım geliştiricilere yönelik çıkması planlanlanan ancak çeşitli sebeplerden dolayı iptal edilen bir dergi için aylar önce yazdığım orijinal makalemi bu amaçla düzenleyerek - ve bir blog postu için uzun olduğu için bölerek - sizlerle paylaşıyorum.

1. WPF ile Model View ViewModel: Giriş
2. WPF ile MVVM: Model
3. WPF ile MVVM: View
4. WPF ile MVVM: ViewModel
5. WPF ile MVVM: Parçaları Birleştirmek
Seriye ait kaynak kodlar: MvvmWithWpfSample.rar (245,34 KB)

ViewModel (VM), diğer bir değişle “model of a view”, View’ın bir çeşit modelidir. Üzerinde View’ın durumunu ve davranışlarını tutar.  View’ın sunabilmesi için bazen ham, yani direk olarak Model’in özelliklerini, bazen de (altında genellikle model özellikleri olacak şekilde sarmalayarak) kendi üzerinde özellikler tanımlar. Model’i, View’ın sunabileceği hale getirdiği için VM’i “aslında koca bir dönüştürücüdür” şeklinde tanımlayanlar olmuştur (Josh Smith).

ViewModel’in önemli bir sorumluluğu da Model’i direk refere ettiği için Model servislerini çağırmasıdır. View’lar ViewModel üzerinde tanımlanan özelliklere “bind” ederler. Bu özelliklerden bir kısmı da aslında View’ın Command’larını çalıştıran delegelerdir. Delegeler çağrıldığında, örneğin müşteri arama komutu gerçekleştiğinde, VM bu isteği gerekli parametreleri sağlayarak Model’e iletir ve Model’in cevabını alarak View tarafından kullanılabilecek hale getirir.

MVVM tasarım kalıbı, aynı Fowler’ın PM’de bahsettiği gibi GUI (yani View) veya ViewModel tarafında bir özelliğin değeri değiştirildiğinde diğer tarafın da bundan haberdar olmasını gerektirir. View tarafında bu işlem Binding mekanizmaları ve GUI bileşenleri ile kolayca halledilir. Örneğin View’daki bir metin kutusuna yazılan bir metin Binding deklerasyonu ile tanımlandığı ölçüde otomatik olarak VM’de bağlandığı özelliği günceller. Ancak VM, yani kod tarafından yapılan bir değişiklik View tarafından otomatik olarak algılanamaz. Örneğin müşteri arama komutu sonucu Model’imizden dönen bir dizi Customer nesnesinin CustomersFound koleksiyonunu doldurduğunu düşünelim, ne yazık ki böyle bir durumda binding tanımlarımızın eksiksiz olmasına rağmen View, yani GUI’miz otomatik olarak kendini güncelleyerek bulunan müşterileri göstermez. View’ımıza ilgili VM özelliğinin değiştiğini haber verecek bir mekanizma gereklidir. System.ComponentModel isim uzayında bulunan INotifyPropertyChanged arayüzü bu işlevi sağlar. Bu arayüz sadece bir PropertyChanged event’i tanımlar.  View içindeki WPF mekanizmaları, DataContext özelliğine referansı atanmış nesne INotifyPropertyChanged arayüzünü uyguluyorsa PropertyChanged event’ini işleyecek şekilde kendini kaydeder. Böylelikle bir VM özelliği değişirse View, kaydolduğu VM PropertyChanged eventi sayesinde bundan haberdar olur ve üzerinde gerekli güncellemeyi gerçekleştirir. INotifyPropertyChanged mekaniklerinin View tarafındaki kısmı WPF tarafından otomatik gerçekleştirilmesine rağmen, özellik değişince VM tarafında bu event’i tetiklemek yine MVVM geliştiricisine kalır. MVVM altyapılarında INotifyPropertyChanged implementasyonu genellikle bütün VM’lerin türediği kök bir sınıfta halledilir, ancak tetikleme işlemi bir şekilde VM özelliğinin setter’ına sızabilir.

Örnek senaryomuz için SearchWindowViewModel adlı bir sınıf oluşturup, SearchWindowView’ımıza bind edilmek üzere özelliklerimizi Diyagram 3’de görüldüğü üzere oluşturuyoruz.

ViewModel Sınıflarımız

Dikkat edilirse SearchWindowView sınıfının içi inşa edici metodu dışında boştur, yani View’ın code behind’ını boş tutma çabalarımız sonuç vermiştir. SearchWindowViewModel üzerinde tanımlanmış özellikleri inceleyelim. CustomerNameSearched, GUI’mizdeki arama metin kutusunun Text özelliğine bağlanmıştır. CustomersFound, GUI’deki arama sonucu gözükecek listenin öğelerine bağlanmıştır. IsCustomerSearchResultsVisible arama sonucu gözükecek listenin Visibility’sine bağlanmıştır. SearchCommand ise “Listele” butonunun Command’ı olarak bağlanmıştır.

Özelikle değinilmesi gereken iki noktadan biri CustomersFound koleksiyonunun bir ObservableCollection olmasıdır. Bunun sebebi senaryomuzda kullanılmasa bile CustomersFound koleksiyonuna herhangi bir öğe eklenip çıkarıldığında GUI’mizin kendini güncellemesini istememizdir.  Tahmin edeceğiniz üzere ObservableCollection’ın normal bir koleksiyondan tek farkı, INotifyPropertyChanged arayüzünü uygulamış olmasıdır.

Değineceğimiz ikinci nokta ise SearchCommand’dır. Bu özellik ICommand, tipindedir ve içerisinde OnSearchCommand’ı sarmalayan bir DelegateCommand nesnesi döndürür. DelegateCommand, kullanıcı tanımlı ICommand oluşturmayı kolaylaştırmak için geliştirilen benzeri sınıflardan biridir ve Microsoft tarafından geliştirilen uygulama geliştirme altyapısı Prism’in bileşenidir. Aşağıda bulunan "Command Sorunsalı Üzerine” adlı başlıkta Command’larla ilgili daha fazla bilgiye yer vereceğiz.

Kullanıcı uygulamayı çalıştırıp, arama kutusuna giriş yaptıktan sonra, “Listele” butonuna tıkladığında, VM üzerindeki OnSearchCommand çalışacak ve Model’i sorgulayıp gerekli bilgileri aldıktan sonra, üzerinde bulunan CustomersFound koleksiyonunu dolduracak ve IsCustomerSearchResultsVisible özelliğini true değerine çekecektir. VM üzerinde güncellenen az önce saydığımız özellikler setter kısımlarında ViewModelBase de tanımlı PropertyChanged event’ini tetikledikleri için de, bu event’i dinleyen View kendini güncelleyerek sonuçları kullanıcıya gösterecektir.

Command Sorunsalı Üzerine

WPF bize MVVM için birçok olanak tanımasına rağmen View’daki bileşenlerin event’lerini VM’deki Command‘lara Binding yaparak çözmek, aslında bazı zorlukları da beraberinde getirir. Command’ların tasarım nedeni, birden fazla kaynaktan (ICommandSource), gelebilecek aynı işlevin tek bir nesne ile simgelemektir. Örneğin bir metin kutusundan fare ile seçerek “Kopyala” komutunun verilmesi ile klavye ile CTRL+C yapılması durumunda ApplicationCommands.Copy komutu gönderilir. Kendi komutunuzu oluşturmak istediğinizde ICommand arayüzünü implemente eden bir sınıf yazmanız gerekir. Ayrıca komutunuzun hangi metodu çalıştıracağıyle ilgili CommandBinding’ini de ya XAML içine, ya da code behind’a el ile kodlayarak koymalısınız. Sıkıntılar burada bitmiyor, her View bileşeni bir ICommandSource olmadığından Command’ları tetikleyeceğiniz bileşenler de sınırlıdır. Yine aynı şekilde Command’ların tetikleneceği eventler’de sınırlıdır. Bütün bunları aşmak amacıyla MVVM altyapısı geliştiricileri, DelegateCommand (Prism), RelayCommand (JoshSmith), SimpleCommand (Marlon Grechs) gibi ICommand implementasyonları ve bunları XAML’da ilgili element’de deklare etmeyi sağlayan AttachedBehaviour veya DependencyProperty’ler oluşturmuşlardır.

Pingbacks and trackbacks (4)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading