DataTemplateSelectorを切り替えたい

環境

  • Winui3 アプリ

準備:

/// アイテム
public partial class MyClass : ObservableRecipient
{
    [ObservableProperty]
    public string name = string.Empty;

    [ObservableProperty]
    public string description = string.Empty;

    [ObservableProperty]
    public int index = -1;
}
/// ViewModel
public class MainViewModel : ObservableRecipient
{
    // ## 3 で使う
    public bool IsMode
    {
        get; set;
    }

    public ObservableCollection<MyClass> MyArray = new();

    public MainViewModel()
    {
        foreach (var item in Enumerable.Range(1, 100))
        {
            MyArray.Add(item: new MyClass()
            {
                Name = string.Format("Yamada-{0:000}", item),
                Index = item
            }); ; ;
        }
    }
}

1:StyleSelector を使う

/// セレクター
public class MySelector : StyleSelector
{
    public Style? Style1
    {
        get; set;
    }
    public Style? Style2
    {
        get; set;
    }

    protected override Style? SelectStyleCore(object item, DependencyObject container)
    {
        if (item is MyClass myClass)
        {
            if (myClass.Index % 2 == 0)
            {
                return Style1;
            }
            else
            {
                return Style2;
            }
        }
        return base.SelectStyleCore(item, container);
    }
}
<Page.Resources>
    <Style x:Key="MyStyle1" TargetType="ListViewItem">
        <Setter Property="Background" Value="AliceBlue" />
    </Style>
    <Style x:Key="MyStyle2" TargetType="ListViewItem">
        <Setter Property="Background" Value="LightYellow" />
    </Style>

    <views:MySelector
        x:Key="MySelectorListview"
        Style1="{StaticResource MyStyle1}"
        Style2="{StaticResource MyStyle2}" />
</Page.Resources>
<ListView
    Grid.Row="1"
    ItemContainerStyleSelector="{StaticResource MySelectorListview}"
    ItemsSource="{x:Bind Path=ViewModel.MyArray}">

    <ListView.ItemTemplate>
        <DataTemplate x:DataType="viewmodels:MyClass">
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="25" Text="{x:Bind Path=Index}" />
                <TextBlock Width="150" Text="{x:Bind Path=Name}" />
                <TextBlock Text="{x:Bind Path=Description}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>

</ListView>

2:DataTemplateSelector を使う

// コレクションの各アイテムのテンプレートを、コレクションの要素によって切り替えるクラス
public class MyTemplateSelector : DataTemplateSelector
{
    // テンプレートのプロパティ
    public DataTemplate MyTemplate
    {
        get; set;
    }
    public DataTemplate DefaultTemplate
    {
        get; set;
    }

    // コレクションの要素に応じてテンプレートを選択するメソッド
    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item is MyClass myClass)
        {
            if (myClass.Index % 2 == 0)
            {
                return DefaultTemplate;
            }
            else
            {
                return MyTemplate;
            }
        }
        return DefaultTemplate;
    }
}
<DataTemplate x:Key="MyTemp1" x:DataType="viewmodels:MyClass">
    <StackPanel Background="AliceBlue" Orientation="Horizontal">
        <TextBlock Width="25" Text="{x:Bind Path=Index}" />
        <TextBlock Width="150" Text="{x:Bind Path=Name}" />
        <TextBlock Text="{x:Bind Path=Description}" />
    </StackPanel>
</DataTemplate>
<DataTemplate x:Key="MyTemp2" x:DataType="viewmodels:MyClass">
    <StackPanel Background="LightYellow" Orientation="Horizontal">
        <TextBlock Width="25" Text="{x:Bind Path=Index}" />
        <TextBlock Width="150" Text="{x:Bind Path=Name}" />
        <TextBlock Text="{x:Bind Path=Description}" />
    </StackPanel>
</DataTemplate>

<views:MyTemplateSelector
    x:Key="MyTemplateSelector"
    DefaultTemplate="{StaticResource MyTemp1}"
    MyTemplate="{StaticResource MyTemp2}" />
<ListView
    Grid.Row="1"
    ItemTemplateSelector="{StaticResource MyTemplateSelector}"
    ItemsSource="{x:Bind Path=ViewModel.MyArray}" />

3: IValueConverter → DataTemplateSelector を使う

動的にセレクターを切り替えたい

public class ModeToSelectorConvertor : IValueConverter
{
    public DataTemplateSelector? MySelector
    {
        get; set;
    }
    public DataTemplateSelector? DefaultSelector
    {
        get; set;
    }

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is bool iMode)
        {
            if (iMode)
            {
                return MySelector;
            }
            else
            {
                return DefaultSelector;
            }
        }
        return this.DefaultSelector!;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}
<DataTemplate x:Key="MyTemp1" x:DataType="viewmodels:MyClass">
    <StackPanel Background="AliceBlue" Orientation="Horizontal">
        <TextBlock Width="25" Text="{x:Bind Path=Index}" />
        <TextBlock Width="150" Text="{x:Bind Path=Name}" />
        <TextBlock Text="{x:Bind Path=Description}" />
    </StackPanel>
</DataTemplate>
<DataTemplate x:Key="MyTemp2" x:DataType="viewmodels:MyClass">
    <StackPanel Background="LightYellow" Orientation="Horizontal">
        <TextBlock Width="25" Text="{x:Bind Path=Index}" />
        <TextBlock Width="150" Text="{x:Bind Path=Name}" />
        <TextBlock Text="{x:Bind Path=Description}" />
    </StackPanel>
</DataTemplate>

<DataTemplate x:Key="MyTemp3" x:DataType="viewmodels:MyClass">
    <StackPanel Background="OrangeRed" Orientation="Horizontal">
        <TextBlock Width="25" Text="{x:Bind Path=Index}" />
        <TextBlock Width="150" Text="{x:Bind Path=Name}" />
        <TextBlock Text="{x:Bind Path=Description}" />
    </StackPanel>
</DataTemplate>

<views:MyTemplateSelector
    x:Key="MyTemplateSelector"
    DefaultTemplate="{StaticResource MyTemp1}"
    MyTemplate="{StaticResource MyTemp2}" />

<views:MyTemplateSelector
    x:Key="MyTemplateSelector2"
    DefaultTemplate="{StaticResource MyTemp1}"
    MyTemplate="{StaticResource MyTemp3}" />

// コンバーター → セレクター
<views:ModeToSelectorConvertor
    x:Key="MyModeToSelectorConvertor"
    DefaultSelector="{StaticResource MyTemplateSelector}"
    MySelector="{StaticResource MyTemplateSelector2}" />
<ListView
    Grid.Row="1"
    ItemTemplateSelector="{x:Bind Path=ViewModel.IsMode, Mode=OneWay, Converter={StaticResource MyModeToSelectorConvertor}}"
    ItemsSource="{x:Bind Path=ViewModel.MyArray}" />

もう少しスマートな方法がある気がするが…。

以上