【入門編】【中級者・実務】VB.NETとWPFにおける入力値検証(Validation)とエラー情報の視覚的フィードバック実装 – Visual Basic (VB / VB.NET)解析バイブル

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

【極限の知見】WPFで「正しいデータ」を導く技術 —— VB.NETによる入力検証と視覚的フィードバックのマスターガイド

こんにちは。現場の設計からコードの最適化まで、日々アプリケーションの「魂」を吹き込んでいるアーキテクトです。

あなたがもし、これまでVBAやWindows Forms(WinForms)で「入力チェック」を行ってきたのなら、きっとこんな経験があるはずです。
「保存ボタンが押された瞬間に、`If`文の羅列で全項目をチェックし、エラーがあれば `MsgBox` を出す……」

もちろん、それは間違いではありません。しかし、モダンな業務アプリケーションにおいて、その手法は「ユーザー体験(UX)」という観点から見ると、少しばかり「野蛮」と言わざるを得ません。

ユーザーは、ボタンを押して初めて「あ、間違っていた」と知らされるのではなく、「入力しているその瞬間」に、優しく、かつ明確に間違いを指摘されたいのです。

今日は、WPF(Windows Presentation Foundation)の真骨頂である「入力値検証(Validation)」を、VB.NETを使って極限まで深く、そして分かりやすく解説していきます。これをマスターすれば、あなたの作るアプリは「ただ動くツール」から「プロフェッショナルな製品」へと昇華します。

さあ、一緒に新しい世界へ踏み出しましょう。

1. なぜ「ボタンを押した後のMsgBox」では足りないのか?

アーキテクトの視点から、まず「設計思想」のお話をしましょう。

従来の方式(命令型プログラミング)と、WPFが推奨する方式(宣言型プログラミング)には、決定的な違いがあります。

特徴 従来の方式 (MsgBox方式) WPFのValidation方式
タイミング アクション(保存等)の後 入力・変更の瞬間
情報の伝達 割り込み(ポップアップ) 画面内での視覚的変化
責任の所在 ロジックがUIを操作する データ自体が「正しさ」を知っている

WPFでは、「データ(ViewModel)が自分自身の正しさを管理し、UIはそれを鏡のように映し出す」という考え方(MVVMパターン)をとります。この「鏡」の役割を果たすのが、今回学ぶ技術です。

2. 二つの武器:ValidationRule と IDataErrorInfo

WPFにおける入力検証には、大きく分けて二つのアプローチがあります。これらを使い分けるのが、プロへの第一歩です。

① ValidationRule(守護神:型と形式を守る)

これは、「データがViewModelに届く前」に食い止めるフィルターのようなものです。「数値であるべき場所に文字が入っていないか?」「文字数は範囲内か?」といった、UIレイヤーに近い単純なルールに適しています。

② IDataErrorInfo(賢者:ビジネスルールを守る)

これは、「データがViewModelに届いた後」に、オブジェクト自身が判定する仕組みです。「この日付は過去である必要がある」「このIDはデータベースに既に存在する」といった、業務ロジックが絡む複雑なルールに適しています。

3. 実践:IDataErrorInfo による高度な検証実装

もっとも実務で使われる、`IDataErrorInfo` を使った実装を見ていきましょう。
「ユーザー情報を登録する」というシナリオを想定します。

【Step 1】ViewModelの実装 (VB.NET)

まずは、データの入れ物となるViewModelを作成します。ここで重要なのは、`INotifyPropertyChanged`(値が変わったら通知する)と `IDataErrorInfo`(エラーを報告する)の両方を実装することです。

Imports System.ComponentModel

