WinForms en WPF naar Avalonia met dezelfde sgcUI-API

sgcUI levert native besturingselementen voor WinForms, WPF en Avalonia die één model per product delen. De gridkolommen, de grafiek, de plannerdata en het thema worden één keer geschreven en compileren ongewijzigd voor elke host. Een scherm naar Avalonia verplaatsen, en daarmee naar macOS en Linux, betekent dus de venstercode en de besturingselementtypen wijzigen, niet de schermlogica. Deze kit laat zien hoe, met code uit een voorbeeld dat compileert en zijn zelftest op alle drie de hosts doorstaat.

Een Windows-desktopapp die op macOS en Linux moet draaien

Bedrijfsschermen met grids, grafieken en schedulers, een datalaag die je wilt behouden, en geen zin om de schermlogica in een nieuw UI-framework te herschrijven.

Vanaf WinForms

WinForms draait alleen op Windows. Met sgcUI hebben de TsgcWinForms*-besturingselementen die je nu gebruikt TsgcAvalonia*-tegenhangers met dezelfde leden. De configuratiecode verhuist naar een gedeelde klassenbibliotheek, het formulier wordt een Avalonia-venster, en DataSource wordt ItemsSource.

Vanaf WPF

WPF draait ook alleen op Windows. De sgcUI-besturingselementen ervan komen het dichtst bij Avalonia: beide binden met ItemsSource, beide worden in XAML gedeclareerd met dezelfde sgc-namespace-URI, en de aanroep naar de UI-thread is de belangrijkste codewijziging.

De gebruikelijke route voor een bestaande WPF-applicatie is Avalonia XPF, dat zijn leverancier beschrijft als "a binary-compatible WPF runtime" en "a fork of WPF that maintains API and binary compatibility across the managed surface area". Op het moment van schrijven (2026-10-20) wordt XPF verkocht als eeuwigdurende licenties per applicatie met twaalf maanden updates en support: Internal voor EUR 9,500 per app (alleen uitrol naar je eigen medewerkers), Business voor EUR 29,500 per app (commerciële uitrol naar macOS en desktop-Linux, embedded Linux is een toevoeging van EUR 21,250), en Enterprise vanaf EUR 124,500 per app voor mobiel of de browser. WPF-suites van derden kunnen onder voorwaarden op XPF draaien: de notities over bibliotheken van derden bij XPF zeggen dat Win32-API-shims "required for DevExpress controls" zijn, dat sommige DevExpress-besturingselementen afhangen van System.Drawing.Common (GDI+), "which is deprecated on non-Windows platforms", dat DevExpress-besturingselementen op Linux libgdiplus vereisen, en dat "Issues most commonly arise with controls that use GDI or WinForms components". Avalonia zelf is een gratis, MIT-gelicentieerd UI-framework. sgcUI voor .NET wordt per ontwikkelaar gelicentieerd, vanaf €380 voor de .NET Standard-editie, met volledige C#-broncode voor WinForms, WPF en Avalonia, gratis herdistributie van binaries en geen vergoeding per applicatie. Gratis Community-pakketten staan op NuGet.

Eén model, drie dunne hosts

Elk sgcUI-product heeft een model dat niets van de host weet. De WinForms-, WPF- en Avalonia-besturingselementen zijn dunne weergaven erbovenop.

Gedeelde modellen

TsgcGridColumns, TsgcGridData, TsgcChart, TsgcPlannerData, TsgcPlannerSchedulerOptions, TsgcPivotEngine en TsgcDiagramDocument zijn op de drie hosts dezelfde typen. Code die ertegen is geschreven compileert ongewijzigd voor elke host.

Dezelfde lidnamen

Columns, Data, ShowFooter, GroupSummaries, GroupBy, SaveLayoutToString, LoadLayoutFromString, Chart, Options, View, Date, PrintSettings, CreatePrintSource, Print, ShowPrintPreview en ExportToPDF, plus de grid-events zoals OnCellClick, OnSelectionChange en OnSortChange.

Dezelfde opgeslagen indeling

Een met SaveLayoutToString opgeslagen gridindeling is op elke host dezelfde JSON-tekst. In het voorbeeld schrijven de drie hosts hun indeling naar een bestand en de drie bestanden hebben één SHA-256. Thema's (TsgcUITheme, sgcUIApplication.Theme, sgcUIApplication.AccentColor, hoog contrast) worden ook gedeeld.

