【実務・中級編】【上級者向け】WPFアプリへのFluent Design System / ModernWpf UI導入によるレガシー感払拭のモダン化手法 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク
スポンサーリンク

「灰色」の終焉。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年後のユーザーにも信頼される設計になっているか?」と。

タイトルとURLをコピーしました