.NET Hot Reload Desteği
FmgLib.MauiMarkup UI'ları düz C# olduğundan .NET'in yerleşik Hot Reload'u onlara uygulanır — kütüphane, kod değiştiğinde UI kurulumunuzu yeniden çalıştıran küçük bir handler ekler; edit'ler çalışan uygulamada yeniden başlatmadan görünür.
Desen: IFmgLibHotReload + Build()#
- Sayfada
IFmgLibHotReload'ı uygulayın (tek birvoid Build()metodu bildirir). - Constructor'da
this.InitializeHotReload()çağırın. - Tüm UI kurulumunu
Build()içine koyun.
public partial class ExamplePage : ContentPage, IFmgLibHotReload
{
public ExamplePage()
{
this.InitializeHotReload();
}
public void Build()
{
this
.Content(
new Label()
.Text("FmgLib.MauiMarkup")
.CharacterSpacing(2)
.FontSize(30)
.FontAttributes(FontAttributes.Italic)
.TextColor(Colors.Green)
.TextCenter()
);
}
}
InitializeHotReload() ne yapar#
Build()'i bir kez hemen çağırır (ilk kurulum).- Sayfayı hot-reload handler'ına her zaman kaydeder — weak reference ile (üretimde hiçbir güncelleme gelmediğinden no-op'tur ve sayfanın ömrünü asla uzatmaz). Runtime bir kod güncellemesi uyguladığında,
Build()her kayıtlı sayfa için ana thread'de yeniden çağrılır (varsayılanRebuildAllOnUpdate = true— pratikte tek güvenilir mod: sayfalar, edit'leri onları yenilemeli olan yardımcı sınıf/stilleri kompoze eder ve iOS/Android'deki Mono runtime çoğu zaman boş güncellenen-tip listesi bildirir). - Her güncelleme debug çıktısına bir tanılama satırı yazar:
FmgLib.MauiMarkup hot reload: update received (types: …) — rebuilding N registered target(s).— bu satırı görüyorsanız boru hattı uçtan uca çalışıyor demektir. - Reload sırasında fırlatan bir
Build()uygulamayı asla çökertmez: hata loglanır (Trace) veFmgLibHotReloadHandler.ReloadFailedolayıyla iletilir; böylece edit'i düzeltip tekrar kaydedersiniz.
Bellek güvenliği#
Hot-reload kaydı weak reference kullanır (FmgLibHotReloadHandler.Register): kayıt bir sayfanın ömrünü asla uzatmaz. Sayfa navigasyondan çıkarılıp serbest bırakıldığında normal şekilde çöp toplanır ve kaydı otomatik temizlenir — leak detector'lar (örn. Nalu'nunki) hot reload'a takılı sayfalar bildirmez. Aynı örneği iki kez kaydetmek no-op'tur; canlı bir sayfayı yeniden kurulumlardan çıkarmak isterseniz FmgLibHotReloadHandler.Unregister(page) de vardır.
Hazır Taban Sınıflar#
Kütüphane bunu sizin için bağlayan taban sayfalar sunar.
FmgLibContentPage#
public class HomePage : FmgLibContentPage
{
public override void Build() =>
this.Content(
new Label().Text("Merhaba!").Center()
);
}
Constructor tesisatı yok — taban sınıf hot reload'a kaydolur ve Build()'i çağırır.
FmgLibContentPage<TViewModel> — MVVM tabanı#
View model'i constructor'da alır, ilk Build() çalışmadan önce BindingContext'e atar (böylece Build() view model'i güvenle okuyabilir) ve — güzel kısmı — BindingContext özelliğini yeniden tipler; VM'inize cast olmadan erişirsiniz:
public class ProfilePage : FmgLibContentPage<ProfileViewModel>
{
public ProfilePage(ProfileViewModel vm) : base(vm) { }
public override void Build() =>
this.Content(
new VerticalStackLayout()
.Padding(20)
.Children(
new Label().Text(e => e.Getter(static (ProfileViewModel v) => v.UserName)),
new Button()
.Text("Yenile")
.Command(BindingContext.RefreshCommand) // tipli! cast yok
)
);
}
Dependency injection ile doğal eşleşir:
builder.Services.AddTransient<ProfileViewModel>();
builder.Services.AddTransient<ProfilePage>();
Reload-Güvenli Build() Kuralları#
Build() bir debug oturumunda birçok kez çalışabilir. Buna göre yapılandırın:
✅ Yapın
Build()'i idempotent yapın: her seferinde UI'yi sıfırdan tam olarak tanımlamalı (Contentayarlamak eski ağacı değiştirir, bu doğal stildir).Durumu (sayaç, view model, servis referansı)
Build()'te değil, constructor'da başlatılan alanlarda tutun:C# private readonly MainPageViewModel viewModel; public MainPage() { viewModel = new MainPageViewModel(); // reload'ları atlatır this.InitializeHotReload(); } public void Build() => this.BindingContext(viewModel).Content(/* ... */);
❌ Kaçının
- View model'i
Build()içinde oluşturmaktan — her reload'da uygulama durumunu sıfırlarsınız. - Uzun ömürlü/statik olaylara
Build()içinde abone olmaktan (örn.Application.Current.RequestedThemeChanged += …) — abonelikler her yeniden kurulumda birikir. Bunu constructor'da yapın. - Animasyon veya ağ çağrılarını doğrudan
Build()'te başlatmaktan;OnLoaded/OnAppearingolay işleyicilerini kullanın.
Yalnızca Sayfalar Değil, Her View'da Çalışır#
IFmgLibHotReload + InitializeHotReload() bir ContentView üzerinde de geçerlidir:
public class ProductCard : ContentView, IFmgLibHotReload
{
public ProductCard() => this.InitializeHotReload();
public void Build() => this.Content(/* ... */);
}
VS Code'da Tek Tuş Geliştirme Döngüsü (önerilen)#
fmglib-mauimarkup-app şablonundan oluşturulan projeler hazır bir .vscode/tasks.json ile gelir. Mevcut projeler için ekleyin:
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "🔥 Hot Reload: iOS Simulator",
"type": "shell",
"command": "dotnet",
"args": [ "watch", "run", "-f", "net10.0-ios" ],
"isBackground": true,
"problemMatcher": []
},
{
"label": "🔥 Hot Reload: Android",
"type": "shell",
"command": "dotnet",
"args": [ "watch", "run", "-f", "net10.0-android" ],
"isBackground": true,
"problemMatcher": []
}
]
}
Görevi çalıştırın (Terminal → Run Task…), Build()'i düzenleyin, kaydedin — değişiklik saniyeler içinde cihazda; terminalde hem dotnet watch 🔥 … applied hem kütüphanenin update received … rebuilding N target(s) onayı görünür. Breakpoint gerektiğinde F5 debug'ı ayrıca kullanın.
IDE / Kanal Destek Matrisi#
FmgLib'in handler'ı .NET Hot Reload (MetadataUpdateHandler) üzerine oturur; .NET araçlarının çalışan sürece kod güncellemesi iletebildiği her yerde çalışır:
| Kanal | Windows | macOS | Notlar |
|---|---|---|---|
| Visual Studio (F5 debug) | ✅ | — (emekli) | Tam destek, güncellemeler tip listesiyle iletilir |
| VS Code + C# Dev Kit / .NET MAUI eklentisi | ✅ | ✅ | Debug'da "csharp.experimental.debug.hotReload": true ayarı gerekir — onsuz F5 hiç güncelleme iletmez |
dotnet watch run (CLI, herhangi bir editör) |
✅ | ✅ | Debugger gerekmez — en IDE-bağımsız, en güvenilir yol |
| Rider (debugger) | ❌ | ❌ | Rider'ın debugger'ı MAUI için .NET Hot Reload iletmez — Rider içinde bir dotnet watch run configuration kullanın |
Düz dotnet run / Release build |
❌ | ❌ | Güncelleme kanalı yok — tasarım gereği, sıfır ek yük |
Öz-teşhis: debugger bağlıyken süreç güncelleme alamıyorsa kütüphane debug çıktısına tek seferlik bir uyarı yazar (
FmgLib.MauiMarkup hot reload: … MetadataUpdater.IsSupported = false …) ve hangi ayarı/kanalı düzelteceğinizi tam olarak söyler. Bu mesajı görüyorsanız, o oturumda ne değiştirirseniz değiştirin edit'ler uygulanmayacaktır.
Platform notları:
- Android emülatör/cihaz (debug): yukarıdaki tüm kanallar destekler.
- iOS / Mac Catalyst (debug): .NET Hot Reload, Mono interpreter'ını gerektirir; MAUI bunu Debug yapılandırmasında varsayılan açık tutar (
UseInterpreter). Kapattıysanız Debug için yeniden açın. - Çalışma zamanında desteği belirleyen
System.Reflection.Metadata.MetadataUpdater.IsSupported'tır (araçlar başlatırkenDOTNET_MODIFIABLE_ASSEMBLIES=debugayarlar).FmgLibHotReloadHandler.IsSupported'ı kendiniz de kontrol edebilirsiniz (örn. yalnızca-geliştirme banner'ı göstermek için).
Handler Seçenekleri ve Tanılama#
// Varsayılan TRUE: her güncellemede tüm kayıtlı sayfalar yeniden kurulur. Hedefli
// yeniden kuruluma (yalnızca kendi/taban tipi değişen sayfalar) geçmek isterseniz:
FmgLibHotReloadHandler.RebuildAllOnUpdate = false;
// Reload hatalarını gözlemleyin (zaten Trace ile loglanır; uygulamayı asla çökertmez):
FmgLibHotReloadHandler.ReloadFailed += (target, ex) =>
Console.WriteLine($"{target.GetType().Name} için hot reload başarısız: {ex.Message}");
// Kurtarma çıkışı: TÜM kayıtlı sayfaları ŞİMDİ zorla yeniden kur — kodu uygulayan ama
// runtime handler'larını çağırmayan araçlar için. Yalnızca-debug bir jeste/butona bağlayın:
FmgLibHotReloadHandler.RebuildAll();
Örnek — bir sayfada yalnızca-debug yenileme jesti:
#if DEBUG
this.GestureRecognizers(
new TapGestureRecognizer()
.NumberOfTapsRequired(3)
.OnTapped((s, e) => FmgLibHotReloadHandler.RebuildAll()));
#endif
Hedefli-mod eşleşme kuralları: bir güncelleme, runtime kayıtlı hedefin tam tipini, taban tiplerinden herhangi birini (ortak taban sayfa düzenlemek tüm türemişleri yeniler), veya bilinmeyen/boş bir tip listesi bildirdiğinde yeniden kurar.
Yalnızca
IFmgLibHotReloaduygulayan tipler (veInitializeHotReloadçağıran, veyaFmgLibContentPage'ten türeyenler) kendini yeniden kurar.AppShell'inize veya düz birContentView'a yaptığınız edit görünmüyorsa, o tipe de aynı deseni uygulayın.
Sorun Giderme#
| Belirti | Neden / çözüm |
|---|---|
| Edit'ler görünmüyor (VS Code, debug) | Önce Debug Console'da FmgLib.MauiMarkup hot reload: update received … satırını arayın. Satır yoksa, eklenti değişiklikleri uygulamanın metadata update handler'larını bildirmeden uygulamıştır (Mono/mobil hedeflerde debug-launch kanalının bilinen bir boşluğu — Hot Reload result: {"result":0, …} tüm diziler boşken bir belirtidir). Kütüphaneden düzeltilemez: dotnet watch run ile iterasyon yapın (tam destekli), veya uygulanmış edit'leri render etmek için yalnızca-debug bir jestten FmgLibHotReloadHandler.RebuildAll() tetikleyin. Ayrıca "csharp.experimental.debug.hotReload": true ayarlı olsun. |
| Edit'ler görünmüyor (Rider, debug) | Rider'ın debugger'ı MAUI uygulamalarına .NET Hot Reload uygulayamaz. Bir dotnet watch run -f <tfm> run configuration oluşturun (emülatör/simülatörle çalışır) — handler'ımız watch'ı debugger'sız destekler. |
Yardımcı ContentView/metot düzenlemesi onu kullanan sayfayı yenilemiyor |
Ya bileşende IFmgLibHotReload'ı uygulayın (kendini yeniden kurar), ya da FmgLibHotReloadHandler.RebuildAllOnUpdate = true bırakın (varsayılan). |
| Edit'te UI durumu sıfırlanıyor | Durum Build() içinde yaşıyor; alanlara/constructor'a taşıyın. |
| Edit sonrası olaylar iki kez tetikleniyor | Build() içinde uzun ömürlü bir nesneye handler bağlanmış; constructor'a taşıyın. |
| IDE'den "rude edit" mesajları | Bazı kod değişiklikleri (metot imzası değiştirmek, bazı tiplere alan eklemek) .NET Hot Reload'un kapasitesini aşar — oturumu yeniden başlatın. Bu bir runtime/araç sınırıdır, kütüphane değil. |
| iOS cihaz edit'leri yok sayılıyor | Debug'ın Mono interpreter'ını kullandığından (MAUI varsayılanı) ve oturumun IDE/watch tarafından başlatıldığından emin olun. |