Search code examples
wpfdatatemplateitemscontrolcontentcontrol

Add different controls in ItemsControl


I need to add different controls (TextBox/CheckBox/ComboBox, etc) in ItemsControl based on certain condition. Each Item in ItemsControl is a Name-Value pair. Name is always represented by TextBlock but Value can be any UI control. I am using horizontally aligned StackPanel to represent each Item. First control in StackPanel remains TextBlock but second control is dependent upon "ItemDataType" property set in ViewModel at runtime.

The problem I have is that I am not able to assign different controls in StackPanel's 2nd element using Style trigger with ItemDataType property.

Code Snippet:

<UserControl.Resources>

    <DataTemplate x:Key="TextBoxTemplate">
        <TextBox Text="{Binding Path=DataValue}"/>
    </DataTemplate>

    <DataTemplate x:Key="ComboBoxTemplate">
        <ComboBox ItemsSource="{Binding Path=SelectionList}" SelectedValue="{Binding Path=DataValue,Mode=TwoWay}"/>
    </DataTemplate>

    <DataTemplate x:Key="CheckBoxTemplate">
        <CheckBox IsChecked="{Binding Path=DataValue,Mode=TwoWay}" />
    </DataTemplate>

    <DataTemplate x:Key="ButtonTemplate">
        <Button Content="{Binding Path=DataValue}"/>
    </DataTemplate>

    <DataTemplate x:Key="dynamicTemplate">
        <StackPanel Orientation="Horizontal" Tag="{Binding ItemDataType}">
            <TextBlock Text="{Binding Path=DataName,Mode=TwoWay}"/>
            <ContentControl>
                <ContentControl.Style>
                    <Style TargetType="{x:Type ContentControl}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ItemDataType}" Value="TextBox">
                                <Setter Property="Template" Value="{StaticResource TextBoxTemplate}"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </StackPanel>
    </DataTemplate>

</UserControl.Resources>

<Grid>
    <!-- CONTROL LAYOUT -->
    <ItemsControl ItemsSource="{Binding Path=DataList,Mode=TwoWay}" ItemTemplate="{StaticResource dynamicTemplate}">

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Grid>

Error I get is DataTemplate invalid for ContentControl.Template property. I understand that what I am doing is wrong, but I want help to do it right way.

Thanks,

RDV


Solution

  • I wanted to have a XAML solution - took me sometime :-). Below is the working code:

        <Style x:Key="nvpTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="Width" Value="{Binding Path=LabelWidthStr, FallbackValue=50}"/>
            <Setter Property="Margin" Value="0,5,0,5"/>
            <Setter Property="Text" Value="{Binding Path=NameData,Mode=TwoWay}"/>
            <Setter Property="FontSize" Value="16"/>
        </Style>
    
        <DataTemplate x:Key="textBoxTemplate">
            <TextBox Margin="1,1" Text="{Binding Path=ValueData,UpdateSourceTrigger=PropertyChanged,
                        ValidatesOnExceptions=True,NotifyOnValidationError=True,ValidatesOnDataErrors=True}"/>
        </DataTemplate>
    
        <DataTemplate x:Key="comboBoxTemplate">
            <ComboBox HorizontalAlignment="Left" ItemsSource="{Binding Path=SelectionList}" 
                          SelectedValue="{Binding Path=ValueData,Mode=TwoWay}"
                          IsEnabled="{Binding IsDataItemEnabled}"/>
        </DataTemplate>
    
        <DataTemplate x:Key="checkBoxTemplate">
            <CheckBox HorizontalAlignment="Left" VerticalAlignment="Center"  
                          IsChecked="{Binding Path=ValueData,Mode=TwoWay}"/>
        </DataTemplate>
    
        <DataTemplate x:Key="buttonTemplate">
            <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.AddCommand}" 
                        CommandParameter="{Binding}" Width="30" Height="25">
                <TextBlock Text="&#x1F511;" FontFamily="Segoe UI Symbol" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" />
            </Button>
        </DataTemplate>
    
        <DataTemplate x:Key="dynamicTemplate">
            <StackPanel Orientation="Horizontal" Margin ="5">
                <TextBlock Style="{StaticResource nvpTextBlockStyle}"/>
                <ContentPresenter Content="{Binding}" 
                    Tag="{Binding Path=CustomDataType, FallbackValue={x:Static local:CustomViewModel.TEXTBOX_TEMPLATE}}">
                    <ContentPresenter.Resources>
                        <Style TargetType="{x:Type ContentPresenter}">
                            <Style.Triggers>
                                <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.TEXTBOX_TEMPLATE}">
                                    <Setter Property="ContentTemplate" Value="{StaticResource textBoxTemplate}"/>
                                </Trigger>
                                <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.COMBOBOX_TEMPLATE}">
                                    <Setter Property="ContentTemplate" Value="{StaticResource comboBoxTemplate}"/>
                                </Trigger>
                                <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.CHECKBOX_TEMPLATE}">
                                    <Setter Property="ContentTemplate" Value="{StaticResource checkBoxTemplate}"/>
                                </Trigger>
                                <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.BUTTON_TEMPLATE}">
                                    <Setter Property="ContentTemplate" Value="{StaticResource buttonTemplate}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </ContentPresenter.Resources>
                </ContentPresenter>
            </StackPanel>
        </DataTemplate>
    
        <Grid>       
        <ScrollViewer VerticalScrollBarVisibility="Auto">
            <ItemsControl  ItemsSource="{Binding Path=CustomDataList,Mode=TwoWay}" 
                           ItemTemplate="{StaticResource dynamicTemplate}";
                           KeyboardNavigation.IsTabStop="False">
                <ItemsPanelTemplate>
                    <StackPanel></StackPanel>
                </ItemsPanelTemplate>
            </ItemsControl>
        </ScrollViewer>
    
    </Grid>
    

    Thanks,

    RDV