連載
» 2016年02月03日 05時00分 UPDATE

.NET TIPS:WPF/UWP:ラジオボタンを双方向バインディングするには?[C#/VB]

バリューコンバータを使って、ラジオボタン(UI)とロジックの間で双方向データバインディングを行う方法を解説する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象:.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

ラジオボタンを列挙型にバインドするためのバリューコンバータ(上:C#、下:VB)
このコードはWPF用で、Visual Studio Express 2012 for Windows Desktopで作成した。
バリューコンバータは、バインディングソースのデータをバインディングターゲットに渡すとき(=Convertメソッド)および逆向きにデータを渡すとき(=ConvertBackメソッド)に変換を行うメソッドを二つ作成する。
コメント中に出てくるConverterParameterとは、XAMLの側で指定するパラメータである。XAMLでバインドを指定するときに、バリューコンバータへ渡すパラメータを指定できるのだ。それを利用して、クリックされたラジオボタンがどの選択肢に該当するものなのかを識別している。
ラジオボタンに使うバリューコンバータで大切なことは、ConvertBackメソッドでfalseがtrueに変わったときだけデータを返すようにすることだ。そのようにすることで、ラジオボタンの都合に合わせるためのコードを、ロジックからConvertBackメソッドに移せるのである。逆にいえば、このバリューコンバータはラジオボタン専用であり、チェックボックスなどとのバインディングには使えない。

 上のバリューコンバータを使うXAMLコードは、次の画像とソースのようになる。

上のバリューコンバータを使う画面(VS 2012) 上のバリューコンバータを使う画面(VS 2012)
ラジオボタンのIsCheckedプロパティにデータバインディングの指定があり、その中にConverterParameter(前述)とConverterという指定がしてある。Converterに指定している「E2BConverter」については、次のXAMLコードを参照。<Window.Resources>タグの中で宣言している。

<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>

上のバリューコンバータを使う画面(XAML)
要所のみを掲載する。コードの全体は、別途公開のサンプルをご覧いただきたい。
なお、このコードはWPF用である。UWPでもほぼ同様だが、明示的に双方向バインディングの指定が必要になるなど、細かい相違はある。

 あとは、画面のコンストラクタでデータバインドし、ボタンのクリックイベントのハンドラでバインドしているデータを変更すればよい。その部分のコードおよびバインドするデータのコードについては、別途公開のサンプルをご覧いただきたい。データのコードは、「.NET TIPS:WPF:ラジオボタンの選択をバインディングソースに反映させるには?[C#/VB]」で示したデータと同様な「Value」プロパティを持っているが、それ以外のプロパティは持っておらず、とてもシンプルになっている。

実行結果

 実行すると、次の画像のようになる。ラジオボタンをクリックするとバインディングソースのデータに反映され、それは再びデータバインドによってボタンの上にあるテキストブロックの表示に反映される。また、下部のボタンをクリックするとそのイベントハンドラでデータが書き換えられ、その変化はデータバインドによってラジオボタンの選択状態に反映される。

実行している様子(WPF版、Windows 10) 実行している様子(WPF版、Windows 10)
ボタン[2]をクリックしたところ。ボタンのイベントハンドラでデータを「Two」に書き変えている。その変化がデータバインドによって画面に反映される(ラジオボタンの選択状態は[選択肢2]になり、「現在値」の右にあるテキストブロックは[Two]になる)。
また、ラジオボタンをクリックして選択状態を変更すれば、データバインドによってデータに反映され、それはまたテキストブロックの表示にも反映される。双方向にデータバインドできているのだ。

まとめ

 ラジオボタンの選択状態とデータを双方向データバインディングするには、.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]


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。