【WPF】Converterの作成方法【C#】

# 概要
WPFでよく使用する`Conveter`クラスについて、説明とサンプルコードを簡単にまとめました。

# `Converter`クラスとは
`Converter`クラスは、ViewとViewModel間で異なる型のデータを、互換性のある形式に変換する役割を果たします。たとえば、ViewModelのBool型のデータを、Viewに合わせて`Visibility`型に変換するConverterがあるとします。その場合、ViewModelのとあるbool型のデータが`true`なら、Viewのとあるコントロールを表示、`false`ならとあるコントロールを非表示、という実装ができます。
他にも、ViewModelのBool型のデータを、Viewの`Brushes`型 (色) に変換するConverterなら、bool型の値に合わせて、Viewのコントロールの色を変更できたりします。

# 作成方法
`Converter`クラスを作る際は、標準ライブラリ`PresentationFramework.dll`の名前空間`System.Windows.Data`にある、インターフェース`IValueConverter`をもつのが一般的です。`Converter`クラス内には`Convert`メソッドと`ConvertBack`メソッドを実装します。`Convert`メソッドはViewModelのプロパティからViewへの変換を記述し、`ConvertBack`メソッドはViewからViewModelのプロパティへの変換を記述します。
ただ`ConvertBack`メソッドは使用しない場合も多いので、
その場合は`throw new NotImplementedException();`等を記述しておきます。