Het hostbesturingselement van elk product

Vuistregel: vervang het voorvoegsel TsgcWinForms of TsgcWpf door TsgcAvalonia. De uitzonderingen staan in de tabel gemarkeerd.

Product WinForms WPF Avalonia
sgcGridTsgcWinFormsGridTsgcWpfGridTsgcAvaloniaGrid
sgcGrid gedeclareerde kolomin code, Columns.AddTsgcWpfGridColumn (XAML)TsgcAvaloniaGridColumn (XAML)
sgcChartsTsgcWinFormsChart, TsgcWinFormsChartEditorTsgcWpfChart, TsgcWpfChartEditorTsgcAvaloniaChart, TsgcAvaloniaChartEditor
sgcPivotGridTsgcWinFormsPivotGrid, TsgcWinFormsPivotFieldListTsgcWpfPivotGrid, TsgcWpfPivotFieldListTsgcAvaloniaPivotGrid, TsgcAvaloniaPivotFieldList
sgcPlannerTsgcWinFormsScheduler, TsgcWinFormsGantt, TsgcWinFormsKanbanTsgcWpfScheduler, TsgcWpfGantt, TsgcWpfKanbanTsgcAvaloniaScheduler, TsgcAvaloniaGantt, TsgcAvaloniaKanban
sgcDiagramTsgcWinFormsDiagramView, TsgcWinFormsDiagramPalette, TsgcWinFormsDiagramMinimapTsgcWpfDiagramView, TsgcWpfDiagramPalette, TsgcWpfDiagramMinimapTsgcAvaloniaDiagramView, TsgcAvaloniaDiagramPalette, TsgcAvaloniaDiagramMinimap
sgcGaugesTsgcWinForms<Kind>GaugeTsgcWpf<Kind>GaugeTsgcAvalonia<Kind>Gauge
Kind is Radial, Linear, Digital, Compass, Clock, LED, LEDBar, ProgressRing, VUMeter of Annunciator.
sgcDashboardTsgcWinFormsDashboardTsgcWpfDashboardTsgcAvaloniaDashboard
sgcDashboard-tegelsTsgcWinFormsDashboardTile, ...ChartTile, ...GridTile, ...GaugeTile, ...PivotTile, TsgcWinFormsKPICard en TsgcWinFormsDashboardMapTileTsgcWpfDashboardTile, ...ChartTile, ...GridTile, ...GaugeTile, ...PivotTile, TsgcWpfKPICard. Geen kaarttegel.TsgcAvaloniaDashboardTile, ...ChartTile, ...GridTile, ...GaugeTile, ...PivotTile, TsgcAvaloniaKPICard. Geen kaarttegel.
sgcMapsSgcMapControl (namespace esegece.sgcMaps.WinForms)TsgcWpfMapTsgcAvaloniaMap
sgcEditorsTsgcWinForms<Editor>TsgcWpf<Editor>TsgcAvalonia<Editor>
Editor is ButtonEdit, CurrencyEdit, MaskEdit, SpinEdit, DatePicker, DateTimePicker, TimePicker, LookupEdit, SearchEdit, TokenEdit, Memo, ColorPicker, MonthCalendar, RadioGroup, CheckGroup, Rating of RangeSlider.
sgcNavigationTsgcWinFormsNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManagerTsgcWpfNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManagerTsgcAvaloniaNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManager
sgcToolbarTsgcWinFormsRibbon, TsgcWinFormsToolbar, TsgcWinFormsStatusBarTsgcWpfRibbon, TsgcWpfToolbar, TsgcWpfStatusBarTsgcAvaloniaRibbon, TsgcAvaloniaToolbar, TsgcAvaloniaStatusBar
sgcWizardTsgcWinFormsWizard, TsgcWinFormsStepperTsgcWpfWizard, TsgcWpfStepperTsgcAvaloniaWizard, TsgcAvaloniaStepper
sgcRichEditTsgcWinFormsRichEditTsgcWpfRichEditTsgcAvaloniaRichEdit
sgcPDF-viewerTsgcPDFViewerControlTsgcWpfPDFViewerTsgcAvaloniaPDFViewer
sgcReports-ontwerperTsgcWinFormsReportDesignerTsgcWpfReportDesignerTsgcAvaloniaReportDesigner
sgcReports-voorbeeldTsgcReportPreviewVCL (een WinForms-Panel)TsgcWpfReportPreviewTsgcAvaloniaReportPreview
sgcUICoreTsgcWinFormsAIQueryBar, TsgcWinFormsValidationSummaryTsgcWpfAIQueryBar, TsgcWpfValidationSummaryTsgcAvaloniaAIQueryBar, TsgcAvaloniaValidationSummary

