かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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();
        }
    }
}