Selecting User Control for Data Template based on an Enum
Asked Answered
P

2

7

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.

Propitiate answered 1/2, 2016 at 21:45 Comment(1)
Do I understand correctly that you want to change your UserControl based on Enum value, not on ViewModel?Outclass
F
10

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"
Favian answered 2/2, 2016 at 7:34 Comment(1)
Fantastic response really helpful. I have got it working with UserControls which was my end goal thanks a lot.Propitiate
H
1

Another possible quick solution is to use Data Triggers:

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Setter Property="Content"
                    Value="{StaticResource YourDefaultLayout}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding YourEnumVMProperty}"
                             Value="{x:Static local:YourEnum.EnumValue1}">
                    <Setter Property="Content"
                            Value="{StaticResource ContentForEnumValue1}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding YourEnumVMProperty}"
                             Value="{x:Static local:YourEnum.EnumValue2}">
                    <Setter Property="Content"
                            Value="{StaticResource ContentForEnumValue2}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

You could also define the template of a whole control using a trigger setter.

I prefer this because there is no need to define all the DataTemplateSelector stuff etc.

Heliacal answered 12/11, 2020 at 16:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.