WinForms と WPF から Avalonia へ、同じ sgcUI API で

sgcUI は、製品ごとに 1 つのモデルを共有する WinForms、WPF、Avalonia 用のネイティブコントロールを提供します。グリッドの列、チャート、プランナーのデータ、テーマは一度だけ記述すれば、どのホストでもそのままコンパイルできます。そのため、画面を Avalonia へ移し、macOS や Linux でも動かすために変更するのは、ウィンドウのコードとコントロールの型であり、画面のロジックではありません。このキットでは、3 つのホストすべてでコンパイルでき、セルフテストに合格するサンプルのコードを使って、その方法を示します。

macOS と Linux でも動かす必要がある Windows デスクトップアプリ

グリッド、チャート、スケジューラーを備えた業務画面があり、データ層は維持したまま、新しい UI フレームワークで画面のロジックを書き直したくない場合です。

WinForms から

WinForms は Windows でしか動作しません。sgcUI では、現在お使いの TsgcWinForms* コントロールに、同じメンバーを持つ TsgcAvalonia* の対応コントロールがあります。設定コードは共有クラスライブラリに移し、フォームは Avalonia のウィンドウになり、DataSource は ItemsSource になります。

WPF から

WPF も Windows でしか動作しません。sgcUI の WPF コントロールは Avalonia に最も近い構成です。どちらも ItemsSource でバインドし、どちらも同じ sgc 名前空間 URI で XAML に宣言し、主なコードの変更点は UI スレッドの呼び出しです。

既存の WPF アプリケーションの通常の移行手段は Avalonia XPF です。ベンダーの説明では、「バイナリ互換の WPF ランタイム」であり、「マネージド領域全体で API とバイナリの互換性を保つ WPF のフォーク」とされています。執筆時点 (2026-10-20) の XPF は、アプリケーションごとの永続ライセンスで、12 か月分のアップデートとサポートが付属して販売されています。Internal はアプリあたり EUR 9,500 (自社の従業員への配布のみ)、Business はアプリあたり EUR 29,500 (macOS とデスクトップ Linux への商用配布。組み込み Linux は EUR 21,250 の追加オプション)、Enterprise はモバイルまたはブラウザー向けでアプリあたり EUR 124,500 からです。サードパーティ製の WPF スイートも条件付きで XPF 上で動作します。XPF のサードパーティライブラリに関する注記には、Win32 API のシムが「DevExpress コントロールに必要」であること、一部の DevExpress コントロールが System.Drawing.Common (GDI+) に依存していて、これは「Windows 以外のプラットフォームでは非推奨」であること、Linux 上の DevExpress コントロールには libgdiplus が必要であること、そして「問題は GDI や WinForms コンポーネントを使うコントロールで最も多く発生する」ことが書かれています。Avalonia 自体は無償の MIT ライセンスの UI フレームワークです。.NET 版の sgcUI は開発者ごとのライセンスで、.NET Standard エディションは €380 から提供され、WinForms、WPF、Avalonia の完全な C# ソースコードが付属し、バイナリの再配布は無償で、アプリケーションごとの料金はありません。無償の Community パッケージは NuGet で公開されています。

1 つのモデルと 3 つの薄いホスト

sgcUI の各製品には、ホストについて何も知らないモデルがあります。WinForms、WPF、Avalonia のコントロールは、そのモデルの上に載る薄いビューです。

共有モデル

TsgcGridColumns、TsgcGridData、TsgcChart、TsgcPlannerData、TsgcPlannerSchedulerOptions、TsgcPivotEngine、TsgcDiagramDocument は 3 つのホストで同じ型です。これらに対して書いたコードは、どのホストでもそのままコンパイルできます。

同じメンバー名

Columns、Data、ShowFooter、GroupSummaries、GroupBy、SaveLayoutToString、LoadLayoutFromString、Chart、Options、View、Date、PrintSettings、CreatePrintSource、Print、ShowPrintPreview、ExportToPDF、さらに OnCellClick、OnSelectionChange、OnSortChange などのグリッドイベントです。

