A. Poole A. Poole - 4 months ago 41
C# Question

Datagrid autogenerated columns custom template binding

I am trying to apply a DataTemplate to my autogenerated columns. I can do this with a simple template. However I want a combobox with the text bound to the datagrid and an ellipse showing a colour based on this value through a converter.

I have tried two approaches. Firstly creating the template in codebehind and loading it through an xamlReader.

private void LeftPanel_AutoGeneratingColumn(
object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = e.Column.Header;

string xaml =
"<DataTemplate xmlns:local=\"clr-namespace:myView\">" +
"<DataTemplate.Resources><local:goColorConverter x:Key=\"goColorConverter\" /></DataTemplate.Resources>" +
"<ComboBox " +
"SelectedValue=\"{Binding [" + e.Column.Header +
"], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\" Width=\"85\" " +
"ItemsSource=\"{Binding Path=DataContext.goNoGo, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}\">" +
"<ComboBox.ItemTemplate><DataTemplate><StackPanel Orientation=\"Horizontal\">" +
"<Ellipse Fill=\"{Binding Converter={StaticResource goColorConverter}}\" Height=\"14\" Width=\"14\" HorizontalAlignment=\"Right\"/>" +
"<TextBlock Text=\"{Binding}\" Padding=\"5 0\"/>" +
"</StackPanel></DataTemplate></ComboBox.ItemTemplate></ComboBox></DataTemplate>";

var sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
var pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
var datatemplate = (DataTemplate)XamlReader.Load(sr, pc);

col.CellTemplate = datatemplate;
col.CellEditingTemplate = datatemplate;
e.Column = col;

return;
}


This gives me the correct binding to the data but I cant compile with the converter. I get error cannot create unknown type for my converter (lives in my code behind). I have read many posts about assemblies but could not use them to help me.

My second approach was to define the template in the xaml and apply it in my code behind. This makes the converter work but I don't know how to define my selected value binding as I don't know the column names prior to execution. I was hoping to use this Josh smith FindName example but I don't know how to access the content presenter.

code:

private void LeftPanel_AutoGeneratingColumn(
object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = e.Column.Header;

col.CellTemplate = (DataTemplate)Resources["goDropColumn"];
col.CellEditingTemplate = (DataTemplate)Resources["goDropColumn"];

//Do magic here to get combo box and update its binding

e.Column = col;
return;
}


xaml:

<DataTemplate x:Key="goDropColumn">
<ComboBox Name="combo" Width="85" ItemsSource="{Binding Path=DataContext.goNoGo,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
SelectedValue="{Binding ???}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="{Binding Converter={StaticResource goColorConverter}}"
Height="14" Width="14"/>
<TextBlock Text="{Binding}" Padding="5 0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>


I am not married to my approach and am new to WPF and c# so if the method is totally wrong please let me know. I feel the second approach is a lot cleaner and would work if I could change my binding marked with ??? to the column name.

Another possibly relevant article but I don't have a custom datatype, I just want selectedValue bound to the column.

UPDATED CODE as requested by MachineLearning:

DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = e.Column.Header;


string xaml =
@"<DataTemplate x:Key=""goDropColumn""
xmlns:local=""clr-namespace:RP_SIL.View;assembly=RP_SIL.View""
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x = ""http://schemas.microsoft.com/winfx/2006/xaml"">
<ComboBox SelectedValue=""{Binding [" + e.Column.Header + @"], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}""
Width=""85""
ItemsSource=""{Binding Path=DataContext.goNoGo, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"">
<ComboBox.Resources>
<local:goColorConverter x:Key=""goColorConverter""></local:goColorConverter>
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation=""Horizontal"">
<Ellipse Fill=""{Binding Converter={StaticResource goColorConverter}}"" Height=""14"" Width=""14"" HorizontalAlignment=""Right""/>
<TextBlock Text=""{Binding}"" Padding=""5 0""/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>";
//Binding Converter={StaticResource goColorConverter
var sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
var pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
pc.XmlnsDictionary.Add("local", "clr-namespace:RP_SIL.View;assembly=RP_SIL.View");
var datatemplate = (DataTemplate)XamlReader.Load(sr, pc);

col.CellTemplate = datatemplate;
col.CellEditingTemplate = datatemplate;


Massive Thanks to Machine Learning for his time and solving the problem!

Answer

Welcome. There is a trick to load the converter in the xaml.

This is the code from my demo, as an example for you.

string MyBoolName = "IsEnabled";
string MyTextName = "Title";
string xaml =
 @"<DataTemplate
xmlns:local=""clr-namespace:Templating;assembly=Templating""
x:Key=""goDropColumn"">
                <ComboBox Name=""combo"" Width=""85"" ItemsSource=""{Binding Path=DataContext.MyThings,
        RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}""

       > <!-- SelectedValue=""{Binding ???}"" -->
<ComboBox.Resources>
<local:BrushColorConverter x:Key=""goColorConverter""></local:BrushColorConverter>
</ComboBox.Resources>


                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation=""Horizontal"">
                                <Ellipse Fill=""{Binding ""{Binding "+ MyBoolName + @"}"",  Converter={StaticResource goColorConverter}}""
                           Height=""14"" Width=""14"" />
                                <TextBlock Text=""{Binding " + MyTextName + @"}"" Padding=""5 0""/>
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>
            </DataTemplate>";