Search code examples
c#wpfitemscontrolhierarchicaldatatemplatedatatemplateselector

WPF DataTemplate Selector is not working


I am working on a Treeview control. The items control should display a set of textbox and combobox dynamically depending on the value of the data structure. The ArgumentTypeTemplateSelector 's convert code is executed. however, no Textbox and combo is display. Please would someone kindly help. thank you.

The tree View (xaml)

                  <ItemsControl x:Name="argumentTexts" ItemsSource="{Binding ArgumentDetailsCollection}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel HorizontalAlignment="Stretch" IsItemsHost="True" Orientation="Horizontal"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate DataType="{x:Type structures:ArgumentDetails}">
                                <ItemsControl x:Name="items" ItemsSource="{Binding DefaultValue}" 
                                              ItemTemplateSelector="{Binding DefaultValue, Converter={StaticResource ArgTypeTemplateSelector}}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>


public class ArgumentTypeTemplateSelector : IValueConverter
{
    private DataTemplate comboboxDataTemplate;

    private DataTemplate textboxDataTemplate;

    public DataTemplate ComboBoxDataTemplate
    {
        get
        {
            return this.comboboxDataTemplate;
        }

        set
        {
            this.comboboxDataTemplate = value;
        }
    }

    public DataTemplate TextBoxDataTemplate
    {
        get
        {
            return this.textboxDataTemplate;
        }

        set
        {
            this.textboxDataTemplate = value;
        }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string str = (string)value;

        if (str.Contains("1"))
        {
            return this.ComboBoxDataTemplate;
        }

        return this.TextBoxDataTemplate;
    }

In the resourcedictionary(xaml)

        <DataTemplate x:Key="TextBoxDataTemplate">
            <TextBox Text="{Binding DefaultValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                                         VerticalAlignment="Center" 
                                         Width="Auto" 
                                         Margin="5,0,0,0"
                                         Padding="0" 
                                         Style="{StaticResource GridEditStyle}"
                                         IsEnabled="True"/>
        </DataTemplate>
        <DataTemplate x:Key="ComboBoxDataTemplate">
            <ComboBox HorizontalAlignment="Stretch" IsEnabled="True"/> 
        </DataTemplate>
        <columnConfiguratorControls:ArgumentTypeTemplateSelector x:Key="ArgTypeTemplateSelector" ComboBoxDataTemplate="{StaticResource ComboBoxDataTemplate}" TextBoxDataTemplate="{StaticResource TextBoxDataTemplate}"/>
    </ResourceDictionary>

Solution

  • DataTemplateSelectors work in a different way than converters. Your DataTemplateSelector will retrieve the item of your list as argument and depending on the criteria you define, you can choose a DataTemplate that should be returned.

    Try the following: In your xaml file define your DataTemplateSelector as static resource and set it in your ItemsControl:

    <ItemsControl x:Name="argumentTexts" ItemsSource="{Binding ArgumentDetailsCollection}">
            <ItemsControl.Resources>
                <!-- define your template selector as static resource to reference it later -->
                <ns:ArgumentTypeTemplateSelector x:Key="ArgumentTypeTemplateSelector"/>
            </ItemsControl.Resources>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel HorizontalAlignment="Stretch" IsItemsHost="True" Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type structures:ArgumentDetails}">
                    <!-- use your template selector here -->
                    <ItemsControl x:Name="items" ItemsSource="{Binding DefaultValue}" 
                                  ItemTemplateSelector="{StaticResource ArgumentTypeTemplateSelector }"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    

    In your code behind file, you need to implement your data template selector for example in the following way:

    // derive from base class DataTemplateSelector
    public class ArgumentTypeTemplateSelector : DataTemplateSelector
    {
        // override SelectTemplate method
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            // get access to the resources you need, for example
            // by accessing the UI object where your selector is placed in
            var frameworkElement = (FrameworkElement) container;
    
            // return a data template depending on your custom logic,
            // you can cast "item" here to the specific type and check your conditions
            if(item has condition)
            return (DataTemplate) frameworkElement.FindResource("YourDataTemplateKey");
            else
                // ...
            return base.SelectTemplate(item, container);
        }
    }