【WPF(XAML)C#8】Converterの使い方

Converterとは?

convertの元々の意味はchange(変わる、変える)と同じような意味です。XAMLに限らず、様々なケースでコンバート・コンバーターが使われる機会があると思います。

XAMLもConverterを使用する事が出来て、これを使えばC#側に余分なViewModelを増やさなくていい。渡したい値をstringにしなくていい等様々な恩恵があります。以下の画像を見てください。

今までのセクションから新しく「前日差」というカラムを作りました。前日から上がっている場合に+がついて赤い文字に下がっていればーになって青い文字になって表示されています。

これは今までのセクションから判断すると、文字色を変えるForegroundにC#側で正の数なら赤etc. を設定したBrushesBinding+-の部分は+の場合にViewModelのstringプロパティに+を付け足した。と思う方も多いかもしれません。

確かにそれでも上記画像を実現する事は可能です。が、C#のコードが間延びしてしまい、可読性が下がってしまいます。

そういった事を自動で判定して、変換してくれるものがConverterです。これを自在に使いこなせれば基本的なXAMLのエディットはほぼ問題ないです。

C#側コード

Converterを書くにはC#側にもコードを追加する必要があります。ただし、public partial class DataGridWindow : Window 内でなく、ViewModelと同じく名前空間であるnamespace に追記してください。

    /// <summary>
    /// 差分に対して正の数だったら+をつける
    /// </summary>
    public class CompRaturePlusConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string ret;

            if ((int)value > 0)
            { 
                ret = "+" + value + "℃";
            }
            else
            { 
                ret =  value + "℃";
            }

            return ret;
        }

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

    /// <summary>
    /// 差分が+だったら色を赤にし、-だったら青にし、0なら黒のまま
    /// </summary>
    public class CompRatureColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((int)value > 0)
            {
                return Brushes.Red;
            }
            else if((int)value < 0)
            {
                return Brushes.Blue;
            }
            else
            {
                return Brushes.Black;
            }         
        }

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

Converterに関する他サイトも見ればわかると思いますがpublic object Convert内以外の記述やクラス名以外は定型文のようですので覚えておきましょう。

XAML側コード

C#側で二つを定義したらXAMLの記載に以下のマークしたコードを追記してください。

<Window x:Class="WpfApp_Core.main.DataGridWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp_Core.main"
        mc:Ignorable="d"
        Title="DataGridWindow" Height="400" Width="500"
        Loaded="DataGridWindow_Loaded">
    <Window.Resources>
        <local:CompRaturePlusConverter x:Key="CompRaturePlusConverter"/>
        <local:CompRatureColorConverter x:Key="CompRatureColorConverter"/>

まず上のxmlns:local=”clr-namespace:WpfApp_Core.main”は自分の名前空間を表しています。そこで追加されたConverterのクラス名はCompRaturePlusConverterというKeyで参照しますよ。という宣言をします。宣言をしたら、DataGridのColumnsに以下を追加しましょう。追加変更した箇所にマーカーを引いてあります。

<DataGrid.Columns>
    <DataGridTextColumn Header="都道府県" Width="60" Binding="{Binding prefLabelPref}"/>
    <DataGridTextColumn Header="市区町村" Width="90" Binding="{Binding municipalityLabel}"/>
    <DataGridTemplateColumn Header="天気" CanUserSort="False" CellTemplate="{StaticResource WeatherTemplate}" Width="50"/>
    <DataGridTextColumn Header="気温" Width="40" Binding="{Binding temperature, StringFormat={}{0:N0}℃, TargetNullValue=不明}"/>
    <DataGridTextColumn Header="前日差" Width="40" Binding="{Binding raturecomp, Converter={StaticResource CompRaturePlusConverter}, Mode=OneWay}">
        <DataGridTextColumn.CellStyle>
            <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellBase}">
                <Setter Property="Foreground" Value="{Binding raturecomp, Converter={StaticResource CompRatureColorConverter}, Mode=OneWay}"/>
            </Style>
        </DataGridTextColumn.CellStyle>
    </DataGridTextColumn>
    <DataGridTextColumn Header="降水確率" Width="50" Binding="{Binding rainyPercent, StringFormat={}{0:N0}%, TargetNullValue=不明}"/>
</DataGrid.Columns>

前日差のBinding値にConverterを使っています。こうすることにより、C#側に対してBinding値であるraturecompがValue値になり、その渡されたValue値を判定し、+等を付け足した値が返り値となって戻ってくるため、その値が表示されるようになります。

色を変えるためのConverterもCell内の定義であれば効果があるようなのでCellStyleを定義して個別にForegroundで色を変えられます。

DataGridのセクションでやったかもしれませんが個別にCellStyleを設定すると共通で設定したx:Type DataGridCellのスタイルが適用されなくなってしまうのでBasedOnを使って共通の設定を引き継いでおくと安心です。

色を変えるために使うCompRatureColorConverterの場合は渡された値は元の差分値と同じだけどその+-によって色を変えるために使われるBrushesを返却しています。

それによってViewModelを使用しなくても色の変更が実現できるというわけです。

まとめ

今回紹介した以外にもConverterは様々な方法で活躍できますので色々なパターンで使ってみるとよいかもしれません。

今回紹介した方法はConverterの基本パターンですが、ConverterParameterでメソッドのようにパラメータを渡す事もできますし、MultiConverterを使って複数のBinding値を渡す事もできます。こちらは応用技術となりますのでここでは解説しませんが興味があれば検索してみると良い記事が見つかると思います。(ConverterParameterで渡せる値はリテラルだけ

以上でConverterの説明を終わります。前回セクションで予告していましたが一旦WPFのセクション記事は中断して次は別の技術の記事を連載していきたいと思います~。またその記事はGitHubとも連携したいと思います。