Search code examples
c#xamluwpuwp-xamldatatemplate

Selecting a data template based on type in UWP


Given these types

public class TestTypeBase
{
    public string Name { get; set; }
}
public class TestTypeToggle : TestTypeBase
{
}
public class TestType : TestTypeBase
{
    public bool Enabled { get; set; } = false;
}

this data context

public class vm
{
    public ObservableCollection<TestTypeBase> TestTypes { get; } = new ObservableCollection<TestTypeBase> { new TestTypeToggle { Name = "Don't Test" }, new TestTypeToggle { Name = "Always Test" }, new TestType { Name = "qwert", Enabled = true }, new TestType { Name = "qwert", Enabled = true } };
}

(xaml)

<Page.DataContext>
    <local:vm />
</Page.DataContext>

and this view

<ComboBox Width="120" ItemsSource="{Binding TestTypes}">
    <ComboBox.Resources>
        <DataTemplate x:Key="a" x:DataType="local:TestType">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <CheckBox IsChecked="{Binding Enabled}" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="b" x:DataType="local:TestTypeToggle">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

i was hoping that the ItemTemplate would be selected based on the item types but all i get are the type names as string.

This solution seems promising but i cannot figure how to give the type hint.

(I'm basically having the same problems as in this question but in a UWP context)

Is this possible or do i have to use a ItemTemplateSelector?


Solution

  • Yes, UWP is different from WPF. You have to use a ItemTemplateSelector in UWP.

    Here are the official documents for your reference.

    Page.xaml

    <Page.Resources>
        <DataTemplate x:Key="NormalItemTemplate" x:DataType="x:Int32">
            <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemChromeLowColor}">
                <TextBlock Text="{x:Bind}" />
            </Button>
        </DataTemplate>
    
        <DataTemplate x:Key="AccentItemTemplate" x:DataType="x:Int32">
            <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemAccentColor}">
                <TextBlock Text="{x:Bind}" />
            </Button>
        </DataTemplate>
        <local:MyDataTemplateSelector x:Key="MyDataTemplateSelector"
            Normal="{StaticResource NormalItemTemplate}"
            Accent="{StaticResource AccentItemTemplate}"/>
    </Page.Resources>
    
    <Grid>
        <ListView x:Name = "TestListView"
          ItemsSource = "{x:Bind NumbersList}"
          ItemTemplateSelector = "{StaticResource MyDataTemplateSelector}">
        </ListView>
    </Grid>
    

    Page.xaml.cs

    public sealed partial class MainPage : Page
    {
        public ObservableCollection<int> NumbersList = new ObservableCollection<int>();
        public MainPage()
        {
            this.InitializeComponent();
    
            NumbersList.Add(1);
            NumbersList.Add(2);
            NumbersList.Add(3);
        }
    }
    
    public class MyDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate Normal { get; set; }
        public DataTemplate Accent { get; set; }
    
        protected override DataTemplate SelectTemplateCore(object item)
        {
            if ((int)item % 2 == 0)
            {
                return Normal;
            }
            else
            {
                return Accent;
            }
        }
    }
    

    Edit

    According to this document,

    If your ItemsControl.ItemsPanel is an ItemsStackPanel or ItemsWrapGrid, provide an override for the SelectTemplateCore(Object) method. If the ItemsPanel is a different panel, such as VirtualizingStackPanel or WrapGrid, provide an override for the SelectTemplateCore(Object, DependencyObject) method.

    So you need override the SelectTemplateCore(Object, DependencyObject) method in your DataTemplateSelector.

    Page.xaml

    <Page.Resources>
        <DataTemplate x:Key="NormalItemTemplate" x:DataType="x:Int32">
            <TextBox Text="{x:Bind}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemChromeLowColor}" />
        </DataTemplate>
    
        <DataTemplate x:Key="AccentItemTemplate" x:DataType="x:Int32">
            <TextBox Text="{x:Bind}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemAccentColor}" />
        </DataTemplate>
        
        <local:MyDataTemplateSelector x:Key="MyDataTemplateSelector"
            Normal="{StaticResource NormalItemTemplate}"
            Accent="{StaticResource AccentItemTemplate}"/>
    </Page.Resources>
    
    <Grid>
        <ComboBox x:Name="Testcombox"
                  ItemsSource="{x:Bind NumbersList}"
                  ItemTemplateSelector = "{StaticResource MyDataTemplateSelector}">
        </ComboBox>
    </Grid>
    

    Page.xaml.cs

    public sealed partial class MainPage : Page
    {
    
        public ObservableCollection<int> NumbersList = new ObservableCollection<int>();
    
        public MainPage()
        {
            this.InitializeComponent();
    
            NumbersList.Add(1);
            NumbersList.Add(2);
            NumbersList.Add(3);
        }
    
    }
    
    
    public class MyDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate Normal { get; set; }
        public DataTemplate Accent { get; set; }
    
        protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
        {
            if ((int)item % 2 == 0)
            {
                return Normal;
            }
            else
            {
                return Accent;
            }
        }
    }