De namespace is er één per product (esegece.sgcGrid, esegece.sgcCharts, ...) en voor de drie hosts dezelfde, behalve de WinForms-kaart, die in esegece.sgcMaps.WinForms staat. De hostbesturingselementen zitten in de pakketten esegece.<Product>.WinForms, .Wpf en .Avalonia; de WinForms-kaarttegel van het dashboard zit in esegece.sgcDashboard.Maps.WinForms. Op WPF en Avalonia wordt elk product behalve sgcReports in XAML gedeclareerd met één namespace-URI, xmlns:sgc="https://www.esegece.com/sgcui"; gebruik voor sgcReports een clr-namespace-mapping.

De leden die bij een port veranderen

Al het andere aan de besturingselementen behoudt zijn naam. Dit zijn de plekken waar de host doorschemert.

Onderwerp WinForms WPF Avalonia
Een lijst aan het grid bindenDataSource (en DataMember)ItemsSource (DependencyProperty)ItemsSource (StyledProperty)
Grid-bindingsledenAutoGenerateColumns, KeyMember, SelectedItem, ItemsBinding en ItemsWriteBack (geregistreerd), op de drie hosts hetzelfde
Synchronisatie van het huidige itemgeenIsSynchronizedWithCurrentItemgeen
Gridkolommen en dataColumns (TsgcGridColumns), Data (TsgcGridData)
GrafiekmodelChart (TsgcChart)
PlannermodelData (TsgcPlannerData), Options (TsgcPlannerSchedulerOptions)
Themabesturingselement Theme (TsgcUITheme; utDefault volgt sgcUIApplication.Theme)hetzelfde, plus sgcUICore_Theme_Avalonia.sgcUIApplyThemeToWindow
UI-threadControl.BeginInvokeDispatcher.BeginInvokeDispatcher.UIThread.Post
Start van de applicatieApplication.Run(form)new Application().Run(window)een Application-klasse met een thema, en StartWithClassicDesktopLifetime
Doelframework van de appnet8.0-windows (ook net472)net8.0-windows (ook net472)net8.0, net9.0, net10.0

HOST-regels en SAME-regels

Elk fragment is gekopieerd uit het voorbeeld MigrateToAvalonia, dat in Release compileert voor de geregistreerde en Community-edities met 0 waarschuwingen en zijn zelftest doorstaat op WinForms, WPF en Avalonia. In de hostbestanden verschilt een regel gemarkeerd met HOST tussen de drie hosts en is een regel gemarkeerd met SAME in alle drie identiek.

Eén keer geschreven: de gedeelde laag

De gedeelde klassenbibliotheek verwijst alleen naar de modelpakketten esegece.sgcGrid, esegece.sgcCharts, esegece.sgcPlanner en esegece.sgcUICore, nooit naar een hostpakket. De gridkolommen, met groeperen en samenvattingen, worden geconfigureerd tegen TsgcGridColumns:

