かずきのBlog@hatena

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

Windows RuntimeのXAMLで、型に応じてDataTemplateを選択したい

id:tmytさんのアイデアです。

DataTemplateSelectorを実装して、状況に応じてDataTemplateを何個かの中から返すというのはよくやると思います。今回は、これの汎用実装的なかんじです。型名をキーにデータテンプレートを管理するという発想です。

/// <summary>
/// DataTemplate保持用クラス
/// </summary>
[ContentProperty(Name="DataTemplate")]
public class DataTemplateHolder
{
    public string TypeName { get; set; }
    public DataTemplate DataTemplate { get; set; }
}

/// <summary>
/// 型名をキーにDataTemplateを管理して返すDataTemplateSelector
/// </summary>
[ContentProperty(Name = "Templates")]
public class DataTypeDataTemplateSelector : DataTemplateSelector
{
    private List<DataTemplateHolder> templates = new List<DataTemplateHolder>();
    /// <summary>
    /// 型名とDataTemplateのリスト
    /// </summary>
    public List<DataTemplateHolder> Templates { get { return this.templates; } }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        if (item == null)
        {
            return null;
        }
        // 登録してるDataTemplateに、対応する型名のものがあったらそれを返す
        var r = this.Templates.FirstOrDefault(i => i.TypeName == item.GetType().Name);
        return r != null ? r.DataTemplate : null;
    }
}

使い方

例えば、こんな2種類のクラスがあったとします。

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

public class Dog
{
    public string Name { get; set; }
}

ListViewに先ほどのDataTypeDataTemplateSelectorを設定します。

<ListView ItemsSource="{Binding}">
    <ListView.ItemTemplateSelector>
        <local:DataTypeDataTemplateSelector>
            <local:DataTemplateHolder TypeName="Person">
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="Personだよ" />
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </DataTemplate>
            </local:DataTemplateHolder>
            <local:DataTemplateHolder TypeName="Dog">
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="Dogだよ" />
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </DataTemplate>
            </local:DataTemplateHolder>
        </local:DataTypeDataTemplateSelector>
    </ListView.ItemTemplateSelector>
</ListView>

ListViewのItemsSourceにデータを設定するため、ページのコンストラクタに以下のようなコードを書きます。

public MainPage()
{
    this.InitializeComponent();
    this.DataContext = Enumerable.Range(1, 100)
        .Select(i => i % 2 == 0 ?
            (object)new Person { Name = "にんげん" + i } :
            (object)new Dog { Name = "ぽち" + i });
}

実行すると、型に応じてテンプレートが切り替わってることが確認できます。

f:id:okazuki:20140706182946j:plain

こういうユーテリティ類は懐に用意しておくとよさげですね。