6
votes

This is driving me nuts. I am creating a DataGrid in code and then binding it to a datatable. This is dynamic and the rows and columns will be different everytime the grid is created.

Basically I loop through my datatable and create DataGrid columns for each column, like this:

private static void CreateDataGridColumns(DataGrid datagrid, Document doc)
{
    if (doc == null) return; //return 

    datagrid.Columns.Clear();
    foreach (var item in doc.Keys)
    {
        var column = new DataGridTemplateColumn
        {
           Header = item,
           CellTemplateSelector = new CustomRowDataTemplateSelector(),
         };

        datagrid.Columns.Add(column);
    }
}

As you can see, I am using a custom data template selector so I can render the cell differently for depending on its content.

Here is the template selector

public class CustomRowDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate
        SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        var presenter = container as ContentPresenter;
        var gridCell = presenter.Parent as DataGridCell;

        if (element != null && item != null && gridCell != null)
        {
            var row = item as DataRow;

            if (row != null)
            {
                var cellObject = row[gridCell.Column.DisplayIndex];

                //set template based on cell type

                if (cellObject is DateTime)
                {
                    return element.FindResource("dateCell") as DataTemplate;
                }

                return element.FindResource("stringCell") as DataTemplate;
            }


        }

        return null;
    }
}

Here is my stringCell DataTemplate

<DataTemplate x:Key="stringCell">
    <StackPanel>
        <TextBlock Style="{StaticResource cellStyle}" 
                    Grid.Row="0" Grid.Column="0" 
                    Text="{Binding Converter={StaticResource cellConverter}}" />
    </StackPanel>
</DataTemplate>

The problem is that the template selector is called for each cell (as expected) but I can't tell which cell it is, so I don't know how to set the Text on the TextBlock. I would love to do something like this

<DataTemplate x:Key="stringCell">
    <StackPanel>
        <TextBlock Style="{StaticResource cellStyle}" 
                    Grid.Row="0" Grid.Column="0" 
                    Text="{Binding Path=Row[CellIndex], Converter={StaticResource cellConverter}}" />
    </StackPanel>
</DataTemplate>

But there is nothing available for me to get CellIndex. How can I do something similar to this where I can set the Path=Row[CellIndex]

2

2 Answers

0
votes

You could try to create binding in code. Something like this should work

var bind = new Binding(gridCell.Column.Header.ToString())
bind.Mode = BindingMode.TwoWay;
bind.Source = row;
BindingOperations.SetBinding(YourTextBlock, TextBlock.TextProperty, bind);
-1
votes

Not sure what you are trying to achieve functionally. You might get away with doing it in code. Create a higher level class, CellClass, that has a property DisplayValue. With implementation for date and string. Bind Source to the CellClass with Path=DisplayValue. You can even create List CellClasses and Bind to CellClass[0], CellClass[1] ... In know this works as I do it but I not sure if it provides the functionality you are looking for.

    public abstract class CellClass
    {
         public abstract String DispValue { get; }
    }
    public class CellClassDate : CellClass
    {
         public override String DispValue { get ...; }
         public DateTime DateValue { get .. set ... }
    }
    public class CellClassString : CellClass
    {
         public override String DispValue { get ...; }
         public DateTime StringValue { get .. set ... }
    }