12

I did a little WPF programming a long time ago, but I am just returning to xaml with UWP, but I think this should work and cannot figure out why. Basically I want to use an ItemsControl (because I just want to list some data, I don't want selection) instead of a ListView control. Here are my resources:

<Page.Resources>
    <DataTemplate x:Key="SentMessageDataTemplate">
        <TextBlock Text="Sent" />
    </DataTemplate>
    <DataTemplate x:Key="ReceivedMessageDataTemplate">
        <TextBlock Text="Recieved" />
    </DataTemplate>
    <services:MessageDataTemplateSelector x:Key="MessageDataTemplateSelector" ReceivedTemplate="{StaticResource ReceivedMessageDataTemplate}" SentTemplate="{StaticResource SentMessageDataTemplate}"></services:MessageDataTemplateSelector>
</Page.Resources>

Here is my ItemsControl:

<ItemsControl ItemsSource="{Binding Messages}" ItemTemplateSelector="{StaticResource MessageDataTemplateSelector}" />

Here is my DataTemplateSelector:

public class MessageDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate SentTemplate
    {
        get;
        set;
    }

    public DataTemplate ReceivedTemplate
    {
        get;
        set;
    }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        var message = item as MessageViewModel;
        if (message == null)
        {
            return this.SentTemplate;
        }

        return message.Sent ? this.SentTemplate : this.ReceivedTemplate;
    }
}

Instead of displaying either of my templates it just displays my ViewModel type name (so basically ToString).

However if I switch it from ItemsControl to ListView, it works fine.

Any suggestions?

Romasz
  • 29,662
  • 13
  • 79
  • 154
Cleverguy25
  • 493
  • 3
  • 14

4 Answers4

15

Use this override instead:

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)

This is the one that gets called, not the one without the 2nd parameter.

Kory Gill
  • 6,993
  • 1
  • 25
  • 33
  • I tried that, no luck. The ItemsPanelTemplate is the same as the default template. This works fine if I set an ItemTemplate, it just doesn't like ItemTemplateSelectors. – Cleverguy25 Jan 22 '16 at 07:23
  • I updated my answer. The override with 1 param never gets called (as you observed). There are 2 available. Tricky. – Kory Gill Jan 22 '16 at 08:53
  • 1
    I've noticed this issue when refactoring _inline xaml_ implementations or _selectors_ on the same page to their own resource files. SelectTemplateCore(object item) works in many cases on the same page, but overriding the DependencyObject version works in ALL cases, just make sure you handle the case of `item == null` – Chris Schaller Jan 04 '21 at 04:39
5

Interesting -- ListView and GridView both invoke the template selector; plain ItemsControl or ListBox do not.

Overriding the other SelectTemplateCore method in the template selector helps, e.g.:

protected override DataTemplate SelectTemplateCore(object item)
{
    var message = item as MessageViewModel;
    if (message == null)
    {
        return SentTemplate;
    }

    return message.Sent ? SentTemplate : ReceivedTemplate;
}

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
    return SelectTemplateCore(item);
}

The latter method is called in all cases; the first isn't called for ItemsControl items. This page provides an explanation of sorts:

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.

Petter Hesselberg
  • 5,062
  • 2
  • 24
  • 42
1

Here is what the documentation says:

Remarks

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.

0

Maybe you should use <TextBlock Text="{Binding Sent}" /> You don't bind anything in your Template.

Andy
  • 49,085
  • 60
  • 166
  • 233
TheOliver
  • 120
  • 4
  • Even if I do bind something, it doesn't work. ListView works even without binding anything. If I set a breakpoint in my DataTemplateSelector it never gets called when running in an ItemsControl. I think this is just a bug. – Cleverguy25 Jan 21 '16 at 20:04