Shared\TMigrateModel.cs
// the grid columns, grouped by Region, with a sum of Amount in the
// footer and in every group row; TsgcGridColumns is the Columns of
// TsgcWinFormsGrid, TsgcWpfGrid and TsgcAvaloniaGrid
public static void ConfigureGrid(TsgcGridColumns aColumns)
{
    aColumns.BeginUpdate();
    try
    {
        aColumns.Clear();
        aColumns.Add(nameof(TOrder.Id), "Order", TsgcGridDataType.gdtInteger).Width = 80;
        aColumns.Add(nameof(TOrder.Customer), "Customer").Width = 180;
        aColumns.Add(nameof(TOrder.Region), "Region").GroupIndex = 0;
        TsgcGridColumn oDate = aColumns.Add(nameof(TOrder.Date), "Date", TsgcGridDataType.gdtDateTime);
        oDate.Width = 110;
        oDate.DisplayFormat = "yyyy-mm-dd";
        TsgcGridColumn oAmount = aColumns.Add(nameof(TOrder.Amount), "Amount", TsgcGridDataType.gdtFloat);
        oAmount.Width = 120;
        oAmount.DisplayFormat = "#,##0.00";
        oAmount.SummaryType = TsgcGridSummaryType.gsuSum;
        oAmount.SummaryFormat = "\"Total: \"#,##0.00";
        aColumns.Add(nameof(TOrder.Status), "Status").Width = 100;
    }
    finally
    {
        aColumns.EndUpdate();
    }
}

De grafiek is een TsgcChart-model, de cirkelreeks wordt live gevoed vanuit een ObservableCollection via TsgcChartDataSource_Items:

Shared\TMigrateModel.cs
// the chart model: one pie series fed by RegionTotals through the
// items data source (live: a changed Amount updates its slice)
Chart = new TsgcChart();
Chart.Title.Text = "Amount by region";
Chart.Title.Visible = true;
Chart.Legend.Position = TsgcChartLegendPosition.clpTop;
Pie = new TsgcChartSeries_Pie();
Pie.Title = "Amount";
Pie.Chart = Chart;
FChartSource = new TsgcChartDataSource_Items();
FChartSource.Series = Pie;
FChartSource.LabelMember = nameof(TRegionTotal.Region);
FChartSource.YMember = nameof(TRegionTotal.Amount);
FChartSource.Items = RegionTotals;

Eén thema voor elk sgcUI-besturingselement van de applicatie:

Shared\TMigrateModel.cs
// the theme of every sgcUI control of the application (their Theme is
// utDefault, so they follow sgcUIApplication.Theme), and one accent
public static void ApplyTheme()
{
    sgcUIApplication.Theme = TsgcUITheme.utLight;
    sgcUIApplication.AccentColor = 0xFF2563EB;
}

De enige hostservice: posten naar de UI-thread

Gebonden collecties veranderen op de UI-thread. De gedeelde laag kent de host niet, dus ze ontvangt de post-aanroep als delegate:

Shared\TMigrateModel.cs
// aPost runs an action on the UI thread; it is the only host specific
// service of the layer (WinForms Control.BeginInvoke, WPF
// Dispatcher.BeginInvoke, Avalonia Dispatcher.UIThread.Post)
public TMigrateModel(Action<Action> aPost)
Shared\TMigrateModel.cs
// a feed on a worker thread: the order is built there and posted to the
// UI thread with aPost, because bound collections change on the UI thread
public Task AddOrderFromWorker()
{
    return Task.Run(() =>
    {
        TOrder oOrder = DoNewOrder();
        FPost(() => DoAdd(oOrder));
    });
}

De besturingselementvelden verschillen alleen in type

Voor: de WinForms- of WPF-typen. Na: de Avalonia-typen.

WinForms\TMigrateForm.cs
// HOST: the WinForms control types
private readonly TsgcWinFormsGrid FGrid;
private readonly TsgcWinFormsChart FChart;
private readonly TsgcWinFormsScheduler FScheduler;
Wpf\TMigrateWindow.cs
// HOST: the WPF control types
private readonly TsgcWpfGrid FGrid;
private readonly TsgcWpfChart FChart;
private readonly TsgcWpfScheduler FScheduler;
Avalonia\TMigrateWindow.cs
// HOST: the Avalonia control types
private readonly TsgcAvaloniaGrid FGrid;
private readonly TsgcAvaloniaChart FChart;
private readonly TsgcAvaloniaScheduler FScheduler;

Het scherm binden: op elke host hetzelfde blok

Twee regels zijn gemarkeerd met HOST: de aanroep naar de UI-thread en de lijstbinding. De rest is in de drie bestanden dezelfde tekst.

WinForms\TMigrateForm.cs
// HOST: WinForms reaches the UI thread with Control.BeginInvoke
FModel = new TMigrateModel(aAction => BeginInvoke(aAction));