# サンプルコード
以下に、いくつかのConverterクラスのサンプルコードをまとめました。
- [1. ViewModelのbool値をViewのVisibilityに変換する](#1-viewmodelのbool値をviewのvisibilityに変換する)
- [2. ViewModelのbool値をViewのstringに変換する](#2-viewmodelのbool値をviewのstringに変換する)
- [3. ConverterParameterの使用例](#3-converterparameterの使用例)
- [4. ConvertBackの使用例](#4-convertbackの使用例)


## 1. ViewModelの`bool`値をViewの`Visibility`に変換する

 ViewModelのとあるプロパティのbool値が`true`のときは`Visible`、`false`のときは`Collapsed`に変換するクラスです。

### Converter
```C#
namespace Hoge.Converters
{
    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                if (value == null || (bool)value == false)
                {
                    return Visibility.Collapsed;
                }
                else
                {
                    return Visibility.Visible;
                }
            }
            catch (Exception)
            {
                // エラーのためログ出力

                return Visibility.Collapsed;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
```

### 呼出し方
#### View
```xml
<UserControl
    x:Class="Hoge.Views.SampleView"
    xmlns:converter="Hoge.Converters">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Button
            Content="サンプルボタン"
            Visibility="{Binding IsButtonVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
    </Grid>
</UserControl>
```

#### ViewModel
ViewModelの以下のプロパティがConverterを通して、viewに適用されます。

```C#
     public bool IsButtonVisible { get; set; }
```

## 2. ViewModelのbool値をViewの`string`に変換する

 ViewModelのとあるプロパティのbool値が`true`のときは`ふがふが`、`false`のときは`ほげほげ`という文字列に変換するクラスです。

### Converter

```C#
namespace Hoge.Converters
{
    public class BoolToTextConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                if (value == null || (bool)value == false)
                {
                    return "ほげほげ";
                }
                else
                {
                    return "ふがふが";
                }
            }
            catch (Exception)
            {
                // エラーのためログ出力

                return "ぴよぴよ";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
```

### 呼出し方
#### View
```xml
<UserControl
    x:Class="Hoge.Views.SampleView"
    xmlns:converter="Hoge.Converters">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToTextConverter x:Key="BoolToTextConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Button
            Content="{Binding isHogeFugaPiyo, Converter={StaticResource BoolToTextConverter}}"
            Visibility="Visible" />
    </Grid>
</UserControl>
```

#### ViewModel

```C#
     public bool isHogeFugaPiyo { get; set; }
```

## 3. `ConverterParameter`の使用例

xaml側で`ConverterParameter`を使用して、Converterクラスのメソッドにパラメータを設定することもできます。下の例では、`Convert`クラスの引数`parameter`に`inverse`の文字があると、`Visibility`と`Collapsed`が逆となるよう実装しています。

### Converter

```C#
namespace Hoge.Converters
{
    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                bool result;
                if (value == null || (bool)value == false)
                {
                    result = false;
                }
                else
                {
                    result = true;
                }

                if (parameter?.ToString().ToLower() == "inverse")
                {
                    return result ? Visibility.Collapsed : Visibility.Visible;
                }
                else
                {
                    return result ? Visibility.Visible: Visibility.Collapsed;
                }

            }
            catch (Exception)
            {
                // エラーのためログ出力

                return Visibility.Collapsed;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
```

### 呼出し方
#### View
`Visibility`に`ConverterParameter=inverse`を記載しています。

```xml
<UserControl
    x:Class="Hoge.Views.SampleView"
    xmlns:converter="Hoge.Converters">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Button
            Content="サンプルボタン"
            Visibility="{Binding IsButtonVisible, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=inverse}" />
    </Grid>
</UserControl>
```

:::note
xamlでこのように`ConverterParameter`を指定すると、`ConvertBack`メソッドの引数`parameter`にも同じように`ConverterParameter`で指定した文字 (ここでは`inverse`) が入ってきます。
:::

#### ViewModel

```C#
     public bool IsButtonVisible { get; set; }
```

## 4. `ConvertBack`の使用例

このサンプルでは`ConvertBack`も実装しています。
`Convert`メソッドで`ViewModel`のEmum値を`string`に変換して、`ConvertBack`メソッドでViewのstringをViewModelのEnum値に変換しています。
ViewModelの`Sample`プロパティの値を`Convert`メソッドを使用してViewの表示値に変換するのは今までのサンプルと同じ動きです。
今回はそれに加えて、ViewのTextBoxに値が入力され`LostFocus`された際に、ConvertBackメソッドを通して、ViewModelの`Sample`プロパティに適切な値が入ります。

```C#
namespace Hoge.Converters
{
public class SampleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return "";

            var sample = (SampleEnum)Enum.Parse(typeof(SampleEnum), value.ToString());

            switch (sample)
            {
                case SampleEnum.Hoge:
                    return "ほげ";
                case SampleEnum.Fuga:
                    return "ふが";
                case SampleEnum.Piyo:
                    return "ぴよ";
                default:
                    // エラー処理

                    return "";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string sampleText = (string)value;

            switch (sampleText)
            {
                case "ほげ":
                    return SampleEnum.Hoge;
                case "ふが":
                    return SampleEnum.Fuga;
                case "ぴよ":
                    return SampleEnum.Piyo;
                default:
                    // エラー処理

                    return null;
            }
        }
    }
}
```
### 呼出し方
#### View

```xml
<UserControl
    x:Class="Hoge.Views.SampleView"
    xmlns:converter="Hoge.Converters">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:SampleConverter x:Key="SampleConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <TextBox Width="100" Text="{Binding Sample, Converter={StaticResource SampleConverter}" />
    </Grid>
</UserControl>
```

:::note
ViewのTextBoxの`LostFocus`時にConvertBackメソッドが走るのは、
TextBoxのUpdateSourceTriggerのデフォルト値が`UpdateSourceTrigger=LostFocus`だからです。
例えばここで以下のように`UpdateSourceTrigger=PropertyChanged`を指定すると、テキストボックスに文字が入力されたり、一文字消去されるたびに、ViewModelのプロパティも変更されるので、そのたびに`ConvertBack`メソッドも走ります。

`<TextBox Width="100" Text="{Binding Sample, Converter={StaticResource SampleConverter},  UpdateSourceTrigger=PropertyChanged}" />`

:::

#### ViewModel


```C#
public Class SampleClass
{
      public enum SampleEnum
      {
          Hoge,
          Fuga,
          Piyo
      }
        
      public SampleEnum Sample { get; set; }
}
```

# おわりに
Converterクラスは使い方によっては色々とバインディングの幅が広がりそうと感じました。