locked
datatemplate for generic type is not working. RRS feed

  • Question

  • Hi. I have a generic type like that.

    public class TestItem<T>
    {
       public override string ToString()
       {
           return "TestItem";
       }
    }
    
    public class Test<T>
    {
       public ObservableCollection<TestItem<T>> Items { get; set; }
    }

    And xaml.

    <DataTemplate x:Key="{x:Type ns:Test`1}">
      <ComboBox Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type w:Control}, ResourceId=ComboBoxStyle}}" 
         ItemsSource="{Binding Path=Value.Items}" SelectedItem="{Binding Path=Value.SelectedItem, Mode=TwoWay}">                   
      </ComboBox>
    </DataTemplate>
    But i see only name of Test type and items in the combobox doesn't show up. And i have a warning in the xaml saying - "incorrect number if type parameter" for ns:Test`1.

    Thursday, August 22, 2013 1:34 PM

Answers

  • If you specify a template per type, you could use a DataTemplateSelector to choose the correct one:

    public class CustomTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            DataTemplate template = null;
            if (item != null)
            {
                FrameworkElement element = container as FrameworkElement;
                if (element != null)
                {
                    string templateName = item is TestItem<int> ?
                        "IntegerTemplate" : "OtherTemplate";
                    template = element.FindResource(templateName) as DataTemplate;
                } 
            }
            return template;
        }
    }

    <DataTemplate x:Key="IntegerTemplate">
            <Grid>
                ...
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="OtherTemplate">
            <Grid>
                ...
            </Grid>
        </DataTemplate>

    However, you will still need a template per type as there are no out-of-the-box support for creating DataTemplates for generic types in XAML.

    How would you define the template if you don't know the type of the properties of the data bound object anyway in this case? If you want to be able to bind the SelectedItem of the ComboBox to any type of property you should use a property of type object, i.e. the same type as the SelectedItem property of the ComboBox.

    Also, the base type never knows about any properties that are defined in a derived class.

    Tuesday, August 27, 2013 9:58 AM

All replies

  • First, for a DataTemplate to applied to all objects of a specific type you should set the TargetType property rather than the x:Key property.

    Second, XAML doesn't directly support specifying a DataTemplate for a generic type. You could use a DataTemplateSelector to choose the correct template (http://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector.aspx) or use a non-generic version of your class that contains memebers that are not type specific:

    public abstract class TestItem
    {
        // members that are not type (T) specific, if any
    }
    public class TestItem<T> : TestItem
    {
        //type specific members
    }

    Thursday, August 22, 2013 2:07 PM
  • Thanks. I use non generic base class and it works well. Just one more problem that selected item doesn't work for some reason.
    Friday, August 23, 2013 4:02 AM
  • Please clarify what "doesn't work" means. Does the DataContext of the ComboBox has a property named Value which is of a type that has a property named SelectedItem? The setter of this property should get set whenever you change the selection in the ComboBox.
    Friday, August 23, 2013 11:07 AM
  • The thing is that only generic type has properties Items and SelectedItem. Xaml says that it can't find these properties at base nongeneric type. Do i need to implement them in base type or not?
    Friday, August 23, 2013 3:27 PM
  • If you have a generic type with some members of type T, you could wrap your generic class in a derived class that specifies the type and use this one as the TargetType for the DataTemplate:

    public class TestItem<T>
    {
        T SelectedItem {get;set;}
    }
    public class TestItem : TestItem<int>
    {
        
    }

    Without knowing your exact requirements here, another option might be to avoid the generics and simply use the object type for the properties:

    public class TestItem
    {
        object SelectedItem {get;set;}
    }

    Monday, August 26, 2013 9:42 AM
  • Sorry, but i need solution for generics types. I can only have base nongeneric type. Could you explain a bit more about datatemplateselector and how i need to use it for this case?

    • Edited by Booster1 Tuesday, August 27, 2013 5:34 AM
    Tuesday, August 27, 2013 5:30 AM
  • If you specify a template per type, you could use a DataTemplateSelector to choose the correct one:

    public class CustomTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            DataTemplate template = null;
            if (item != null)
            {
                FrameworkElement element = container as FrameworkElement;
                if (element != null)
                {
                    string templateName = item is TestItem<int> ?
                        "IntegerTemplate" : "OtherTemplate";
                    template = element.FindResource(templateName) as DataTemplate;
                } 
            }
            return template;
        }
    }

    <DataTemplate x:Key="IntegerTemplate">
            <Grid>
                ...
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="OtherTemplate">
            <Grid>
                ...
            </Grid>
        </DataTemplate>

    However, you will still need a template per type as there are no out-of-the-box support for creating DataTemplates for generic types in XAML.

    How would you define the template if you don't know the type of the properties of the data bound object anyway in this case? If you want to be able to bind the SelectedItem of the ComboBox to any type of property you should use a property of type object, i.e. the same type as the SelectedItem property of the ComboBox.

    Also, the base type never knows about any properties that are defined in a derived class.

    Tuesday, August 27, 2013 9:58 AM
  • Thanks Magnus. DataTemplateSelector is not convinient for me unless it's possible to generate appropriate DataTemplate in real time. But it can have impact on performance i suppose(although it can have a point). Also using object is not good because it demands casting. I'm still thinking if it's possible to use base non generic class. It works good except SelectedItem.
    Thursday, August 29, 2013 11:41 AM
  • As you can bind the SelectedItem property of a ComboBox to a CLR property of any type type, you don't need to use a generic type for this. Also, a genric type cannot be used as-is because it is not really a type; it is more like a blueprint for a type.
    Friday, August 30, 2013 9:46 AM