同じ保存レイアウト

SaveLayoutToString で保存したグリッドのレイアウトは、どのホストでも同じ JSON テキストです。サンプルでは 3 つのホストがレイアウトをファイルに書き出し、3 つのファイルの SHA-256 は同一です。テーマ (TsgcUITheme、sgcUIApplication.Theme、sgcUIApplication.AccentColor、ハイコントラスト) も共有されます。

各製品のホストコントロール

目安として、TsgcWinForms または TsgcWpf の接頭辞を TsgcAvalonia に置き換えます。例外は表に示しています。

製品 WinForms WPF Avalonia
sgcGridTsgcWinFormsGridTsgcWpfGridTsgcAvaloniaGrid
sgcGrid の宣言済み列コードで 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 は Radial、Linear、Digital、Compass、Clock、LED、LEDBar、ProgressRing、VUMeter、Annunciator のいずれかです。
sgcDashboardTsgcWinFormsDashboardTsgcWpfDashboardTsgcAvaloniaDashboard
sgcDashboard のタイルTsgcWinFormsDashboardTile、...ChartTile、...GridTile、...GaugeTile、...PivotTile、TsgcWinFormsKPICard、TsgcWinFormsDashboardMapTileTsgcWpfDashboardTile、...ChartTile、...GridTile、...GaugeTile、...PivotTile、TsgcWpfKPICard。マップタイルはありません。TsgcAvaloniaDashboardTile、...ChartTile、...GridTile、...GaugeTile、...PivotTile、TsgcAvaloniaKPICard。マップタイルはありません。
sgcMapsSgcMapControl (名前空間 esegece.sgcMaps.WinForms)TsgcWpfMapTsgcAvaloniaMap
sgcEditorsTsgcWinForms<Editor>TsgcWpf<Editor>TsgcAvalonia<Editor>
Editor は ButtonEdit、CurrencyEdit、MaskEdit、SpinEdit、DatePicker、DateTimePicker、TimePicker、LookupEdit、SearchEdit、TokenEdit、Memo、ColorPicker、MonthCalendar、RadioGroup、CheckGroup、Rating、RangeSlider のいずれかです。
sgcNavigationTsgcWinFormsNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManagerTsgcWpfNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManagerTsgcAvaloniaNavigationBar, ...NavigationTabs, ...NavigationBreadcrumb, ...DockManager
sgcToolbarTsgcWinFormsRibbon, TsgcWinFormsToolbar, TsgcWinFormsStatusBarTsgcWpfRibbon, TsgcWpfToolbar, TsgcWpfStatusBarTsgcAvaloniaRibbon, TsgcAvaloniaToolbar, TsgcAvaloniaStatusBar
sgcWizardTsgcWinFormsWizard, TsgcWinFormsStepperTsgcWpfWizard, TsgcWpfStepperTsgcAvaloniaWizard, TsgcAvaloniaStepper
sgcRichEditTsgcWinFormsRichEditTsgcWpfRichEditTsgcAvaloniaRichEdit
sgcPDF ビューアーTsgcPDFViewerControlTsgcWpfPDFViewerTsgcAvaloniaPDFViewer
sgcReports デザイナーTsgcWinFormsReportDesignerTsgcWpfReportDesignerTsgcAvaloniaReportDesigner
sgcReports プレビューTsgcReportPreviewVCL (WinForms の Panel)TsgcWpfReportPreviewTsgcAvaloniaReportPreview
sgcUICoreTsgcWinFormsAIQueryBar, TsgcWinFormsValidationSummaryTsgcWpfAIQueryBar, TsgcWpfValidationSummaryTsgcAvaloniaAIQueryBar, TsgcAvaloniaValidationSummary

