Search code examples
wpfdatatemplate

How to inherit DataTemplate in another DataTemplate


OK, So I don't know if this is possible but? I'm trying to populate a DataGrid (Telerik RadGridView to be exact) and want to dynamically determine the edit controls. I'm trying to create a grid to display data that is being imported into the application and show three columns (Property Name, Inbound Data and Current Database Data). Based on a property in my object class the "Property Name" column needs to have a CheckBox placed in front of it. This follows with the other columns as far as conditionally appending a button so a lookup can occur (a TextBox or TextBlock would be in front of the button to display the current value). I hope this explains what I'm attempting to do. Below is the XAML I've come up with.

Oh This going into a WPF MVVM C# application.

Thanks for any help you can provide.

<UserControl x:Class="PulseHL7Importer.Views.DetailView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
         xmlns:p="clr-namespace:PulseHL7Importer.Properties"
         xmlns:fw="clr-namespace:PulseHL7Importer.Framework"
         xmlns:vm="clr-namespace:PulseHL7Importer.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="200" d:DesignWidth="961">
<UserControl.Resources>
    <telerik:BooleanToVisibilityConverter x:Key="BooleanVisibilityConverter" />
    <DataTemplate x:Key="AddCheckBox">
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding HasCheckBox}" Value="True" />
        </DataTemplate.Triggers>
        <CheckBox IsChecked="{Binding IsChecked}" Margin="2,0,5,0" VerticalAlignment="Center" HorizontalAlignment="Center" />
    </DataTemplate>
    <DataTemplate x:Key="AddTextBlock">
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding IsReadOnly}" Value="True" />
        </DataTemplate.Triggers>
        <TextBlock Text="{Binding Value}" />
    </DataTemplate>
    <DataTemplate x:Key="AddTextBox">
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding IsReadOnly}" Value="False" />
        </DataTemplate.Triggers>
        <TextBox Text="{Binding Value}" />
    </DataTemplate>
    <DataTemplate x:Key="AddButton">
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding HasLookup}" Value="True" />
        </DataTemplate.Triggers>
        <Button Content="..." Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </DataTemplate>
    <DataTemplate x:Key="ConditionalTemplate">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <ContentPresenter Grid.Column="0" Content="{Binding}" ContentTemplate="{StaticResource AddCheckBox}" />
            <ContentPresenter Grid.Column="1" Content="{Binding}" ContentTemplate="{StaticResource AddTextBlock}" />
            <ContentPresenter Grid.Column="1" Content="{Binding}" ContentTemplate="{StaticResource AddTextBox}" />
            <ContentPresenter Grid.Column="2" Content="{Binding}" ContentTemplate="{StaticResource AddButton}" />
        </Grid>
    </DataTemplate>
