RunningCSharp

MS系開発者による、雑多な記事。記事は所属企業とは関係のない、個人の見解です。

WPF:Converterを使うとき、xamlのResource内で宣言をしたくないときの書き方

DataConverterを用いたコーディングは大体こんな感じ。

using System.Windows;
using System.Windows.Data;

namespace WpfApplication
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class viewmodel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private int val;
        public int Value
        {
            get { return val; }
            set
            {
                val = value;
                PropertyChanged(this,new PropertyChangedEventArgs("Value"));
            }
        }
    }

    public class MyConverter : IValueConverter

    {
        public object Convert(object value, Type type, object parameter, CultureInfo culture)
        {
            int v = (int)value;
            if (v > 0)
            {
                return "0より大きい!";
            }
            return "0以下!";
        }

        public object ConvertBack(object value, Type type, object parameter, CultureInfo culture)
        {
            //今回はViewから値が戻ってこないコントロールにバインドする前提とします
            return null;
        }
    }
}
<Window x:Class="WpfApplication.MainWindow"
        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:WpfApplication5"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:viewmodel />
    </Window.DataContext>
    <Window.Resources>
        <local:MyConverter x:Key="MyConverter" />
    </Window.Resources>
    <Grid>
        <TextBlock Text="{Binding Value, Converter={StaticResource MyConverter}}" />
    </Grid>
</Window>

上記文中の

    <Window.Resources>
        <local:MyConverter x:Key="MyConverter" />
    </Window.Resources>

これ毎回書くのも嫌ですし、App.xamlの読み込みリソースに使うConverter全部入れるのもちょっと嫌なので、考えてみました。

Converter内に

    public class MyConverter : IValueConverter
    {
    …
        private static MyConverter _converter;
        public static MyConverter Converter
        {
            get
            {
                return _converter ?? (_converter = new MyConverter());
            }
        }
    }

上記のスタティックプロパティを加え、利用するコントロールで

<Window x:Class="WpfApplication5.MainWindow"
        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:WpfApplication5"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:viewmodel />
    </Window.DataContext>
    <Grid>
        <TextBlock Text="{Binding Value, Converter={x:Static local:MyConverter.Converter}}" />
    </Grid>
</Window>

こうすると定義はIValueConverter継承クラス内にまとまり、xamlでは宣言不要でちょっと楽かなと思いました。


3/21 追記

id:tmori3y2さん コメントに気が付かず、承認が遅れてしまって申し訳ありません。 下記のような書き換えが出来そうですね。

public class MyConverter : MarkupExtension, IValueConverter
{
    …
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return _converter ?? (_converter = new MyConverter());
    }
}

これですと仰る通り、

<TextBlock Text="{Binding Value, Converter={local:MyConverter}}" />

で参照可能ですね。 情報提供をありがとうございます!