// SAME on every host: the shared columns, footer and group sums
TMigrateModel.ConfigureGrid(FGrid.Columns);
FGrid.ShowFooter = true;
FGrid.GroupSummaries = true;
// HOST: WinForms binds with DataSource (WPF and Avalonia: ItemsSource)
FGrid.DataSource = FModel.Orders;
// SAME on every host: the shared chart model and planner data
FChart.Chart = FModel.Chart;
TMigrateModel.ConfigureScheduler(FScheduler.Options);
FScheduler.Data = FModel.Planner;
FScheduler.View = TsgcPlannerSchedulerViewKind.psvWorkWeek;
FScheduler.Date = TMigrateModel.CS_WEEK;
Wpf\TMigrateWindow.cs
// HOST: WPF reaches the UI thread with Dispatcher.BeginInvoke
FModel = new TMigrateModel(aAction => Dispatcher.BeginInvoke(aAction));

// SAME on every host: the shared columns, footer and group sums
TMigrateModel.ConfigureGrid(FGrid.Columns);
FGrid.ShowFooter = true;
FGrid.GroupSummaries = true;
// HOST: WPF binds with ItemsSource (WinForms: DataSource)
FGrid.ItemsSource = FModel.Orders;
// SAME on every host: the shared chart model and planner data
FChart.Chart = FModel.Chart;
TMigrateModel.ConfigureScheduler(FScheduler.Options);
FScheduler.Data = FModel.Planner;
FScheduler.View = TsgcPlannerSchedulerViewKind.psvWorkWeek;
FScheduler.Date = TMigrateModel.CS_WEEK;
Avalonia\TMigrateWindow.cs
// HOST: Avalonia reaches the UI thread with Dispatcher.UIThread.Post
FModel = new TMigrateModel(aAction => Dispatcher.UIThread.Post(aAction));

// SAME on every host: the shared columns, footer and group sums
TMigrateModel.ConfigureGrid(FGrid.Columns);
FGrid.ShowFooter = true;
FGrid.GroupSummaries = true;
// HOST: Avalonia binds with ItemsSource, as WPF (WinForms: DataSource)
FGrid.ItemsSource = FModel.Orders;
// SAME on every host: the shared chart model and planner data
FChart.Chart = FModel.Chart;
TMigrateModel.ConfigureScheduler(FScheduler.Options);
FScheduler.Data = FModel.Planner;
FScheduler.View = TsgcPlannerSchedulerViewKind.psvWorkWeek;
FScheduler.Date = TMigrateModel.CS_WEEK;

De start van de applicatie

De startcode is van nature hostcode. De thema-aanroep is op de drie hosts hetzelfde.

WinForms\Program.cs
// HOST: the WinForms application start
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// SAME on every host: one theme for every sgcUI control
TMigrateModel.ApplyTheme();
TMigrateForm oForm = new TMigrateForm(vTest ? vFolder : null);
Application.Run(oForm);
return oForm.ExitCode;
Wpf\Program.cs
// HOST: the WPF application start
Application oApp = new Application { ShutdownMode = ShutdownMode.OnMainWindowClose };
// SAME on every host: one theme for every sgcUI control
TMigrateModel.ApplyTheme();
TMigrateWindow oWindow = new TMigrateWindow(vTest ? vFolder : null);
oApp.Run(oWindow);
return oWindow.ExitCode;
Avalonia\Program.cs
// HOST: Avalonia needs an Application class with a theme
internal sealed class TMigrateApp : Application
{
    public static string TestFolder;

    public override void Initialize()
    {
        Styles.Add(new FluentTheme());
        RequestedThemeVariant = ThemeVariant.Light;
    }

    public override void OnFrameworkInitializationCompleted()
    {
        // SAME on every host: one theme for every sgcUI control
        TMigrateModel.ApplyTheme();
        IClassicDesktopStyleApplicationLifetime oDesktop =
            ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
        if (oDesktop != null)
            oDesktop.MainWindow = new TMigrateWindow(TestFolder);
        base.OnFrameworkInitializationCompleted();
    }
}

Acht stappen per scherm

De Windows-app blijft na elke stap werken, dus de overstap kan scherm voor scherm.

1

Haal de gedeelde laag eruit