''' <summary>
''' ユーザー情報を管理するViewModel
''' </summary>
Public Class UserViewModel
    Implements INotifyPropertyChanged, IDataErrorInfo

    ' --- プロパティ ---
    Private _userName As String = ""
    Private _age As String = "" ' 数値入力だが、検証のために一旦Stringで受ける手法が一般的

    Public Property UserName As String
        Get
            Return _userName
        End Get
        Set(value As String)
            _userName = value
            OnPropertyChanged(NameOf(UserName))
            ' 値が変わったので、エラー状態も再評価させる必要がある
            OnPropertyChanged(NameOf(Error))
            OnPropertyChanged(NameOf(Item))
        End Set
    End Property

    Public Property Age As String
        Get
            Return _age
        End Get
        Set(value As String)
            _age = value
            OnPropertyChanged(NameOf(Age))
            OnPropertyChanged(NameOf(Error))
            OnPropertyChanged(NameOf(Item))
        End Set
    End Property

    ' --- INotifyPropertyChanged の実装 ---
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    ' --- IDataErrorInfo の実装 ---

    ''' <summary>
    ''' 全体的なエラー(通常は使用しない)
    ''' </summary>
    Public ReadOnly Property Error As String Implements IDataErrorInfo.Error
        Get
            Return Nothing ' 個別のプロパティで管理するため、ここではNothingを返す
        End Get
    End Property

    ''' <summary>
    ''' プロパティごとのエラー内容を返すインデクサー
    ''' </summary>
    Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
        Get
            ' プロパティ名に応じてエラーメッセージを切り替える
            Select Case columnName
                Case NameOf(UserName)
                    If String.IsNullOrWhiteSpace(UserName) Then
                        Return "ユーザー名は必須入力です。"
                    End If
                    If UserName.Length < 3 Then
                        Return "ユーザー名は3文字以上で入力してください。"
                    End If

                Case NameOf(Age)
                    Dim ageValue As Integer
                    If Not Integer.TryParse(Age, ageValue) Then
                        Return "年齢には数値を入力してください。"
                    ElseIf ageValue < 0 OrElse ageValue > 150 Then
                        Return "年齢は0から150の間で入力してください。"
                    End If
            End Select

            Return Nothing ' エラーがない場合はNothingを返す
        End Get
    End Property
End Class

【Step 2】XAMLによる視覚的フィードバックの実装

ViewModelを作っただけでは、画面にエラーは表示されません。XAML側で「エラーが起きたらこう見せる」というデザインを定義する必要があります。

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Validation Master" Height="300" Width="400">
    
    <Window.Resources>
        <!-- エラー時に表示する「赤枠」と「エラーメッセージ」のスタイル定義 -->
        <ControlTemplate x:Key="ErrorTemplate">
            <StackPanel>
                <!-- 入力コントロールの枠線を赤くする -->
                <AdornedElementPlaceholder x:Name="ControlSlot" />
                <!-- エラーメッセージを下に表示 -->
                <TextBlock Text="{Binding [0].ErrorContent}" 
                           Foreground="Red" 
                           FontSize="11" 
                           Margin="0,2,0,0"/>
            </StackPanel>
        </ControlTemplate>
    </Window.Resources>

    <StackPanel Margin="20">
        <Label Content="ユーザー名:" />
        <!-- 
            Validation.ErrorTemplate: エラー時の見た目を指定 
            UpdateSourceTrigger=PropertyChanged: 1文字打つごとに検証を実行する(ここが肝!)
        -->
        <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrorInfo=True}"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
                 Margin="0,0,0,10"/>

        <Label Content="年齢:" />
        <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrorInfo=True}"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
                 Margin="0,0,0,20"/>

        <Button Content="登録" Width="100" Height="30" />
    </StackPanel>
</Window>

4. 伝説のアーキテクトが教える「陥りやすい罠」

ここまで実装できれば、あなたはもう中級者の仲間入りです。しかし、現場では細かな「落とし穴」があなたを待ち構えています。

罠その1:`UpdateSourceTrigger` を忘れる

デフォルトでは、TextBoxの検証は「フォーカスが外れた時(LostFocus)」に実行されます。
「入力している最中にエラーを出したい」なら、必ず `UpdateSourceTrigger=PropertyChanged` を指定してください。これを忘れると、ユーザーは「なぜか反応が遅い」と感じてしまいます。

罠その2:`OnPropertyChanged` の連鎖

`IDataErrorInfo` を使う場合、プロパティの値が変わった時に、「エラー判定自体も更新されたこと」をUIに伝える必要があります。
上記のコードで、`Set`の中で `OnPropertyChanged(NameOf(Item))` を呼び出しているのはそのためです。これを忘れると、エラーメッセージが古いまま残るという怪奇現象が起こります。

罠その3:型の不一致による例外

`Age` プロパティを `Integer` 型にしてしまうと、ユーザーが「あ」と入力した瞬間に、ViewModelへの代入時に例外が発生してアプリが落ちることがあります。
「入力値の検証が完了するまでは、ViewModelのプロパティは `String` で受ける」。これが、堅牢なシステムを構築するための鉄則です。

まとめ:ここをクリアすれば、基本はバッチリですよ

いかがでしたでしょうか?

1. ValidationRule で形式的なガードを固める。
2. IDataErrorInfo でビジネスロジックに基づいた検証を行う。
3. XAMLのErrorTemplate で、ユーザーに直感的なフィードバックを与える。

この三位一体が揃ったとき、あなたのアプリケーションは、ユーザーにストレスを与えない「知的な道具」へと進化します。

最初は少し複雑に感じるかもしれません。でも、一度この「データの流れ」を理解してしまえば、WPFの真のパワーを実感できるはずです。一歩ずつ、確実にマスターしていきましょう。応援していますよ!

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