バリューコンバータを使って、ラジオボタン(UI)とロジックの間で双方向データバインディングを行う方法を解説する。
対象:.NET 4以降
ラジオボタンの選択状態を列挙型や整数型のデータと双方向にバインドするとき、データの変更がラジオボタンの選択状態に正しく反映されなくて困ったことはないだろうか? そのような例と回避策は「.NET TIPS:WPF:ラジオボタンの選択をバインディングソースに反映させるには?[C#/VB]」で述べたが、UIコントロールの都合にロジックのコードを合わせるという不格好なものだった。何とかUIとロジックをきれいに分離できないだろうか? .NET 4以降ならば、バリューコンバータを使うことでそれが可能なのだ*1。本稿では、その方法を解説する。
なお、本稿のサンプルは「Windows desktop code samples:.NET Tips #1127」からダウンロードできる。
*1 .NET 3.5では、本稿の方法ではうまくいかない。「.NET TIPS:WPF:ラジオボタンの選択をバインディングソースに反映させるには?[C#/VB]」で述べた不具合が、やはり発生するのだ。別途公開のサンプル(WPF版)は.NET 3.5でもコンパイルできるように書いてあるので、プロジェクトのプロパティでバージョンを切り替えて試してみてほしい。
バリューコンバータとは、バインディングソース(=データ)とバインディングターゲット(UIコントロール)の間に「挟んで」、双方向にデータを変換する仕組みである。バリューコンバータは、IValueConverterインタフェース(WPFではSystem.Windows.Data名前空間/UWPではWindows.UI.Xaml.Data名前空間)を実装して作成する。
冒頭で述べた問題に対処するには、次のコードのようなバリューコンバータを作ればよい。ポイントは、ConvertBackメソッドにおいて、falseがtrueに変わったときにだけデータを返すようにすることだ。
public class SampleEnum2BooleanConverter : System.Windows.Data.IValueConverter
{
// ConverterParameterをEnumに変換するメソッド
private SampleEnum ConvertFromConverterParameter(object parameter)
{
string parameterString = parameter as string;
return (SampleEnum)Enum.Parse(typeof(SampleEnum), parameterString);
}
#region IValueConverter メンバー
// Enum → bool
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
// XAMLに定義されたConverterParameterをEnumに変換する
SampleEnum parameterValue = ConvertFromConverterParameter(parameter);
// ConverterParameterとバインディングソースの値が等しいか?
return parameterValue.Equals(value);
}
// bool → Enum
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
// true→falseの変化は無視する
// ※こうすることで、選択されたラジオボタンだけをデータに反映させる
if(!(bool)value)
return System.Windows.DependencyProperty.UnsetValue;
// ConverterParameterをEnumに変換して返す
return ConvertFromConverterParameter(parameter);
}
#endregion
}
Public Class SampleEnum2BooleanConverter
Implements System.Windows.Data.IValueConverter
' ConverterParameterをEnumに変換するメソッド
Private Function ConvertFromConverterParameter(parameter As Object) As SampleEnum
Dim parameterString As String = CType(parameter, String)
Return CType([Enum].Parse(GetType(SampleEnum), parameterString), SampleEnum)
End Function
#Region "IValueConverter メンバー"
' Enum → bool
Public Function Convert(value As Object, targetType As Type, parameter As Object,
culture As Globalization.CultureInfo) As Object _
Implements IValueConverter.Convert
' XAMLに定義されたConverterParameterをEnumに変換する
Dim parameterValue As SampleEnum = ConvertFromConverterParameter(parameter)
' ConverterParameterとバインディングソースの値が等しいか?
Return parameterValue.Equals(value)
End Function
' bool → Enum
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object,
culture As Globalization.CultureInfo) As Object _
Implements IValueConverter.ConvertBack
' true→falseの変化は無視する
' ※こうすることで、選択されたラジオボタンだけをデータに反映させる
If (Not CType(value, Boolean)) Then
Return System.Windows.DependencyProperty.UnsetValue
End If
' ConverterParameterをEnumに変換して返す
Return ConvertFromConverterParameter(parameter)
End Function
#End Region
End Class
上のバリューコンバータを使うXAMLコードは、次の画像とソースのようになる。
<Window ……省略……
xmlns:local="clr-namespace:……省略……"
>
<Window.Resources>
……省略……
<!-- バリューコンバータ -->
<local:SampleEnum2BooleanConverter x:Key="E2BConverter"/>
</Window.Resources>
<Grid>
……省略……
<StackPanel x:Name="RadioButtosGroup" ……省略……>
<RadioButton Tag="One" Content="選択肢1"
IsChecked="{Binding Value, ConverterParameter=One,
Converter={StaticResource E2BConverter}}" />
……省略(選択肢2/選択肢3のラジオボタンも同様に記述)……
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="24">現在値:</TextBlock>
<TextBlock FontSize="24" Text="{Binding Value}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" ……省略……>
<Button Click="SelectButton_Click" Tag="1">1</Button>
……省略(選択肢2/選択肢3のボタンも同様に記述)……
</StackPanel>
</Grid>
</Window>
あとは、画面のコンストラクタでデータバインドし、ボタンのクリックイベントのハンドラでバインドしているデータを変更すればよい。その部分のコードおよびバインドするデータのコードについては、別途公開のサンプルをご覧いただきたい。データのコードは、「.NET TIPS:WPF:ラジオボタンの選択をバインディングソースに反映させるには?[C#/VB]」で示したデータと同様な「Value」プロパティを持っているが、それ以外のプロパティは持っておらず、とてもシンプルになっている。
実行すると、次の画像のようになる。ラジオボタンをクリックするとバインディングソースのデータに反映され、それは再びデータバインドによってボタンの上にあるテキストブロックの表示に反映される。また、下部のボタンをクリックするとそのイベントハンドラでデータが書き換えられ、その変化はデータバインドによってラジオボタンの選択状態に反映される。
ラジオボタンの選択状態とデータを双方向データバインディングするには、.NET 4以降であればバリューコンバータを使うとよい。そのバリューコンバータには、ラジオボタンの都合に合わせるための「細工」が必要だ。
利用可能バージョン:.NET Framework 4以降
カテゴリ:WPF 処理対象:データバインディング
カテゴリ:WPF/XAML 処理対象:RadioButtonコントロール
使用ライブラリ:RadioButtonコントロール(System.Windows.Controls名前空間)
使用ライブラリ:IValueConverterインターフェース(System.Windows.Data名前空間)
関連TIPS:WPF/UWP:ラジオボタンの選択をコードから切り替えるには?[C#/VB]
関連TIPS:WPF:ラジオボタンの選択をバインディングソースに反映させるには?[C#/VB]
Copyright© Digital Advantage Corp. All Rights Reserved.