</UserControl.Resources>
<Grid d:DataContext="{d:DesignInstance vm:DetailViewModel}">
    <telerik:RadDockPanel Width="Auto">
        <!-- Toolbar -->
        <telerik:RadDockPanel telerik:RadDockPanel.Dock="Top">
            <telerik:RadToolBar OverflowButtonVisibility="Collapsed" />
        </telerik:RadDockPanel>

        <!-- Warnings and Errors -->
        <telerik:RadDockPanel telerik:RadDockPanel.Dock="Bottom">
            <telerik:GroupBox Header="Warnings and Errors"
                              telerik:Theming.Theme="{Binding Source={x:Static p:Settings.Default}, Path=SelectedTheme}">
                <TextBox Height="60" IsReadOnly="True"
                         VerticalScrollBarVisibility="Auto"
                         Background="FloralWhite" />
            </telerik:GroupBox>
        </telerik:RadDockPanel>

        <!-- Grid Area -->
        <telerik:RadDockPanel Width="Auto">
            <telerik:RadGridView Name="DetailGridView" Width="Auto" AutoGenerateColumns="False" RowIndicatorVisibility="Collapsed"
                                 CanUserDeleteRows="False" CanUserInsertRows="False" CanUserReorderColumns="False" 
                                 CanUserSortColumns="False" IsFilteringAllowed="False" ColumnWidth="*"
                                 ShowGroupPanel="False" ItemsSource="{Binding Path=Properties}" >
                <telerik:RadGridView.Columns>
                    <telerik:GridViewDataColumn Header="Name" DataMemberBinding="{Binding Path=Value}">
                        <telerik:GridViewDataColumn.CellTemplate>
                            <DataTemplate>
                                <ContentPresenter ContentTemplate="{StaticResource ConditionalTemplate}" Content="{Binding Path=Properties}" />
                            </DataTemplate>
                        </telerik:GridViewDataColumn.CellTemplate>
                        <telerik:GridViewDataColumn.CellEditTemplate>
                            <DataTemplate>
                                <ContentPresenter ContentTemplate="{StaticResource ConditionalTemplate}" Content="{Binding Path=Properties}" />
                            </DataTemplate>
                        </telerik:GridViewDataColumn.CellEditTemplate>
                    </telerik:GridViewDataColumn>
                    <telerik:GridViewDataColumn Header="Client Data" DataMemberBinding="{Binding Path=ClientData.Value}">
                        <telerik:GridViewDataColumn.CellEditTemplate>
                            <DataTemplate>
                                <ContentPresenter ContentTemplate="{StaticResource ConditionalTemplate}" Content="{Binding Path=ClientData}" />
                            </DataTemplate>
                        </telerik:GridViewDataColumn.CellEditTemplate>
                    </telerik:GridViewDataColumn>
                    <telerik:GridViewDataColumn Header="Pulse Data" DataMemberBinding="{Binding Path=PulseData.Value}">
                        <telerik:GridViewDataColumn.CellEditTemplate>
                            <DataTemplate>
                                <ContentPresenter ContentTemplate="{StaticResource ConditionalTemplate}" Content="{Binding Path=PulseData}" />
                            </DataTemplate>
                        </telerik:GridViewDataColumn.CellEditTemplate>
                    </telerik:GridViewDataColumn>
                </telerik:RadGridView.Columns>
            </telerik:RadGridView>
        </telerik:RadDockPanel>
    </telerik:RadDockPanel>
</Grid>