Verplaats de data en de configuratie van de besturingselementen naar een klassenbibliotheek die alleen naar de sgcUI-modelpakketten verwijst (esegece.sgcGrid, esegece.sgcCharts, esegece.sgcPlanner, esegece.sgcUICore). Schrijf haar tegen TsgcGridColumns, TsgcChart, TsgcPlannerData en TsgcPlannerSchedulerOptions.

2

Roep haar aan vanuit Windows

Laat het bestaande WinForms- of WPF-scherm die bibliotheek aanroepen. Het blijft op Windows werken zoals voorheen.

3

Maak de Avalonia-app

Een net8.0-project met Avalonia.Desktop, Avalonia.Themes.Fluent en de esegece.<Product>.Avalonia-pakketten die je nodig hebt.

4

Wissel de besturingselementtypen om

TsgcWinForms* en TsgcWpf* worden TsgcAvalonia*, met de uitzonderingen uit de mappingtabel.

5

Port het venster

Een Form met een TableLayoutPanel, of een WPF-Window met een Grid, wordt een Avalonia-Window met een Grid. WPF-XAML verhuist naar .axaml en behoudt de sgc-namespace-URI.

6

Alleen WinForms: de binding

Wijzig DataSource in ItemsSource. WPF-code gebruikt al ItemsSource.

7

De aanroep naar de UI-thread

BeginInvoke of Dispatcher.BeginInvoke wordt Dispatcher.UIThread.Post.

8

Controleer elke host

Voer op elke host dezelfde controles uit, zoals /selftest van het voorbeeld doet, en vergelijk de opgeslagen JSON van de gridindeling: die moet dezelfde tekst zijn.

Wat niet één op één past

Lees dit voordat je de overstap plant.

MigrateToAvalonia

Eén scherm met een grid van 40 bestellingen gegroepeerd per regio, een cirkeldiagram van het bedrag per regio en een werkweekscheduler, getoond door drie hosts vanuit één gedeelde laag.

Waar staat hij

In de sgcUI .NET-demo's, map Suite\MigrateToAvalonia van de oplossing sgcUI_Demos.sln: Shared, WinForms, Wpf, Avalonia en een readme.html. In de demo-download verwijzen de projecten naar de sgcUI NuGet-pakketten.

Zo draai je hem

dotnet build Avalonia\sgcUIMigrate.Avalonia.csproj -c Release, en hetzelfde voor WinForms\sgcUIMigrate.WinForms.csproj en Wpf\sgcUIMigrate.Wpf.csproj. Voeg -p:SgcCommunity=true toe voor de Community-editie. De Avalonia-host draait ook op macOS en Linux.

Zelftest

sgcUIMigrate.<Host>.exe /selftest [folder] controleert de rijen, het groeperen, de taart, de scheduler, een bestelling die op een worker-thread is toegevoegd en een verwijderde bestelling, schrijft migrate_selftest_<host>.txt en migrate_layout_<host>.json, en eindigt met het aantal mislukte controles. Alle 13 controles slagen op de drie hosts, en de drie indelingsbestanden zijn byte voor byte gelijk.

Om beide routes recht te doen: XPF wil een bestaande WPF-applicatie op macOS en Linux laten draaien met code en besturingselementen grotendeels zoals ze zijn, en sgcUI doet dat niet. Met sgcUI port je de venstercode van elk scherm naar Avalonia, en de sgcUI-onderdelen van het scherm gaan ongewijzigd mee. Schermen die op besturingselementen van andere leveranciers zijn gebouwd, moeten die eerst op Windows laten vervangen voor de overstap. sgcUI is de weg als je schermen het sgcUI-grid, de grafieken, planner, draaitabel, het diagram en de andere producten hierboven gebruiken of ernaartoe kunnen, en je native Avalonia-besturingselementen wilt met volledige broncode en geen runtimelaag ertussen. Zie de productpagina van sgcUI voor de producten en edities.

De beste deal: All-AccessElk eSeGeCe-product, inclusief Premium-ondersteuning, vanaf €1,059 per jaar.
Bekijk de All-Access-prijzen

Breng je eerste scherm vandaag over

Voeg de gratis Community-pakketten van NuGet toe, of de geregistreerde pakketten voor elke functie en volledige broncode, en port je eerste scherm met het voorbeeld MigrateToAvalonia ernaast.