名前空間は製品ごとに 1 つ (esegece.sgcGrid、esegece.sgcCharts など) で、3 つのホストで共通です。ただし WinForms のマップだけは esegece.sgcMaps.WinForms にあります。ホストコントロールは esegece.<Product>.WinForms、.Wpf、.Avalonia のパッケージに含まれます。ダッシュボードの WinForms 用マップタイルは esegece.sgcDashboard.Maps.WinForms にあります。WPF と Avalonia では、sgcReports を除くすべての製品を 1 つの名前空間 URI xmlns:sgc="https://www.esegece.com/sgcui" で XAML に宣言します。sgcReports では clr-namespace によるマッピングを使います。

移行で変わるメンバー

コントロールのそれ以外のメンバーは名前が変わりません。ここに挙げるのが、ホストの違いが表に出る箇所です。

項目 WinForms WPF Avalonia
グリッドにリストをバインドするDataSource (および DataMember)ItemsSource (DependencyProperty)ItemsSource (StyledProperty)
グリッドのバインディングメンバーAutoGenerateColumns、KeyMember、SelectedItem、ItemsBinding、ItemsWriteBack (Registered)。3 つのホストで共通です
現在の項目の同期なしIsSynchronizedWithCurrentItemなし
グリッドの列とデータColumns (TsgcGridColumns), Data (TsgcGridData)
チャートモデルChart (TsgcChart)
プランナーモデルData (TsgcPlannerData), Options (TsgcPlannerSchedulerOptions)
テーマコントロールの Theme (TsgcUITheme。utDefault は sgcUIApplication.Theme に従います)同じ。加えて sgcUICore_Theme_Avalonia.sgcUIApplyThemeToWindow
UI スレッドControl.BeginInvokeDispatcher.BeginInvokeDispatcher.UIThread.Post
アプリケーションの起動Application.Run(form)new Application().Run(window)テーマを持つ Application クラスと StartWithClassicDesktopLifetime
アプリのターゲットフレームワークnet8.0-windows (net472 も可)net8.0-windows (net472 も可)net8.0, net9.0, net10.0

HOST の行と SAME の行

各コード抜粋は MigrateToAvalonia サンプルからコピーしたもので、このサンプルは Registered と Community の両エディションで Release 構成の警告 0 件でコンパイルでき、WinForms、WPF、Avalonia でセルフテストに合格します。ホストのファイルでは、HOST と記した行は 3 つのホストで異なる行、SAME と記した行はすべてで同一の行です。

一度だけ書く: 共有レイヤー

共有クラスライブラリは、モデルのパッケージ esegece.sgcGrid、esegece.sgcCharts、esegece.sgcPlanner、esegece.sgcUICore だけを参照し、ホストのパッケージは参照しません。グループ化と集計を含むグリッドの列は、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();
    }
}

チャートは TsgcChart モデルで、円グラフの系列には TsgcChartDataSource_Items を通じて ObservableCollection からリアルタイムにデータが渡されます。

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;

アプリケーション内のすべての sgcUI コントロールに 1 つのテーマ:

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;
}

唯一のホストサービス: UI スレッドへの投稿

バインドされたコレクションは UI スレッドで変更します。共有レイヤーはホストを知らないため、投稿の呼び出しをデリゲートとして受け取ります。

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));
    });
}

コントロールのフィールドは型だけが異なります

移行前: WinForms または WPF の型。移行後: Avalonia の型。

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;

画面のバインド: どのホストでも同じブロック

HOST と記した行は 2 行あり、UI スレッドの呼び出しとリストのバインドです。残りは 3 つのファイルで同じテキストです。

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;

アプリケーションの起動

起動コードは性質上ホストのコードです。テーマの呼び出しは 3 つのホストで同じです。

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();
    }
}

画面ごとの 8 つの手順

Windows アプリは各手順の後も動作し続けるため、移行は画面ごとに進められます。

1

共有レイヤーを切り出す

データとコントロールの設定を、sgcUI のモデルパッケージ (esegece.sgcGrid、esegece.sgcCharts、esegece.sgcPlanner、esegece.sgcUICore) だけを参照するクラスライブラリに移します。TsgcGridColumns、TsgcChart、TsgcPlannerData、TsgcPlannerSchedulerOptions に対して記述します。

