「灰色」の終焉。VB.NET WPFをWindows 11の美学へと昇華させる、極限のモダン化戦略
業務自動化ツールの開発現場において、多くのエンジニアが陥る「呪縛」がある。それは、「動けばいい、見た目は二の次だ」という思考停止だ。
長年使い込まれたVB.NET製のデスクトップアプリが、Windows 11の洗練されたUI環境の中で、Windows XP時代の残滓のような「灰色で角張ったウィンドウ」として浮いている光景を、私は何度見てきたことか。UIの古臭さは、単なる審美的な問題ではない。それはユーザーに対する「このソフトウェアはメンテナンスされていない」「信頼性が低い」という無言のメッセージであり、業務効率を著しく低下させる心理的障壁となる。
本稿では、レガシーなVB.NET WPFアプリケーションを、`ModernWpf` ライブラリを用いて、Windows 11スタイルのFluent Design Systemへと劇的に進化させるための、アーキテクチャレベルの知見を伝授する。単なる見た目の変更ではない。保守性と堅牢性を担保した「真のモダン化」の手法だ。
1. なぜ「ただの着せ替え」では失敗するのか
多くの開発者が、既存のコードに無理やりスタイルを適用しようとして自爆する。
原因は明確だ。「ロジックとUIの密結合」である。
WinForms時代の思考を引きずったまま、WPFのコードビハインド(`MainWindow.xaml.vb`)に直接データベース接続やファイル操作を書き込み、その上でコントロールのプロパティを直接操作する……。このような設計に対して、モダンなUIライブラリを被せても、それは「ボロ布を纏った崩壊寸前の建築物」に過ぎない。
モダンなUIを実現するためには、以下の3つの柱を同時に構築する必要がある。
1. Fluent Designの導入: `ModernWpf` によるコントロールテンプレートの刷新。
2. MVVMパターンの徹底: View(見た目)とViewModel(状態・ロジック)の完全分離。
3. 非同期プログラミングの完遂: UIスレッドをブロックしない、I/O待ちの制御。
2. 基盤構築:ModernWpf による Fluent Design の注入
まずは、プロジェクトに `ModernWpfUI` NuGetパッケージを導入する。これだけで、ボタン、スライダー、チェックボックスといった標準コントロールが、Windows 11風の滑らかなアニメーションとデザインに置き換わる。
App.xaml の設定
ここが生命線だ。アプリケーション全体にリソースを適用し、ダークモードとライトモードの切り替えを容易にする。
<Application x:Class="ModernApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- ModernWpfの基本スタイルをロード -->
<ui:ThemeResources />
<ui:XamlControlsResources />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
3. 実践的アーキテクチャ:MVVM と 非同期 I/O の統合
業務ツールにおいて最も重要なのは、「重い処理(DB検索やCSV出力)を行っている間も、UIが固まらないこと」だ。これを実現するために、`CommunityToolkit.Mvvm` を活用した、プロフェッショナルな実装例を示す。
ViewModel の設計(VB.NET)
ここでは、ビジネスロジックを完全に隔離し、`ObservableProperty` を用いて状態を管理する。
Imports CommunityToolkit.Mvvm.ComponentModel
Imports CommunityToolkit.Mvvm.Input
Imports System.Threading.Tasks
''' <summary>
''' 業務データ処理を司るViewModel。
''' UIスレッドに依存せず、ロジックの堅牢性を担保する。
''' </summary>
Public Partial Class MainViewModel
Inherits ObservableObject
<ObservableProperty>
Private _statusMessage As String = "待機中..."
<ObservableProperty>
Private _isBusy As Boolean = False
<ObservableProperty>
Private _dataList As String() = {}
''' <summary>
''' データ取得コマンド。
''' Async/Awaitを使い、UIスレッドを解放しながら実行する。
''' </summary>
<RelayCommand(CanExecute:=Function() Not IsBusy)>
Private Async Function FetchDataAsync() As Task
IsBusy = True
StatusMessage = "データベースからデータを取得中..."
Try
' 実際の業務ではここでDBアクセスやファイル読み込みを行う
' Task.Runを使用して、重い処理をバックグラウンドスレッドへ逃がす
Dim results = Awake Task.Run(Function()
' 擬似的な重いI/O処理
Threading.Thread.Sleep(3000)
Return New String() {"売上データ_2023.csv", "在庫リスト_最新.xlsx", "顧客マスタ.db"}
End Function)
DataList = results
StatusMessage = "データの取得に成功しました。"
Catch ex As Exception
' エラーハンドリングは極めて重要。ユーザーに不透明な例外を見せない。
StatusMessage = $"エラーが発生しました: {ex.Message}"
' ロギング処理をここに挟むべき
Finally
IsBusy = False
End Try
End Function
End Class
View の設計 (MainWindow.xaml)
`ModernWpf` の機能を活用し、プログレスリング(ぐるぐる)やモダンなボタン配置を行う。
<ui:Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:local="clr-namespace:ModernApp"
Title="業務自動化システム Pro" Height="450" Width="800"
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ヘッダー領域 -->
<StackPanel Grid.Row="0" Margin="0,0,0,20">
<TextBlock Text="データ管理ダッシュボード" FontSize="28" FontWeight="SemiBold"/>
<TextBlock Text="{Binding StatusMessage}" Foreground="Gray"/>
</StackPanel>
<!-- メインコンテンツ:モダンなリスト表示 -->
<ListBox Grid.Row="1" ItemsSource="{Binding DataList}"
BorderThickness="0" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<ui:SymbolIcon Symbol="Document" Margin="0,0,10,0"/>
<TextBlock Text="{Binding}" VerticalAlignment="Center" FontSize="16"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- フッター:アクション領域 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
<!-- プログレスリング:処理中のみ表示 -->
<ProgressBar IsIndeterminate="True" Width="100" Margin="0,0,20,0"
Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="データ更新"
Command="{Binding FetchDataCommand}"
Style="{StaticResource AccentButtonStyle}"
Padding="20,8"/>
</StackPanel>
</Grid>
</ui:Window>
4. プロダクション環境における「陥りがちな罠」と回避策
設計思想が正しくても、実装の細部でプロジェクトは崩壊する。私が現場で必ずチェックするポイントは以下の3点だ。
① I/O操作における「スレッドの死」
`Task.Run` を使わずに、ViewModel内で直接 `File.ReadAllLines()` や `SqlConnection.Open()` を呼び出してはならない。これを行うと、データ量が増えた瞬間にUIがフリーズし、Windowsから「応答なし」と判定される。「UIスレッドは表示に専念させ、計算とI/Oはすべて別スレッドに投げる」。これが鉄則だ。
② リソースリークの防止
モダンなUIライブラリは、高度な描画(アニメーションやエフェクト)を行うため、メモリ消費量が増える傾向にある。特に、動的にコントロールを生成したり、イベントハンドラを `+=` で登録し続けたりすると、メモリリークを引き起こす。
- 対策: イベントハンドラは `-=` で適切に解除するか、可能な限り `Command` パターンを使用せよ。
③ データベース接続のライフサイクル
`Using` ステートメントの徹底だ。VB.NETにおいて、DB接続やファイルストリームの解放を怠ることは、致命的なバグ(ファイルロックやコネクションプール枯渇)への直行便である。
' 悪い例
Dim conn As New SqlConnection(connString)
conn.Open()
' ... 処理 ...
' conn.Close() を忘れると、後続のプロセスがファイルにアクセスできない
' 良い例(極限の堅牢性)
Using conn As New SqlConnection(connString)
Await conn.OpenAsync()
' ... 処理 ...
End Using ' ここで確実に解放される
結論:エンジニアリングは「体験」を設計することだ
VB.NETは決して古い言語ではない。適切に設計され、モダンなUIフレームワークと組み合わせれば、Windows 11環境においても、極めて強力で美しい業務ツールを構築できる。
「見た目が変わる」ことは、単なる装飾ではない。それは、ユーザーの認知負荷を下げ、システムの信頼性を高め、エンジニアとしての設計能力を証明する行為なのだ。
次にコードを書くときは、自分に問いかけてほしい。
「そのコードは、10年後のユーザーにも信頼される設計になっているか?」と。
