← blog

8 octobre 2026 · 7 min de lecture

Ce qui a atterri depuis août : 26.9.0, quatre nouveaux backends, thèmes CSS et l'audit de comportement

Cette page est une traduction. En cas de divergence, la version anglaise fait foi. English

Le dernier billet publié ici a été écrit à partir de la version 26.0.30. Depuis le 2026-08-17, le dépôt a reçu 557 commits et la version actuelle est 26.9.0. Voici un tour d’horizon pour les utilisateurs existants et ceux qui évaluent le projet : ce qui a changé, le degré de maturité de chaque élément, et la seule chose que vous devez faire lors de la mise à niveau.

Quatre nouveaux backends

Il y avait trois hôtes — Avalonia, Uno, Headless. Il y en a maintenant sept, qui exécutent tous le même jeu de contrôles ; la différence tient à qui crée la fenêtre et présente la surface Skia.

GTK 4 (Majorsilence.Forms.Gtk4) est un hôte pensé d’abord pour Linux, construit sur gir.core : une vraie Gtk.Window par formulaire, la boucle principale GLib, utilisable aussi sous Windows et macOS avec le runtime GTK 4. Sélectionnez-le explicitement avec Gtk4Application.Use (); avant Application.Run. L’intégration fonctionne dans les deux sens (ToGtkWidget(), ToGtkWindow()), NativeControlHost n’a pas de problème d’« airspace » parce que GTK 4 compose un seul arbre de rendu, et WebBrowser s’appuie sur WebKitGTK 6.0. Vérifié sous Wayland. Lacunes connues : pas de contrôle de la position à l’écran (GTK 4 a supprimé l’API), SetIcon(byte[]) est un no-op, les sélecteurs de fichiers natifs renvoient un résultat vide et des boîtes de dialogue de repli sont donc utilisées, facteur d’échelle entier uniquement, non analysé pour l’AOT.

Terminal (Majorsilence.Forms.Terminal, 2026-10-04) héberge un formulaire dans une console en vue unique — le formulaire remplit le terminal, sans barre de titre, comme sur un téléphone. Il utilise le protocole graphique Kitty ou Sixel à la résolution réelle en pixels lorsque c’est disponible, sinon des caractères de bloc Unicode ; la détection se fait en interrogeant le terminal, et MF_TERMINAL_GRAPHICS=halfblock|kitty|sixel fige un mode. La souris et le clavier fonctionnent ; Ctrl+C quitte toujours. Sélectionné avec TerminalApplication.Use (options);. Vérifié uniquement dans xterm et WezTerm. Pas de sélecteurs natifs, ni de NativeControlHost, ni de webview.

WinForms (Majorsilence.Forms.WinForms) est un backend de migration réservé à Windows : de vraies fenêtres System.Windows.Forms sur la pompe de messages Win32, Skia présenté via un bitmap GDI. Il existe pour que vous puissiez intégrer des contrôles Majorsilence.Forms dans une application WinForms existante, un contrôle à la fois — myMfControl.ToWinFormsControl(), myForm.ToWinFormsForm(), MajorsilenceFormsPresenter — puis basculer l’hôte vers Avalonia ou Uno une fois que tout est porté. Pas de gestes, pas d’IWebViewFactory. Il est distinct de l’ancien WindowsFormsInterop, qui fait le pont pour des formulaires entiers sur l’hôte Avalonia.

WPF (Majorsilence.Forms.Wpf) a la même forme et le même objectif pour les applications WPF : une vraie Window WPF, une présentation par WriteableBitmap, ToWpfElement() et ToWpfWindow(). Sélectionnez-le avec Platform.Backend = new Majorsilence.Forms.Wpf.WpfPlatformBackend ();.

Avalonia, WinForms et GTK 4 offrent de véritables boîtes de dialogue modales au niveau du système ; Uno ouvre une fenêtre indépendante, utilisez donc Form.ShowDialog(parent) sur cet hôte.

