WPF データーバインディングを用いた表示について

WPFではWindowに何かを表示する場合、通常、ViewModelにプロパティを設定して、データバインディングの機能を利用してViewに表示するようなっています。

例えば名前をと性別の属性を持つPersonクラスのデータをウィンドウに表示するサンプルは以下のようになります。

## サンプルソース

```csharp:sample.cs
namespace DataBindTest
{
    // enum : Genderの定義
    public enum Gender { Unknown, Male, Female };

    // Genderへの拡張メソッドの定義
    static class GenderExt
    {
        public static string DisplayName(this Gender gender)
        {
            string[] names = { "不明", "男性", "女性" };
            return names[(int)gender];
        }
    }

    // モデルクラスの定義
    class Person
    {
        public string Name { get; set; }
        public Gender Gender { get; set; }
        public Person(string name, Gender gender)
        {
            Name = name;
            Gender = gender;
        }
    }

    // ビューモデルの定義
    class ViewModel : ViewModelBase
    {
        private Person Person { get; set; }
        public string Name 
        {  
            get { return Person.Name; } 
        }
        public Gender Gender
        {
            get { return Person.Gender; }
        }
        public ViewModel()
        {
            Person = new Person("山田太郎", DataBindTest.Gender.Male);
        }
    }
}
```

```xml:MainWindow.xaml
<Window x:Class="DataBindTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DataBindTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources >
        <local:ViewModel x:Key="ViewModel"/>
    </Window.Resources>
    <Grid DataContext="{StaticResource ViewModel}">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <StackPanel Orientation="Horizontal" Margin="10">
                <TextBlock Text="名前：" />
                <TextBlock Text="{Binding Path=Name}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="10">
                <TextBlock Text="性別：" />
                <TextBlock Text="{Binding Path=Gender}" />
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>
```

実行すると名前と性別がウィンドウの中央に表示されます。しかし名前は問題ありませんが、性別の表示がenumで定義された識別子名がそのまま表示されてしまい見栄えがよくありません。値がMaleなら「男性」、Femaleなら「女性」と日本語で表示したいところです。

ではどうすれば性別を日本語で表示できるでしょうか。

## 1. ViewModelのプロパティを日本語にする

一番手っ取り早い方法はViewModelで公開するプロパティの時点で日本語に変換してしまう方法です。

ViewModelのGenderプロパティの型をGenderからstringに変更し、getメソッドの定義をGenderの拡張メソッドであるDisplayName()の値を返すように変更します。

```csharp
public string Gender
{
    get { return Person.Gender.DisplayName(); }
}
```

プログラムを実行すると、今度は性別が「男性」と日本語で表示される筈です。

## 2. データーコンバーターを利用する

しかし、本来であれば値をどのような書式で表示するかはViewModelではなく、Viewで解決すべき問題といえます。

WPFにはそのための仕組みとしてデーターコンバーターが用意されています。`IValueConverter`クラスの派生クラスを実装することで、値を任意の形式に変換してViewに渡すことが可能です。

Genderプロパティの値を日本語の文字列に変換する場合は、まず以下のようなIValueConverterの派生クラスを実装します。

```csharp
// データコンバーターの定義
[ValueConversion(typeof(Gender), typeof(string))]
public class Gender2StringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((Gender)value).DisplayName();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        foreach (var n in Enum.GetValues(typeof(Gender)))
        {
            if (((Gender)n).DisplayName() == (string)value)
                return (Gender)n;
        }
        return Gender.Unknown;
    }
}
```

次に、このクラスをViewのXAMLでStaticResourceとして宣言します。

```xml
<Window.Resources >
    <local:ViewModel x:Key="ViewModel"/>
    <local:Gender2StringConverter x:Key="Gender2StringConverter" />
</Window.Resources>
```

最後に性別を表示するTextBlockのデータバインドの記述にデータコンバーターを指定してやります。

```xml
<TextBlock Text="{Binding Path=Gender, Converter={StaticResource Gender2StringConverter}}" />
```

WPF的にはこちらのやりかたが正解といえるでしょう。

しかし、表示項目一つのためにデータコンバーターを用意するのは、数が増えてくるといちいち実装するのも面倒ですし管理も大変になってきます。手間暇を考えれば、ViewModelに表示専用のプロパティを用意する方がはるかに楽です。

どちらを採用するかケースバイケースではあるのでしょうが頭を悩ますところです。

## 余談

ここで示したような単純なenumの文字列化だけの問題であれば、

```csharp
public enum Gender { 不明, 男性, 女性 };
```

という風に、enumの識別子を端から日本語にしてしまうというのが一番シンプルな解決法かもしれません。