Xamarin.Forms’da MVVM Modeli
İçindekiler
Xamarin.Forms’da uygulama geliştirirken, kullanıcı arayüzü genellikle XAML ile geliştirilir ve arka plan kodlarıyla etkileşim sağlanır. Klasik yöntem böyledir. Ancak bu, uygulama büyüyüp karmaşık hale geldiğinde bakım ve test sorunlarına neden olur. Projeleri MVVM modeli ile inşa etmek bu sorunların önüne geçer.
MVVM (Model – View – ViewModel) modeli, bir uygulamanın iş(business) ve sunum(presentation) katmanlarını kullanıcı arayüzünden ayırır. Katmanların birbirinden bağımsız olması bir uygulamanın test edilmesini, bakımını ve geliştirilmesini kolaylaştırır. Doğal olarak, tasarımcıların ve geliştiricilerin daha uyumlu bir şekilde işbirliği yapmasını sağlar.
MVVM Tasarım Modeli (MVVM Design Pattern)
Her şeyden önce, bileşenlerin görevlerini ve birbirleriyle nasıl etkileştiklerini anlamak gerekir. MVVM modeli, Model, View ve ViewModel olmak üzere üç temel bileşenlerinden oluşur. Bu katmanlar birbirinden tamamen ayrıdır. Yani, Model katmanı ViewModel katmanını bilmez ve ViewModel da katmanı View katmanını bilmez. Böylelikle bileşenler bağımsız olarak geliştirilebilir.

