.NET MAUI arayüzünü XAML yerine C# ile yazmak istediğinizde iki ciddi seçenek var: CommunityToolkit.Maui.Markup ve FmgLib.MauiMarkup. İkisi de aynı soruya cevap veriyor, ikisi de akıcı uzantı metotları kullanıyor, ikisi de MIT lisanslı.
Bu yazıyı FmgLib.MauiMarkup'ın geliştiricisi olarak yazıyorum — dolayısıyla taraflıyım ve bunu peşinen söylüyorum. Buna karşılık burada tek bir pazarlama cümlesi bulmayacaksınız: her iddia iki projenin kendi genel dokümantasyonundan geliyor, Community Toolkit'in daha iyi seçim olduğu durumlar için ayrı bir bölüm var ve sonda ikisini aynı projeye kurup ne olduğunu ölçtüm.
Sürüm ve indirme rakamları hızla değişir. Buradaki karşılaştırma Ağustos 2026 durumunu yansıtıyor: FmgLib.MauiMarkup 10.3.0, CommunityToolkit.Maui.Markup 8.0.0. Geri dönüşü zor bir karar vermeden önce kaynaklara bakın.
Farkların kaynağı: iki mimari tercih
Tabloya dalmadan önce tek bir cümle, geri kalan her şeyi açıklıyor:
- CommunityToolkit.Maui.Markup, elle yazılmış ve özenle seçilmiş bir uzantı kümesidir.
- FmgLib.MauiMarkup, bir Roslyn kaynak üreteci ile derleme anında üretilir.
Bunun doğrudan sonucu şu: Toolkit'te kapsam, birinin o metodu yazmış olmasına bağlıdır. FmgLib'de kapsam, MAUI'de o property'nin var olmasına bağlıdır.
İkinci tercih de şu: Toolkit binding'i ayrı bir .Bind(...) çağrısıyla kurar; FmgLib binding'i zaten set ettiğiniz property'nin içine koyar. Aşağıdaki farkların çoğu bu ikisinden türüyor.
Kapsam: zincir nerede kırılıyor?
Toolkit Label, Image, Grid, VisualElement, ItemsView, Placeholder gibi bir düzine aileyi kapsıyor ve kapsadığı yerde gayet iyi. Ama biri o property için yardımcı yazmamışsa zincirin ortasında object initializer'a düşersiniz:
// CommunityToolkit — yardımcı olmayan property'de iki stil karışıyor
new Entry
{
Keyboard = Keyboard.Numeric, // fluent yardımcı yok → object initializer
ReturnType = ReturnType.Done,
}
.Placeholder("Sayı girin") // yardımcı var → fluent
.FontSize(15)
.Height(44);
// FmgLib — her bindable property üretildiği için zincir kırılmıyor
new Entry()
.Keyboard(Keyboard.Numeric)
.ReturnType(ReturnType.Done)
.Placeholder("Sayı girin")
.FontSize(15)
.HeightRequest(44);
Aynı şey event'ler için de geçerli: FmgLib her event için On<Event> üretir, Toolkit'te event'ler normal += aboneliğiyle kalır.
Bu tek başına belirleyici olmayabilir — object initializer da geçerli C#. Ama kod tabanı büyüdükçe “hangi property'nin yardımcısı var?” sorusu her yazana ayrı bir yük bindirir.
Üçüncü parti kontroller: farkın en geniş olduğu yer
Gerçek uygulamalarda ekranların önemli bir kısmı Syncfusion, DevExpress, UraniumUI, SkiaSharp, ZXing gibi kütüphanelerden gelir.
Toolkit ile bu kontroller yalnızca genel VisualElement/View yardımcılarını alır; kontrole özgü her property düz atama olarak kalır. FmgLib'de aynı üreteç onlar için de çalışır — ya tek satırlık bir MSBuild bayrağıyla, ya da kontrol başına opt-in ile:
<MauiMarkupSourceGenerator>true</MauiMarkupSourceGenerator>
new SfButton().Text("Satın al").CornerRadius(8) // Syncfusion
new SKLottieView().Source(…).RepeatCount(-1) // SkiaSharp.Extended
new CameraView().IsTorchOn(true).OnFrameReady(…) // ZXing
Üretilen metotlar birinci sınıf: binding alırlar, Style<T> içinde çalışırlar, animasyon uzantıları gelir. Üçüncü parti kontrollerin attached property'leri de kapsam dâhilinde.
Binding nerede yaşıyor?
İkinci mimari tercihin görünür hâli:
// CommunityToolkit — binding, property'yi bir kez daha adlandırır
new Entry().Bind(Entry.TextProperty,
static (ViewModel vm) => vm.RegistrationCode,
static (ViewModel vm, string text) => vm.RegistrationCode = text)
// FmgLib — binding, zaten set ettiğiniz property'nin içinde
new Entry().Text(e => e
.Getter(static (ViewModel vm) => vm.RegistrationCode)
.Setter(static (ViewModel vm, string text) => vm.RegistrationCode = text)
.BindingMode(BindingMode.TwoWay))
İkisi de derlenen (compiled) binding kuruyor, ikisi de inline Convert/ConvertBack destekliyor. Fark, Entry.TextProperty'yi tekrar yazıp yazmadığınız ve binding'in görsel olarak nereye ait olduğu.
Multi-binding'de fark tipe dönüşüyor. Toolkit'te FuncMultiConverter konumsal/object[] değerler alır; FmgLib'de delege parametreleri alt binding'lerin tipleridir:
new Button().IsEnabled(e => e
.Path("AcceptedTerms")
.Path("ConfirmedEmail")
.MultiConvert((bool terms, bool email) => terms && email))
// derlenmiş ve string alt binding'ler aynı multi binding'de karışabilir
new Label().Text(e => e
.Getter(static (OrderVm vm) => vm.Total)
.Path("ItemCount")
.MultiConvert((decimal total, int count) => $"{count} ürün — {total:C}"))
Duruma göre değişen değerler
FmgLib'in property builder'ı yalnızca binding için değil; tema, cihaz tipi ve platform da aynı lambda'nın içinde:
new Label()
.TextColor(e => e.OnLight(Colors.Black).OnDark(Colors.White))
.FontSize(e => e.OnPhone(13.0).OnTablet(15.0).OnDesktop(17.0))
.Margin(e => e.OniOS(new Thickness(0, 20, 0, 0)).Default(new Thickness(0)))
.Text(e => e.Path("Title"))
OnLight/OnDark gerçek bir AppThemeBinding üretir; tema değişince çalışan arayüz kendini yeniden boyar, sayfa yeniden kurmaya ya da kaynak sözlüğünü boşaltıp doldurmaya gerek kalmaz. Toolkit'te tema için AppThemeBinding/dinamik kaynak yardımcıları var ama property çağrısının içinde bir değer olarak değil; idiom ve platform için karşılığı yok.
Yalnızca FmgLib'de olanlar
Toolkit'te karşılığı bulunmayan başlıklar:
- Yerelleştirme — JSON ve RESX, canlı dil değişimi, kültür fallback zinciri, biçimli çeviriler, RTL için
FlowDirection, eksik anahtar politikası. Toolkit hiç yerelleştirme içermez; ayrı bir paket kurupINotifyPropertyChangedtekrar-okumalarını kendiniz bağlarsınız. - Arayüz metodunu yeniden çalıştıran hot reload — .NET Hot Reload ikisinde de kodunuza uygulanır, ama Toolkit'te arayüz kurulumunu yeniden çağıran bir şey yoktur; düzenlemeyi görmek için genelde sayfayı yeniden açarsınız. FmgLib'in
Build()deseni her uygulanan düzenlemede arayüzü yeniden kurar, üstelik sayfaları zayıf referansla kaydeder. - Üretilen
Animate<Property>To— her animatable property için, await edilebilir. VisualState<T>ve adlandırılmış durum sabitleri, ayrıca durum girişinde çalışan animasyonlar.- Akıcı trigger'lar — property, data, multi, event.
- .NET 9 ve .NET 10 tek paket sürümünden.
dotnet newşablonu, galeri örnek uygulaması ve İngilizce/Türkçe dokümantasyon.
Community Toolkit ne zaman daha iyi seçim?
Bu bölüm nezaket icabı değil; gerçekten böyle.
Yönetişim ve süreklilik. Toolkit bir .NET Foundation projesi. Ekip değişse de sürecek, Microsoft Learn üzerinde dokümante ve kurumsal onay süreçlerinden geçmesi kolay. FmgLib bağımsız bir proje; bu bazı organizasyonlarda tek başına belirleyici bir kriterdir ve olması da normaldir.
Olgunluk ve topluluk. İndirme sayıları arasında iki mertebe fark var (~1 milyona karşı ~19 bin). Bu, karşılaşacağınız sorunun daha önce sorulmuş olma ihtimali, Stack Overflow cevabı bulma şansı ve ekibinize aldığınız kişinin kütüphaneyi tanıma olasılığı demektir.
Küçük API yüzeyi bir özellik olabilir. Toolkit'in kapsamı dar ama net. Küçük ekiplerde “her şeyin akıcı karşılığı var” bazen fazla seçenek anlamına gelir; sınırlı bir yardımcı kümesi tutarlılığı kendiliğinden zorlar.
Derleme süresi. FmgLib'in gücü kaynak üretecinden geliyor ve üreteç bedava değil. Otomatik modda çok sayıda üçüncü parti kütüphane tarandığında derleme süresine yansır (opt-in mod bunu sınırlar). Toolkit'te böyle bir maliyet yok, çünkü üretilen kod yok.
Zaten Toolkit ekosistemindeyseniz. CommunityToolkit.Maui'nin davranış, converter ve popup'larını kullanıyorsanız, markup'ı da aynı aileden almak tutarlı bir tercihtir.
Kabaca bir özet: kapsam, üçüncü parti kontroller, yerelleştirme veya hot reload döngüsü sizin için belirleyiciyse FmgLib; kurumsal yönetişim, topluluk büyüklüğü ve minimum bağımlılık riski belirleyiciyse Toolkit.
Aynı projede ikisi birden olur mu?
Bu, geçiş düşünen herkesin ilk sorusu — ve tahmin etmek yerine ölçtüm. İkisini de gerçek NuGet paketleriyle aynı projeye kurdum.
Aynı dosyada iki namespace'i import ederseniz çakışır. Örtüşen isimler derleme hatası verir:
error CS0121: 'FmgLib.MauiMarkup.LabelExtension.Text<T>(T, string)' ile
'CommunityToolkit.Maui.Markup.ElementExtensions.Text<TBindable>(TBindable, string?)'
arasındaki çağrı belirsiz.
Aynı hata .Placeholder gibi diğer örtüşen isimlerde de çıkıyor. Beklenen bir durum: ikisi de MAUI tiplerine uzantı metodu ekliyor.
Ama dosya bazında ayırırsanız sorunsuz derleniyor. İki paketi aynı projede tutup her dosyada yalnızca birinin namespace'ini import ettiğimde derleme temiz geçti:
// YeniSayfa.cs
using FmgLib.MauiMarkup; // yalnızca FmgLib
// EskiSayfa.cs
using CommunityToolkit.Maui.Markup; // yalnızca Community Toolkit
Pratik sonuç: geçiş “hep ya da hiç” değil. Yeni ekranları yeni kütüphaneyle yazıp mevcut ekranlara dokunmayabilir, dosya dosya ilerleyebilirsiniz. ImplicitUsings veya global using ile birini proje geneline yaymadığınıza dikkat edin; yaydıysanız çakışan dosyalarda using static/alias yerine tek namespace bırakmak en temizi.
Karar için üç soru
- Kullandığınız kontrollerin ne kadarı MAUI'nin kendi kontrolü? Cevap “çoğu değil” ise üçüncü parti üretimi tek başına farkı kapatır.
- Yerelleştirme ve tema, ürününüzün gereksinimi mi? Öyleyse hazır gelmesiyle kendiniz kurmanız arasındaki fark haftalarla ölçülür.
- Kurumunuz bağımlılık seçiminde yönetişime bakıyor mu? Bakıyorsa .NET Foundation etiketi tartışmayı kısaltır.
Kapanış
İki kütüphane de aynı fikrin farklı ölçekte uygulanmış hâli: arayüzü C# ile, tipli ve refactor edilebilir biçimde yazmak. Toolkit bunu dar ve güvenli bir kümeyle yapıyor; FmgLib aynı fikri MAUI'nin tamamına, üçüncü parti kontrollere, temaya, yerelleştirmeye ve geliştirme döngüsüne kadar genişletiyor.
Hangisini seçerseniz seçin, XAML'den çıkıp arayüzü kodun kendisiyle ifade etmenin kazancı ortak: derleyici desteği, refactor güvenliği, tek bir dil. Gerisi projenizin ihtiyaç listesiyle ilgili — ve iyi haber şu ki, gördüğünüz gibi seçim geri dönülmez değil.