.NET Framework 4.8 et netstandard2.0

Les paquets cœur — Majorsilence.Forms, .Drawing.Common, .Telerik — ciblent désormais à la fois net8.0, net10.0 et netstandard2.0, et les backends WinForms et WPF ajoutent net48. Une application .NET Framework 4.8 peut donc héberger des contrôles Majorsilence.Forms sans passer d’abord à .NET moderne, ce qui supprime un problème d’ordonnancement que beaucoup de migrations rencontraient.

L’audit des écarts de comportement

Les deux plans de couverture de la surface d’API (WinForms et GDI+) sont à zéro : chaque membre présent en amont est déclaré. C’était de toute façon la moitié la moins intéressante. Un membre qui existe mais stocke une valeur que personne ne lit, ou déclenche un événement que personne ne lève, compile votre application migrée puis ne fait discrètement rien.

Le 2026-08-25, un audit en douze domaines a donc comparé chaque domaine à l’implémentation en amont et consigné 483 constats où le comportement différait. Depuis, les phases 0 à 4 et la plupart des familles de contrôles ont atterri. Éléments concrets désormais réels : la chaîne de prétraitement ProcessCmdKey, un point de passage unique pour le focus et la validation, la barre de titre hors de la zone cliente, AutoScaleMode.Font qui met réellement à l’échelle, la liaison de données vivante (CurrencyManager, BindingNavigator), ListView.View = Details, le cycle de vie du formulaire dans l’ordre en amont (Load → VisibleChanged → Activated, Shown posté), le réordonnancement des colonnes de DataGridView, Ctrl+Z dans les contrôles de texte, NotifyIcon dans la zone de notification, Application.AddMessageFilter, et la libération d’un formulaire qui libère ses contrôles.

La surface creuse est en outre mesurée maintenant : des fichiers de référence figent les méthodes no-op connues, les événements inertes et les propriétés simplement stockées, de sorte qu’en ajouter relève d’un acte conscient et non d’un accident. La politique des stubs est inchangée — no-op ou valeur par défaut, jamais de NotImplementedException.

Unités logiques : la seule chose que vous devez faire

Le 2026-10-01, ClientRectangle, ClientSize et le canvas de peinture (OnPaint, Paint, e.ClipRectangle, e.Canvas) sont passés en unités logiques, à l’image de Width/Height/Bounds et de MouseEventArgs. Le framework met le canvas à l’échelle de l’affichage pour vous.

Si un contrôle personnalisé appelait e.Graphics.ScaleTransform (e.Scaling, e.Scaling), supprimez cet appel — le dessin est maintenant mis à l’échelle deux fois. Les pixels physiques restent accessibles via la famille Scaled* (ScaledWidth, ScaledBounds, …), PaintEventArgs.Scaling et LogicalToDeviceUnits. La seule exception concerne les événements owner-draw (DrawItem, DrawNode, CellPainting), qui vous transmettent toujours des bornes en pixels physiques. Exécutez vos tests avec MF_HEADLESS_SCALE=2 pour attraper tout ce qui dépend de l’ancien comportement.

Navigateur et mobile

Sur net10.0-browser, -android et -ios, le backend Avalonia signale CanRunModalLoop = false, et les appels bloquants — Form.ShowDialog, MessageBox.Show, les sélecteurs de fichiers, TaskDialog.ShowDialog, VbInteraction.MsgBox/InputBox, RadMessageBox.Show — lèvent désormais une PlatformNotSupportedException nommant leur jumeau asynchrone avant que quoi que ce soit ne soit affiché, au lieu de se figer. Les formes asynchrones (ShowDialogAsync, MessageBox.ShowAsync, FileDialog.ShowDialogAsync, …) fonctionnent sur tous les hôtes ; l’idiome est donc un gestionnaire async void avec await. Un analyseur Roslyn dans le paquet cœur les repère à l’avance — MFB001 appel modal bloquant, MFB002 attente synchrone sur une Task, MFB003 Thread.Sleep, chacun avec un correctif de code — actif pour les TFM navigateur ou avec majorsilence_forms.browser_target = true dans .editorconfig.

