バインドしたデータの検証や更新を BindingGroup を使って一括で行う

UI にデータをバインドしたとき、OK ボタンを押すまで入力内容をソースに反映させたくない場合があります。例えば設定ダイアログとか。

BindingGroup を利用すれば、入力内容をデータソースに一括で反映させることができます。入力内容の検証も可能です。

BindingGroup を使った UI のサンプルコードは次の通り。

<Window x:Class="BindingGroupSample.AccountView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.BindingGroup>
        
        <!--バインドしたデータを一括で検証/反映するための BindingGroup-->
        <BindingGroup Name="AccountBindings" NotifyOnValidationError="True"/>
        
    </Window.BindingGroup>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Content="ID" Grid.Column="0" Grid.Row="0"/>
        <Label Content="Password" Grid.Column="0" Grid.Row="1"/>
        <TextBox Grid.Column="1" Grid.Row="0">
            <TextBox.Text>
                
                <!-- BindingGroupName を指定したものは一括で検証と更新ができる -->
                <!-- UpdateSourceTrigger には Explicit を指定しておくこと-->
                <Binding Path="Id"
                         Mode="TwoWay"
                         BindingGroupName="AccountBindings"
                         UpdateSourceTrigger="Explicit"/>
                
            </TextBox.Text>
        </TextBox>
        <TextBox Grid.Column="1" Grid.Row="1">
            <TextBox.Text>
                <Binding Path="Password"
                         Mode="TwoWay"
                         BindingGroupName="AccountBindings"
                         UpdateSourceTrigger="Explicit"/>
            </TextBox.Text>
        </TextBox>
        <StackPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    FlowDirection="RightToLeft"
                    Orientation="Horizontal">
            <Button Content="OK"
                    Click="Button_Click"/>
        </StackPanel>
    </Grid>
</Window>

データを更新する場合は、BindingGroup の UpdateSources メソッドを呼び出せば OK。

public partial class AccountView : Window
{
    public AccountView()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // UpdateSources メソッドを呼び出して、
        // 入力内容を反映させる。
        if (BindingGroup.UpdateSources())
        {
            // TODO: 成功したときの処理
        }
    }
}

ところで、Model-View-ViewModel パターンの場合、どうすればいいんだろう?

  • Button の Click イベントハンドラ内で UpdateSources メソッドを呼び出してから ViewModel の Command を呼び出す
  • ViewModel に View への参照を渡して、ViewModel 側で UpdateSources メソッドを呼び出す

思いついたのはこの2つ。どちらもピンと来ないな。