Search code examples
c#wpfdatagrid

DataTemplated DataGrid not binding correctly? DataTemplateSelector.SelectTemplate receives null as "item"


So im trying to make a custom styled DataGrid with DataTemplateSelector. I'm new to WPF and binding so I'm probably doing something wrong.

The item object received as parameter to the SelectTemplate function is always null.

I've tried googling this problem but I can't find an answer.

XAML:

        <Grid.Resources>
            <!-- Default DataTemplate -->
            <DataTemplate x:Key="DefaultDataTemplate">
                <TextBlock Text="{Binding Path=Value}" />
            </DataTemplate>

            <!-- DataTemplate for Booleans -->
            <DataTemplate x:Key="BooleanDataTemplate">
                <CheckBox IsChecked="{Binding Path=Value, Mode=TwoWay}" />
            </DataTemplate>

            <!-- DataTemplate for Arrays -->
            <DataTemplate x:Key="ArrayDataTemplate">
                <ComboBox ItemsSource="{Binding Path=Value, Mode=TwoWay}" />
            </DataTemplate>

            <local:ValueDataTemplateSelector x:Key="templateSelector" 
                   DefaultDataTemplate="{StaticResource DefaultDataTemplate}" 
                   BooleanDataTemplate="{StaticResource BooleanDataTemplate}" 
                   ArrayDataTemplate="{StaticResource ArrayDataTemplate}" />

        </Grid.Resources>

        <DataGrid Name="DG1" ItemsSource="{Binding TagListView}" AutoGenerateColumns="False" >
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" />
                <DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource templateSelector}" CellEditingTemplateSelector="{StaticResource templateSelector}" />
            </DataGrid.Columns>
        </DataGrid>

code behind:

    public partial class TestView : Window
    {
        public TestView()
        {
            var t = new TestViewModel();
            DataContext = t;
            InitializeComponent();


        }
    }

the view model:

    class TestViewModel
    {

        public TestViewModel()
        {
            generateTestList();
            TagListView = new ListCollectionView(testList);
        }

        private ObservableCollection<TestTag> testList;

        private void generateTestList()
        {
            testList = new ObservableCollection<TestTag>();

            bool b = false;
            testList.Add(new TestTag("bool", (object)b, b.GetType()));

            string s = "...";
            testList.Add(new TestTag("string", (object)s, s.GetType()));

            ushort ui = (ushort)100u;
            testList.Add(new TestTag("uint16", (object)ui, ui.GetType()));

            int[] i_arr = new int[] { 1, 2, 3 };
            testList.Add(new TestTag("int[]", (object)i_arr, i_arr.GetType()));



        }
        public ListCollectionView TagListView { get; private set; }
}

DataTemplateSelector. "item" parameter to SelectTemplate is always null

public class ValueDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate DefaultDataTemplate { get; set; }
        public DataTemplate BooleanDataTemplate { get; set; }
        public DataTemplate ArrayDataTemplate { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var tag = item as TestTag;

            Type t = tag.Type;

            if (t == typeof(bool))
                return BooleanDataTemplate;

            if (t == typeof(int[]))
                return ArrayDataTemplate;

            return DefaultDataTemplate;
        }
    }

the model

    class TestTag : INotifyPropertyChanged
    {

        private string _Name;
        private object _Value;
        public Type Type;

        public string Name
        {
            get { return _Name; }
            private set
            {
                _Name = value;
                NotifyPropertyChanged("Name");
            }
        }

        public object Value
        {
            get
            {
                return _Value;
            }

            set
            {
                _Value = value;
                NotifyPropertyChanged("Value");
            }
        }

Solution

  • It looks like you are just missing a null check on the SelectTemplate method:

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                var tag = item as TestTag;
                if(tag == null)
                    return DefaultDataTemplate;
                Type t = tag.Type;
                if (t == typeof(bool))
                    return BooleanDataTemplate;
                if (t == typeof(int[]))
                    return ArrayDataTemplate;
                return DefaultDataTemplate;
            }
    

    You might as well consider some cleaning up to your TestTag model class, for instance use generic rather than an Object type, lose the Type property and use the typeof whe needed ...