Solution

  • OK, I've managed to solve this with a bit of a hack. I'm now dynamically creating the DataTemplate in the CellEditTemplateSelector. This allows me to check all the properties and add the controls and binding for the appropriate cells.

    Here is the basic XAML grid:

                    <telerik:RadGridView Name="DetailGridView" Width="Auto" AutoGenerateColumns="False" RowIndicatorVisibility="Collapsed"
                                     CanUserDeleteRows="False" CanUserInsertRows="False" CanUserReorderColumns="False" 
                                     CanUserSortColumns="False" IsFilteringAllowed="False" ColumnWidth="*"
                                     ShowGroupPanel="False" ItemsSource="{Binding Path=Properties}">
                    <telerik:RadGridView.Columns>
                        <telerik:GridViewDataColumn Header="Name" IsReadOnlyBinding="{Binding Path=IsReadOnly}"
                                                    DataMemberBinding="{Binding Path=Value}"
                                                    CellTemplateSelector="{StaticResource CellTemplateSelector}"
                                                    CellEditTemplateSelector="{StaticResource CellEditTemplateSelector}">
                            <telerik:GridViewDataColumn.CellStyle>
                                <Style>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="True">
                                            <Setter Property="Canvas.Background" Value="LightGoldenrodYellow" />
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsGroupItem}" Value="True">
                                            <Setter Property="Canvas.Background"  Value="LightGray" />
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsRequired}" Value="True">
                                            <Setter Property="TextElement.Foreground" Value="Red" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </telerik:GridViewDataColumn.CellStyle>
                        </telerik:GridViewDataColumn>
                        <telerik:GridViewMaskedTextBoxColumn Header="Client Data"
                                                    DataMemberBinding="{Binding Path=ClientData.Value}"
                                                    CellEditTemplateSelector="{StaticResource CellEditTemplateSelector}">
                            <telerik:GridViewMaskedTextBoxColumn.CellStyle>
                                <Style>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=ClientData.IsReadOnly}" Value="True">
                                            <Setter Property="Canvas.Background" Value="LightGoldenrodYellow" />
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding Path=IsGroupItem}" Value="True">
                                            <Setter Property="Canvas.Background"  Value="LightGray" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </telerik:GridViewMaskedTextBoxColumn.CellStyle>
                        </telerik:GridViewMaskedTextBoxColumn>
                        <telerik:GridViewDataColumn Header="Pulse Data"
                                                    DataMemberBinding="{Binding Path=PulseData.Value}" 
                                                    CellEditTemplateSelector="{StaticResource CellEditTemplateSelector}">
                            <telerik:GridViewDataColumn.CellStyle>
                                <Style>
                                    <Style.Triggers>
                                       <DataTrigger Binding="{Binding Path=PulseData.IsReadOnly}" Value="True">
                                            <Setter Property="Canvas.Background" Value="LightGoldenrodYellow" />
                                       </DataTrigger>
                                       <DataTrigger Binding="{Binding Path=IsGroupItem}" Value="True">
                                            <Setter Property="Canvas.Background"  Value="LightGray" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </telerik:GridViewDataColumn.CellStyle>
                        </telerik:GridViewDataColumn>
                    </telerik:RadGridView.Columns>
                </telerik:RadGridView>
    

    Here is the CellEditTemplateSelector:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows;
    using Telerik.Windows.Controls.GridView;
    using System.Windows.Markup;
    using System.Windows.Media;
    
    namespace PulseHL7Importer.Framework
    {
    public class CellEditTemplateSelector : DataTemplateSelector
    {
        public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            FrameworkElement element = (FrameworkElement)container;
    
            if (element != null && item != null && item is Property)
            {
                GridViewCell cell = (GridViewCell)container;
                Property property = (Property)item;
                Column column = null;
                StringBuilder grid = new StringBuilder();
                BrushConverter converter = new BrushConverter();
    
                #region Control Header
    
                grid.Append("<DataTemplate");
                grid.Append(" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"");
                grid.Append(" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");
                grid.Append(" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"");
                grid.Append(" xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"");
                grid.Append(" xmlns:telerik=\"http://schemas.telerik.com/2008/xaml/presentation\"");
                grid.Append(" mc:Ignorable=\"d\"");
                grid.Append(">");
    
                #endregion  //Control Header
    
                switch (cell.Column.Header.ToString().Replace(" ", "").ToUpper())
                {
                    case "NAME":
                        grid.Append("<StackPanel Orientation=\"Horizontal\"");
                        if (property.IsGroupItem)
                            grid.Append(" Background=\"LightGray\">");
                        else
                            grid.Append(">");
                        if (property.HasCheckBox)
                        {
                            grid.Append("<Grid Width=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                                " Path=ActualWidth}\">");
                            grid.Append("<Grid.ColumnDefinitions>");
                            grid.Append("<ColumnDefinition Width=\"Auto\" />");
                            grid.Append("<ColumnDefinition Width=\"*\" />");
                            grid.Append("</Grid.ColumnDefinitions>");
                        }
                        if (property.HasCheckBox)
                            grid.Append("<CheckBox Grid.Column=\"0\" IsChecked=\"{Binding Path=IsChecked}\" Margin=\"3,2,3,0\" />");
                        if (property.IsReadOnly)
                            grid.AppendFormat("<TextBlock{0} Text=\"{{Binding Path=Value}}\"", property.HasCheckBox ? " Grid.Column=\"1\"" : "");
                        else
                            grid.AppendFormat("<TextBox{0} Text=\"{{Binding Path=Value}}\"", property.HasCheckBox ? " Grid.Column=\"1\"" : "");
                        if (!property.HasCheckBox)
                        {
                            grid.Append(" Margin=\"15,0,0,0\" ");
                            grid.Append(" Width=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                                " Path=ActualWidth}\"");
                        }
                        if(!property.IsReadOnly)
                            grid.Append(" BorderBrush=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                                " Path=BorderBrush}\"");
                        grid.Append(" Background=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                            " Path=Background}\" />");
                        if (property.HasCheckBox)
                            grid.Append("</Grid>");
                        grid.Append("</StackPanel>");
                        break;
                    case "CLIENTDATA":
                        column = property.ClientData;
    
                        grid.Append(this.BuildColumn(property, "ClientData", column));
                        break;
                    case "PULSEDATA":
                        column = property.PulseData;
    
                        grid.Append(this.BuildColumn(property, "PulseData", column));
                        break;
                }
    
                #region Control Footer
    
                grid.Append("</DataTemplate>");
    
                #endregion
    
                if (!string.IsNullOrWhiteSpace(grid.ToString()))
                {
                    return (DataTemplate)XamlReader.Parse(grid.ToString());
                }
            }
    
            return base.SelectTemplate(item, container);
        }
    
        string BuildColumn(Property property, string columnName, Column column)
        {
            StringBuilder grid = new StringBuilder();
    
            try
            {
                grid.Append("<StackPanel Orientation=\"Horizontal\"");
                if (property.IsGroupItem)
                    grid.Append(" Background=\"LightGray\">");
                else if (column.IsReadOnly)
                    grid.Append(" Background=\"LightGoldenrodYellow\">");
                else
                    grid.Append(">");
                if (column.Options != null && column.Options.Count > 0)
                {
                    grid.Append("<telerik:RadComboBox SelectedValue=\"{Binding Path=ClientData.Value}\"" +
                        " ItemsSource=\"{Binding Path=" + columnName + ".Options}\"" +
                        " IsReadOnly=\"{Binding Path=" + columnName + ".IsReadOnly}\"" +
                        " Width=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                        " Path=ActualWidth}\" />");
                }
                else
                {
                    grid.Append("<Grid Width=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                        " Path=ActualWidth}\">");
                    grid.Append("<Grid.ColumnDefinitions>");
                    if (column.HasCheckBox)
                        grid.Append("<ColumnDefinition width=\"Auto\" />");
                    grid.Append("<ColumnDefinition Width=\"*\" />");
                    if (column.HasButton)
                        grid.Append("<ColumnDefinition Width=\"21\" />");
                    grid.Append("</Grid.ColumnDefinitions>");
                    if (column.HasCheckBox)
                        grid.Append("<CheckBox Grid.Column=\"0\"" +
                            " IsChecked=\"{Binding Path=" + columnName + ".IsChecked}\" Margin=\"3,2,3,0\" />");
                    if (column.IsReadOnly)
                    {
                        grid.AppendFormat("<TextBlock{0} Text=\"{{Binding Path=" + columnName + ".Value}}\"",
                            column.HasCheckBox ? " Grid.Column=\"1\"" : " Grid.Column=\"0\"");
                        if (property.IsGroupItem)
                            grid.Append(" Margin=\"4,3,3,0\"");
                        else
                            grid.Append(" Margin=\"4,0,0,0\"");
                    }
                    else if (!string.IsNullOrWhiteSpace(column.Mask))
                    {
                        grid.AppendFormat("<telerik:RadMaskedTextBox{0} Value=\"{{Binding Path=" + columnName + ".Value}}\"" +
                            " Mask=\"{{Binding Path=" + columnName + ".Mask}}\"",
                            column.HasCheckBox ? " Grid.Column=\"1\"" : " Grid.Column=\"0\"");
                    }
                    else
                        grid.AppendFormat("<TextBox{0} Text=\"{{Binding Path=" + columnName + ".Value}}\"",
                            column.HasCheckBox ? " Grid.Column=\"1\"" : " Grid.Column=\"0\"");
                    if (!property.IsReadOnly)
                        grid.Append(" BorderBrush=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                            " Path=BorderBrush}\"");
                    grid.Append(" Background=\"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel, AncestorLevel=1}," +
                        " Path=Background}\" />");
                    if (column.HasButton)
                        grid.AppendFormat("<Button Grid.Column=\"{0}\" Content=\"...\" />",
                            column.HasCheckBox ? "2" : "1");
                    grid.Append("</Grid>");
                }
                grid.Append("</StackPanel>");
    
                return grid.ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                grid.Clear();
            }
    
            return string.Empty;
        }
    }
    }