I would like to display a table of strings in C# using a DataGrid. The column names are dynamically generated (i.e. not known at compile time).
Here is an example:
Here, the numbers have been converted to strings.
I am using a DataTable as the source for the DataGrid which contains the whole table (rows and column headers). However, I have the problem that the values in column "climate w/change" are not shown in the DataGrid. Instead I get the following error message on the Console
"BindingExpression path error: 'climate w' property not found on 'object'
''DataRowView' (HashCode=22429696)'. BindingExpression:Path=climate
w/change; DataItem='DataRowView' (HashCode=22429696); target element is
'TextBlock' (Name=''); target property is 'Text' (type 'String')"
I understand that this is due to the slash ("/") in the column name which is interpreted as a binding expression.
My questions are
Here is the code to generate the DataTable.
public DataTable PaValues { get; set; }
private void CreateDataSet()
{
var dt = new DataTable("Perturbation Analysis");
List<String> ics = _perturbationAnalysis.ImpactCatagories();
dt.Columns.Add("Parameters");
foreach (var ic in ics)
{
dt.Columns.Add(Sanatize(ic));
}
foreach (var parameter in _perturbationAnalysis.ParameterNames())
{
var dr = dt.NewRow();
dr[0] = parameter;
for (int i = 0; i < ics.Count; i++)
{
dr[i+1] = _perturbationAnalysis[parameter, ics[i]].ToString();
}
dt.Rows.Add(dr);
}
PaValues = dt;
}
private string Sanatize(string ic)
{
//return ic.Replace("/", "[/]").Replace("[", "").Replace("]", "").Replace(".", " ");
//return "[" + ic + "]";
return ic;
}
Here is the excerpt from the XAML file
<DataGrid
x:Name="PAGrid"
CanUserAddRows="False"
CanUserDeleteRows="False"
ClipboardCopyMode="IncludeHeader"
FrozenColumnCount="1"
ItemsSource="{Binding Path=PaValues,UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DataGridStyle}"
IsReadOnly="True">
</DataGrid>
As proposed in the comments, I have added an AutoGeneratingColumn handler, which changes the binding to use square brackets:
private void PaViewAutoGeneratingColumnHandler(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column = new DataGridTextColumn
{
Binding = new Binding("[" + e.Column.Header + "]"), Header=e.Column.Header
};
}
It now works fine for "climate w/change", but it does not work for a column named "EDIP2003 w/o LT, acidification w/o LT, acidification w/o LT", which is a real life example used by the customer. The error message is the same as before
BindingExpression path error: '[]' property not found on 'object' ''DataRowView' (HashCode=56876317)'.
BindingExpression:Path=[EDIP2003 w/o LT, acidification w/o LT, acidification w/o LT]
Looking at the documentation on PropertyPath syntax, we can see why the strings fail to map.
The first one, which you already pointed out and could fix, is the slash /
:
Source Traversal (Binding to Hierarchies of Collections)
<object Path="propertyName/propertyNameX" .../>
The / in this syntax is used to navigate within a hierarchical data source object, and multiple steps into the hierarchy with successive / characters are supported.
This is fixed by using an indexer ([]
).
However, the indexer format supports multiple indexers, separated by comma:
Multiple Indexers
<object Path="[index1,index2...]" .../>
or
<object Path="propertyName[index,index2...]" .../>
If a given object supports multiple indexers, those indexers can be specified in order, similar to an array referencing syntax. The object in question can be either the current context or the value of a property that contains a multiple index object.
If we look further, we can see that we can escape the comma using the following syntax:
Escapes for Property Path Strings
For certain business objects, you might encounter a case where the property path string requires an escape sequence in order to parse correctly. The need to escape should be rare, because many of these characters have similar naming-interaction issues in languages that would typically be used to define the business object.
- Inside indexers ([ ]), the caret character (^) escapes the next character.
Using all of this, I came up with the following solution to escape everything while still using auto-generated columns. It might be overkill, but it guarantees that the string is interpreted correctly.
private void PaViewAutoGeneratingColumnHandler(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var columnName = (string)e.Column.Header;
// We'll build a string with escaped characters.
// The capacity is the length times 2 (for the carets),
// plus 2 for the square brackets.
// This is not optimized for multi-character glyphs, like emojis
var bindingBuilder = new StringBuilder(columnName.Length * 2 + 2);
bindingBuilder.Append('[');
foreach (var c in columnName)
{
bindingBuilder.Append('^');
bindingBuilder.Append(c);
}
bindingBuilder.Append(']');
e.Column = new DataGridTextColumn
{
Binding = new Binding(bindingBuilder.ToString()),
Header = e.Column.Header,
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With