かずきのBlog@hatena

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

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

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