← Tüm yazılar

MAUI + MVVM ile kitap listesi ve arama ekranı

Kitap aramasında ekranın işi metni ve listeyi göstermek; aramanın işi ise ViewModel’de kalmak. MVVM ayrımı, düğmeye basılınca ekrandaki kontrolleri tek tek değiştirme ihtiyacını azaltıyor.

t.Turgay
MAUI + MVVM ile kitap listesi ve arama ekranı

Bir kitap arama ekranında düğmenin görevi Label'ları ve liste hücrelerini tek tek değiştirmek olmamalı. Ekran durumunu ViewModel'de tutup View'i o duruma bağlamak, arama davranışını UI ayrıntısından ayırır. Kitaplık arşivimdeki MAUI yapısını, repository ve API katmanlarının yanında bu MVVM sınırı üzerinden ele alıyorum.

ViewModel'in sahip olduğu durum

Örnekte gerçek API yerine Algorithms, Clean Code ve Refactoring başlıkları var. Search girdisi arama metnini, Books koleksiyonu gösterilecek sonucu taşıyor. Bu sadeleştirme, ağ ve kimlik sorunlarını bağlama mekanizmasıyla karıştırmadan incelemeyi sağlıyor. MAUI projesi ve CommunityToolkit.Mvvm 8.3+ kaynak üreticileri gerekir.

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class BooksViewModel:ObservableObject
{
    private readonly string[] source=["Algorithms","Clean Code","Refactoring"];
    [ObservableProperty] private string search="";
    public ObservableCollection<string> Books {get;}=new();
    public BooksViewModel()=>Filter();
    [RelayCommand]
    private void Filter()
    {
        Books.Clear();
        foreach(var title in source.Where(t=>t.Contains(Search,StringComparison.OrdinalIgnoreCase)))
            Books.Add(title);
    }
}
// Page XAML: bind Entry.Text to Search, Button.Command to FilterCommand,
// and CollectionView.ItemsSource to Books; set BindingContext to BooksViewModel.

ObservableProperty, search alanından Search özelliğini ve değişim bildirimini üretir. RelayCommand, Filter metodunu FilterCommand olarak dışarı açar. Sınıfın partial olması üreticinin aynı tipe kod eklemesi içindir. ObservableCollection ise Clear ve Add çağrılarını dinleyen View'e koleksiyon değişikliklerini bildirir; sıradan List değişikliği aynı davranışı kendiliğinden sağlamaz.

XAML, metot adına değil üretilmiş komuta bağlanır

<VerticalStackLayout xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
  <Entry Text="{Binding Search}" Placeholder="Search books" />
  <Button Text="Filter" Command="{Binding FilterCommand}" />
  <CollectionView ItemsSource="{Binding Books}">
    <CollectionView.ItemTemplate>
      <DataTemplate><Label Text="{Binding .}" /></DataTemplate>
    </CollectionView.ItemTemplate>
  </CollectionView>
</VerticalStackLayout>
// Inside the page constructor, after InitializeComponent():
BindingContext = new BooksViewModel();

BindingContext yanlışsa doğru property ve command isimleri yazılmış olsa bile bağlar sonuç üretmez. Bu yüzden ilk kontrolde ViewModel'in gerçekten sayfaya verildiğini doğrularım. Gelişmiş ekranda x:DataType ile compiled binding kullanmak, bazı ad hatalarını çalışma anında sessizce görmek yerine derlemede yakalayabilir.

Bu kısa XAML düzeni bağlama fikrini gösterir. Uzun ve kaydırılabilir CollectionView'i gerçek ekranda sınırlı yüksekliği olan Grid satırına yerleştirmek daha uygundur; sonsuz ölçülen bir dikey yığında liste ölçümü ve sanallaştırma beklentileri bozulabilir. Küçük üç elemanlı örneğin sorunsuz görünmesi büyük listede aynı yerleşimi doğrulamaz.

Arama düğmesine basılınca ne değişir?

Search=code olduğunda FilterCommand çalıştırılırsa yalnız Clean Code kalır. Arama metnini değiştirmek bu örnekte komutu kendiliğinden çağırmaz. Anlık arama isteniyorsa property-change kancası veya ayrı bir olay akışı eklenir. Boş sorgu bütün kaynak başlıkları gösterir; null ve kenar boşlukları gerçek Entry girdisinin sınırında normalize edilmelidir.

Filtre, kaynak diziyi değiştirmiyor; yalnız görünen koleksiyonu yeniden kuruyor. Bu ayrım, arama temizlenince bütün kitapların geri gelmesini sağlar. Filtrelenmiş sonuçtan tekrar filtreleme yapmak kaynak kayıtları yanlışlıkla “kaybetmiş” gibi gösterebilir.

API'ye geçildiğinde üç durum daha gerekir

Gerçek kitap sorgusunda yükleniyor, boş sonuç ve hata ayrı olmalı. Boş sonuç başarılı aramadır; bağlantı hatasında eski sonuçları silip “kitap yok” demem. Asenkron komut çift gönderimi kontrol edebilir; yeni arama başladığında eski yanıtın listeyi ezmesini önlemek için iptal veya istek sürümü gerekir.

ObservableCollection güncellemeleri UI thread'inde yapılmalıdır. HTTP çağrısını Task.Run içine koymak otomatik performans iyileştirmesi değildir; I/O zaten async olarak beklenebilir. ViewModel testinde sahte repository ile sonuç ve hata akışları sınanır; cihaz ekranına tıklamak her iş kuralı testinin zorunlu parçası olmamalı.

Sayfa ömrü ve kullanıcı girdisi

Her gezinmede yeni ViewModel yaratmak arama metnini sıfırlayabilir; sürekli aynı örneği tutmak da eski veriyi koruyabilir. Bu bir DI süresi kararıdır. Sayfa kapanınca devam eden arama veya event aboneliklerinin nasıl duracağı belirlenmeli. Barkoddan ISBN alma ise ayrı bir giriş akışı; aynı forma veri taşısa da bu makalenin liste ve komut sözleşmesini değiştirmez.

İlk denemede üç kayıt, code aramasında tek kayıt, olmayan kelimede boş liste ve aramayı temizleyince tekrar üç kayıt beklerim. Ardından yavaş ve başarısız repository yanıtlarını denerim. Bağlama sorunu ile veri sorunu böylece ayrı ayrı görülebilir.

ObservableProperty üreticisi; RelayCommand üreticisi; CollectionView yerleşimi.

✳ Bu yörüngenin sonuna geldin.
Diğer yazıları keşfet →
turgay.Biraz kod, biraz piksel, bolca merak.© 2026 · Dünya’dan sevgilerle ✳