wpfdata-bindingdatatemplate

Selecting User Control for Data Template based on an Enum


I am working on a WPF app and currently I have an ItemsControl bound up to my View Model ObservableCollection and I have a DataTemplate that uses a UserControl to render the items on canvas. Can you use multiple User Controls and then switch which one is used based on an Enum? Another way to look it is to either create a Button or a TextBox for the item in the ObservableCollection based on an Enum.


Solution

  • You can select the data template for an item using a custom DataTemplateSelector. Assume we have the following:

    public enum Kind
    {
        Button, TextBox,
    }
    
    public class Data
    {
        public Kind Kind { get; set; }
        public string Value { get; set; }
    }
    

    Your data template selector might then look like this:

    public class MyTemplateSelector : DataTemplateSelector
    {
        public DataTemplate ButtonTemplate { get; set; }
    
        public DataTemplate TextBoxTemplate { get; set; }
    
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            Data data = (Data)item;
            switch (data.Kind)
            {
                case Kind.Button:
                    return ButtonTemplate;
                case Kind.TextBox:
                    return TextBoxTemplate;
            }
    
            return base.SelectTemplate(item, container);
        }
    }
    

    In XAML, declare templates for all the cases you want to cover, in this case buttons and text boxes:

    <Window.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="ButtonTemplate" DataType="local:Data">
                <Button Content="{Binding Value}" />
            </DataTemplate>
            <DataTemplate x:Key="TextBoxTemplate" DataType="local:Data">
                <TextBox Text="{Binding Value}" />
            </DataTemplate>
        </ResourceDictionary>
    </Window.Resources>
    

    Finally, have your ItemsControl create an instance of your custom template selector, initializing its two DataTemplateproperties from the above data templates:

    <ItemsControl>
        <ItemsControl.ItemTemplateSelector>
            <local:MyTemplateSelector
                ButtonTemplate="{StaticResource ButtonTemplate}"
                TextBoxTemplate="{StaticResource TextBoxTemplate}"/>
        </ItemsControl.ItemTemplateSelector>
        <ItemsControl.Items>
            <local:Data Kind="Button" Value="1. Button" />
            <local:Data Kind="TextBox" Value="2. TextBox" />
            <local:Data Kind="TextBox" Value="3. TextBox" />
            <local:Data Kind="Button" Value="4. Button" />
        </ItemsControl.Items>
    </ItemsControl>
    

    (In real life, set the ItemsSource instead of declaring the items inline, as I did.)

    For completeness: To access your C# classes you need to set up the namespace, e.g.,

    xmlns:local="clr-namespace:WPF"