かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

WPFのXAMLのTypeConverterお試し

WPFのXAMLではTypeConverterを自作して文字列から、オブジェクトを作るようなことが簡単にできるようになっています。ということで簡単に試してみました。XAMLからオブジェクトへの変換にはTypeConverterクラスのConvertFromメソッドとCanConvertFromメソッドを実装していれば問題なさそう。

今回は以下のようなPersonクラスで

public class Person
{
    public string Name { get; set; }
    public Person Child { get; set; }
}

Tanaka->Kimura->Hoge->Fooみたいに入力するとNameがTnakaのPersonを作って、そのChildにNameがKimuraのPersonを作って、そのChildに…(略)となるようなTypeConverterを作りました。->で区切って組み立てていくだけですので簡単です。

public class PersonTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var text = (string)value;
        var people = text
            .Split(new[] { "->" }, StringSplitOptions.None)
            .Select(s => s.Trim())
            .Select(s => new Person { Name = s })
            .ToArray();
        people
            .Aggregate((parent, child) =>
            {
                parent.Child = child;
                return child;
            });
        return people.First();
    }
}

このコンバータをPersonクラスにつけます。

[TypeConverter(typeof(PersonTypeConverter))]
public class Person
{
    public string Name { get; set; }
    public Person Child { get; set; }
}

System.Xamlを参照してレッツパース。

class Program
{
    static void Main(string[] args)
    {
        var xaml = @"
            <Person xmlns='clr-namespace:ConsoleApplication3;assembly=ConsoleApplication3'
                Name='Tanaka taro'
                Child='Tanaka goro->Tanaka jiro->Tanaka hoge->Tnaka foo' />
        ";

        var p = XamlServices.Parse(xaml) as Person;
        while (p != null)
        {
            Console.WriteLine("Name: {0}", p.Name);
            p = p.Child;
        }
    }
}

実行するといい感じに表示されます。

Name: Tanaka taro
Name: Tanaka goro
Name: Tanaka jiro
Name: Tanaka hoge
Name: Tnaka foo

コード全体

最後にコード全体をのせておきます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xaml;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            var xaml = @"
                <Person xmlns='clr-namespace:ConsoleApplication3;assembly=ConsoleApplication3'
                    Name='Tanaka taro'
                    Child='Tanaka goro->Tanaka jiro->Tanaka hoge->Tnaka foo' />
            ";

            var p = XamlServices.Parse(xaml) as Person;
            while (p != null)
            {
                Console.WriteLine("Name: {0}", p.Name);
                p = p.Child;
            }
        }
    }

    [TypeConverter(typeof(PersonTypeConverter))]
    public class Person
    {
        public string Name { get; set; }
        public Person Child { get; set; }
    }

    public class PersonTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var text = (string)value;
            var people = text
                .Split(new[] { "->" }, StringSplitOptions.None)
                .Select(s => s.Trim())
                .Select(s => new Person { Name = s })
                .ToArray();
            people
                .Aggregate((parent, child) =>
                {
                    parent.Child = child;
                    return child;
                });
            return people.First();
        }
    }
}