Search code examples
uwplistboxuser-controlsdatatemplatedatatemplateselector

DataTemplateSelector for UserControl


I have a Listbox, where I want to load in different types of UserControls in a UWP app.

The reason I need UserControls is because there is code-behind needed for every item that will be shown in the list, so a simple DataTemplate doesn't cut it.

I'm getting an error on the line

<converters:HomeWidgetControlPicker>

in the listbox.

The specified value cannot be assigned. The following type was expected: "DependencyObject".

How can I make the UserControl load? I used this method in an older UWP app and there it worked, but in my new project it doesn't.

I cannot use x:DataType as all items that will be of the same class (HomeWidget), but depending on the Type property of HomeWidget the correct UserControl is selected.

I made a DataTemplateSelector:

public class HomeWidgetControlPicker : DataTemplateSelector
{
    public DataTemplate Artist { get; set; }
    public DataTemplate Release { get; set; }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item != null)
        {
            var result = (HomeWidget)item;

            if (result.Type == "artist")
            {
                return Artist;
            }

            if (result.Type == "release")
            {
                return Release;
            }
        }
        return null;
    }
}

and I have a Listbox, currently only type (HomeWidgetControlPicker.Artist) is used, but more will follow.

        <ListBox x:Name="wrapGrid" ItemContainerStyle="{StaticResource WrapPanelBorderedItem}" ItemsSource="{x:Bind WidgetList}" Background="{x:Null}" ScrollViewer.VerticalScrollBarVisibility="Disabled" VerticalAlignment="Top" HorizontalAlignment="Center" >

            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <userControls:WrapPanel/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <converters:HomeWidgetControlPicker>
                        <converters:HomeWidgetControlPicker.Artist>
                            <DataTemplate>
                                <homeItems:HomeArtist></homeItems:HomeArtist>
                            </DataTemplate>
                        </converters:HomeWidgetControlPicker.Artist>
                    </converters:HomeWidgetControlPicker>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Solution

  • The way you are using DataTemplateSelector should be wrong. In your code snippet, DataTemplateSelector is placed as the root element of DataTemplate, but actually, you can use a DataTemplateSelector and set properties such as ItemTemplateSelector to assign it to a data view. See remarks section of DataTemplateSelector class for more details.

    The base DataTemplateSelector class is not used as an object element in XAML. However, it is a common scenario to derive a custom DataTemplateSelector, map a xmlns prefix for the custom class and its namespace/assembly, and then refer to an instance of the custom class as defined in a Resources block in XAML. This makes it possible to refer to the custom template selector class by x:Key, and use that reference to set the value of properties such as ItemTemplateSelector in XAML templates and visual states.

    For example:

    <Page.Resources>
        <DataTemplate x:Key="Artist" x:DataType="local:HomeWidget">
            <Grid>           
                <local:UserControl1></local:UserControl1>
                 ...               
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="Release" x:DataType="local:HomeWidget">
            <Grid>        
                <local:UserControl2></local:UserControl2> 
              ...             
            </Grid>
        </DataTemplate>
        <local:HomeWidgetControlPicker x:Key="HomeWidgetControlPicker" Artist="{StaticResource Artist}" Release="{StaticResource Release}">
        </local:HomeWidgetControlPicker>
    </Page.Resources>
    
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListBox x:Name="wrapGrid" ItemTemplateSelector="{StaticResource HomeWidgetControlPicker}"  ItemsSource="{x:Bind WidgetList}"  
         ...
        </ListBox>
    </Grid>