Model
Model katmanı, MVVM modelinde en altta bulunan katmandır. Uygulamada kullanılacak verileri modelleyen, görsel olmayan sınıflardır. Model katmanı, ViewModel katmanından bağımsızdır. Veri Aktarım Nesneleri (DTO’lar) ve Düz Eski CLR Nesneleri (POCO’lar) Model katmanında bulunan nesnelere örnektir.
ViewModel
ViewModel katmanı ise MVVM modelinde ara katmandır. View ve Model arasında verileri bağlar, komutları çalıştırır ve değişiklikleri rapor eder, yani bir köprü görevi görür. ViewModel, Model katmanını bilir ancak View katmanını bilmez. ViewModel katmanı verileri View’a bağlarken PropertyChanged olayını ve koleksiyonlar için ObservableCollection sınıfını kullanır.
View
View katmanı MVVM modelinin en üst katmanıdır. MVVM modeli ile geliştirilmiş olan bir uygulamada kullanıcının gördüğü sayfalar View katmanında bulunan sayfalardır. Yani UI elemanlarının bulunduğu katmandır. Xamarin.Forms uygulamalarında, UI görünümleri genellikle Page veya ContentView sınıfından türetilir.
İdeal olarak, Xamarin.Forms projelerinde görünümleri XAML ile oluşturmanızda fayda var. Böylece, Separation of Concerns prensibini tam olarak uygulamış olursunuz.
Tıklama, seçim gibi View’daki bir görünüm öğesiyle etkileşim kurmak için, ViewModel sınıfında bir komut yöntemi tanımlayın ve bunu görünüm bileşenine bağlayın. Bunu daha sonra basit bir örnekle açıklayacağım.
ViewModel’i View’a Bağlama
MVVM modeli ile geliştirdiğiniz bir Xamarin.Forms uygulamasında Model ile View katmanları arasında etkileşim kurmak için ViewModel sınıfını View’a bağlamalısınız. Bunu yapmanın iki yolu vardır.
Dekleratif Bağlama(Binding Declaratively)
Dekleratif bağlama en basit bağlama yöntemdir. Bir ViewModel sınıfının View’daki görünüme BindingContext ile bağlanmasıdır.
<ContentPage ... xmlns:local="clr-namespace:eShop">
<ContentPage.BindingContext>
<local:LoginViewModel />
</ContentPage.BindingContext>
</ContentPage>
Basit olması avantajdır ancak ViewModel sınıfının varsayılan kurucu methoduna bağlanır, yani parametresi yoktur. Eğer ViewModel sınıfının kurucu methodu herhangi bir parametre almıyorsa dekleratif bağlama sorunsuz çalışır. Ancak, ViewModel sınıfının parametre alan kurucu methodları varsa, ViewModel sınıfını View sayfasının kod tarafında, programatik olarak bağlamak gerekir. Bu yüzden programatik bağlama tercih edilmelidir.
Programatik Bağlama (Binding Programmatically)
Programatik bağlamada ViewModel sınıfı, görünümün xaml.cs sınıfındaki BindingContext özelliğine bağlanır. Bu yöntemle ViewModel sınıfının parametreli kurucu yöntemleri bağlanabilir. Yani ViewModel sınıfındaki birden fazla kurucu methodu View’a bağlayabilirsiniz.
Ayrıca programatik bağlamada bağımlılık enjeksiyonu da yapabilirsiniz.
public LoginView()
{
InitializeComponent();
BindingContext = new LoginViewModel(navigationService);
}
MVVM Modelinde View’lerin Güncellenmesi
MVVM modeli ile geliştirilen uygulamalarda Model veya ViewModel katmanlarındaki sınıfların temel özellikleri değiştiğinde görünümlerdeki bileşenler de değişir. Yani, ViewModel sınıflarındaki propertylerin değeri değiştiğinde veya değerleri diğer özellikler tarafından kullanıldığında tetiklenir.
Bunu yapmanın yolu ViewModel sınıfını, BaseViewModel olarak adlandırılan temel sınıftan miras almak ve sonra ViewModel sınıfındaki property’nin setter’inde onPropertyChanged() methodunu çağırmaktır.
BaseViewModel sınıfı INotifyPropertyChanged interface’sini implement alır. Dolayısıyla onPropertyChanged() methodunu işler. onPropertyChanged() methodu ise property’de bir değişiklik meydana geldiğinde o property’i tetikler.
Yani BaseViewModel sınıfından miras almış olan bir ViewModel sınıfındaki herhangi bir property’nin setter’inde onPropertyChanged() methodu çağrılmışsa bu şu anlama gelir: Bu property her değiştiğinde bildir.
BaseViewModel sınıfı basitçe şu şekildedir:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Gerektiğinde BaseViewModel sınıfının içerisinde kontroller tanımlayabilirsiniz. Örneğin boolean bir değişken olan IsLoading. IsLoading adından da anlaşılacağı gibi değerin yüklenip yüklenmediğini kontrol etmek için kullanılır. IsLoading ilk başta false olarak ayarlanır. Daha sonra ise IsLoading ile kontrol edilen property’nin değeri yüklendiğinde IsLoading true olur. Böylece IsLoading değişkeninin true değerine karşılık gelen işlem yapılır.
public class BaseViewModel : INotifyPropertyChanged
{
private bool _isLoading { get; set; }
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
if (value != _isLoading)
{
_isLoading = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Komutları Uygulama(Implementing Commands)
Komutlar, ICommand interface’sini uygulayan nesnelerin örnekleridir. MVVM modeli ile geliştirdiğiniz uygulamalarda UI öğeleriyle etkileşim kurmak için komutları kullanmalısınız. (bkz. Veri Bağlama’da Komutlar)
Neden mi?
Klasik yöntemde, XAML’da bir UI elemanıyla etkileşim kurmak için, örneğin tıklama, bu UI elemanına Clicked methodu tanımlarız. Ardından bu sayfanın xaml.cs sınıfında, yani kod tarafında, bu Clicked methonun yazarız. Böylece uygulama çalışırken o UI elemanına her tıkladığımızda tanımlı Clicked methodu çalışır.
Ancak bu yöntem MVVM modelinin çalışma prensibine uymaz. Çünkü MVVM modelinde Clicked işleyicisi(handler) yoktur. Dolayısıyla etkileşimler Command arabirimi ile sağlanır. Yani ViewModel sınıfında komut nesneleri tanımlanır ve bu komutlar View’daki görünümlere bağlanır. Temel mantık budur.
Şimdi göstereceğim örnekle konuyu daha iyi anlayacaksınız.
Diyelim ki View katmanındaki bir XAML sayfasında bir PancakeView elemanı var. Bu PancakeView’a her tıklandığında XAML sayfasının bağlı olduğu ViewModel sınıfındaki bir komutun çalışmasını istiyorum.
Bunun yapmak için önce ViewModel sınıfında ICommand interface’sinin örneği olan bir komut tanımlıyorum. Ve bu komuta bir method tanımlıyorum ya da direkt içerisini dolduruyorum.
public ICommand ShareCommand => new Command(() => Share.RequestAsync(selectedTrack.PreviewUrl));
Daha sonra PancakeView bileşeninin TapGestureRecognizer özelliğini kullanarak komut yöntemini aşağıdaki gibi bağlıyorum. Böylece PancakeView’e her tıkladığımda ViewModel sınıfındaki komut çalışacak.
<pv:PancakeView>
<pv:PancakeView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ShareCommand}"/>
</pv:PancakeView.GestureRecognizers>
<Image Source="share.png" HeightRequest="40" WidthRequest="40"/>
</pv:PancakeView>
MVVM Modelinin Avantajları
- Seperation of Concerns (SoC): MVVM modeli katmanları birbirinden bağımsız olarak konumlandırır. Eklenen yeni bir model proje yapısını bozmaz.
- MVVM, her katmanda ayrı kod geliştirilmesine izin verir. Böylece, front-end ve back-end geliştiriciler kolayca işbirliği yapabilir.
- Bakımı kolaydır. Örneğin, Model sınıfında yaptığınız bir değişiklik View’i etkilemez.
- Test edilebilirliği artırır. Görsel arayüz ve kod birbirinden ayrı olduğu için test edilmesi daha kolaydır.
MVVM Modelinin Dezavantajları
MVVM modelinin avantajlarının aksine bazı dezavantajları da vardır.
- Kod miktarı artar. Katmanları birbirine bağlamak doğal olarak kod miktarını artırır.
- Hafıza tüketimi artar.
Sonuç
MVVM modeli bir Xamarin.Forms projesinde iş(business) ve sunum(presentation) katmanlarını kullanıcı arayüzünden ayırır. Katmanların birbirinden bağımsız olması bir uygulamanın test edilmesini, bakımını ve geliştirilmesini kolaylaştırır. Doğal olarak, tasarımcıların ve geliştiricilerin daha uyumlu bir şekilde işbirliği yapmasını sağlar.