2

Windows から呼び出す

既存の WinForms または WPF の画面からそのライブラリを呼び出すようにします。Windows ではこれまでどおり動作します。

3

Avalonia アプリを作成する

Avalonia.Desktop、Avalonia.Themes.Fluent、必要な esegece.<Product>.Avalonia パッケージを含む net8.0 のプロジェクトです。

4

コントロールの型を入れ替える

TsgcWinForms* と TsgcWpf* は TsgcAvalonia* になります。例外はマッピング表に示しています。

5

ウィンドウを移植する

TableLayoutPanel を持つ Form、または Grid を持つ WPF の Window は、Grid を持つ Avalonia の Window になります。WPF の XAML は .axaml に移し、sgc 名前空間 URI はそのまま使います。

6

WinForms のみ: バインディング

DataSource を ItemsSource に変更します。WPF のコードはすでに ItemsSource を使っています。

7

UI スレッドの呼び出し

BeginInvoke または Dispatcher.BeginInvoke は Dispatcher.UIThread.Post になります。

8

すべてのホストで確認する

サンプルの /selftest と同じように、各ホストで同じチェックを実行し、保存したグリッドレイアウトの JSON を比較します。同じテキストになるはずです。

1 対 1 では対応しない点

移行を計画する前にお読みください。

MigrateToAvalonia

リージョンごとにグループ化した 40 件の注文のグリッド、リージョン別金額の円グラフ、週 5 日表示のスケジューラーを持つ 1 つの画面を、1 つの共有レイヤーから 3 つのホストで表示します。

場所

sgcUI の .NET デモにあり、sgcUI_Demos.sln ソリューションの Suite\MigrateToAvalonia フォルダーです。内容は Shared、WinForms、Wpf、Avalonia、readme.html です。デモのダウンロードでは、プロジェクトは sgcUI の NuGet パッケージを参照します。

実行方法

dotnet build Avalonia\sgcUIMigrate.Avalonia.csproj -c Release を実行し、WinForms\sgcUIMigrate.WinForms.csproj と Wpf\sgcUIMigrate.Wpf.csproj も同様にビルドします。Community エディションでは -p:SgcCommunity=true を追加します。Avalonia のホストは macOS と Linux でも動作します。

セルフテスト

sgcUIMigrate.<Host>.exe /selftest [folder] は、行、グループ化、円グラフ、スケジューラー、ワーカースレッドで追加した注文、削除した注文を検証し、migrate_selftest_<host>.txt と migrate_layout_<host>.json を書き出して、失敗したチェックの数を終了コードとして返します。13 件のチェックはすべて 3 つのホストで合格し、3 つのレイアウトファイルはバイト単位で同一です。

どちらの方法にも公平を期すために述べると、XPF は既存の WPF アプリケーションを、コードとコントロールをほぼそのままで macOS と Linux で動かすことを目指しており、sgcUI はそれを行いません。sgcUI では、各画面のウィンドウコードを Avalonia へ移植し、画面の sgcUI の部分はそのまま引き継がれます。他のベンダーのコントロールで作られた画面は、移行の前に、Windows 上でそれらのコントロールを置き換える必要があります。画面で sgcUI のグリッド、チャート、プランナー、ピボット、ダイアグラムや上記の他の製品を使っている、または使えるようになり、その間にランタイム層を挟まない、完全なソースコード付きのネイティブな Avalonia コントロールを使いたいなら、sgcUI が進むべき道です。製品とエディションについては sgcUI の製品ページをご覧ください。

最もお得な選択: All-AccesseSeGeCe の全製品にプレミアムサポートが付いて、年間 €1,059 からご利用いただけます。
All-Access の価格を見る

最初の画面を今日から移行

NuGet から無償の Community パッケージ、またはすべての機能と完全なソースコードを備えた Registered パッケージを追加し、MigrateToAvalonia サンプルを手元に置いて最初の画面を移植しましょう。