La cible navigateur maintient aussi un DOM d’accessibilité à côté du canvas : un élément transparent et traversé par les clics par contrôle, avec rôle ARIA, nom, état et bornes, construit à partir de l’arbre d’automatisation propre au framework. Les lecteurs d’écran, la recherche dans la page et les outils de test fondés sur le DOM peuvent désormais voir l’interface.

Sur Android et iOS, le clavier à l’écran apparaît quand une TextBox prend le focus, TextBoxBase.InputKind choisit le type de clavier, les marges de zone sûre s’appliquent via Form.SafeAreaPadding, et WindowBase.BackRequested gère le bouton Retour d’Android. État honnête : Android a eu une première passe sur appareil réel (démarrage, touchers, mise à l’échelle du rendu, défilement tactile confirmés sur matériel) ; le clavier, la zone sûre et la rotation ne sont couverts que par des tests unitaires. La CI iOS compile la vraie tête et la lance dans un test de fumée sur simulateur, mais personne ne l’a exécutée de manière interactive et le job est toujours en continue-on-error.

Des contrôles de disposition de type téléphone sont arrivés en même temps : StackPanel, Card, RichListBox (lignes multilignes à modèle) et NavigationHost (une pile de pages avec un bouton Retour).

Thèmes CSS et Theme Studio

Les thèmes peuvent désormais s’écrire dans un sous-ensemble strict et documenté de CSS : @theme "Ocean" extends Dark;, des jetons :root à raison d’un par propriété de Theme, des règles par type de contrôle comme Button:hover { … }, des parties via Type::part. L’analyseur n’échoue jamais en silence — ThemeStyleSheet.Parse collecte des diagnostics. Chargez avec Theme.LoadFromCssFile, ou Theme.RegisterThemeCssFromFile + Theme.ApplyTheme ("Ocean") ; exportez avec Theme.ExportCss. Chaque contrôle, y compris la couche de compatibilité Telerik, a un sélecteur, et l’ancien XML <Theme> fonctionne toujours.

Deux paquets compagnons appliquent la même feuille à d’autres hôtes : Majorsilence.Forms.Theming.WinForms restyle de vrais contrôles System.Windows.Forms (Windows uniquement) et Majorsilence.Forms.Theming.Avalonia restyle les contrôles Fluent natifs d’Avalonia, si bien qu’un seul fichier CSS peut thémer une application de migration mixte. samples/ThemeStudio est un éditeur en direct avec aperçu et diagnostics ; des binaires précompilés sont attachés aux GitHub Releases.

MVVM, Essentials, animation

Majorsilence.Forms.Mvvm est un câblage compatible trimming et AOT au-dessus d’INotifyPropertyChanged et d’ICommand, sans réflexion ni dépendance à un toolkit : viewModel.Observe (nameof (VM.Count), vm => vm.Count, …), des liaisons bidirectionnelles BindText/BindChecked/BindSelectedIndex/BindValue, BindCommand, et un BindingScope pour tout libérer. Il fonctionne aux côtés de CommunityToolkit.Mvvm.

Majorsilence.Forms.Essentials garde les capacités propres à chaque plateforme hors du cœur : SecureStorage, la synthèse vocale Speech, Launcher.OpenAsync (http/https/mailto/tel/sms) et FileSystem.OpenAppPackageFileAsync. Tout se dégrade en no-op ; vérifiez IsSupported.

control.RequestAnimationFrame plus Majorsilence.Forms.Animation (Tween<T>, Easing, Animator) offrent une animation alignée sur l’affichage sous Avalonia et une horloge manuelle sous Headless.

Outillage

Notes de mise à niveau

Pour aller plus loin