Reputation: 3401
I have created an indexed property in a class, and I need to set bindings for each cell of a DataGridTemplateColumn
in a DataGrid
(each cell to its corresponding item of the indexed property).
It is for a work-schedule generating application. Each cell in the grid will be displaying data relevant to an employee and shift - more employees in the shift will be growing the grid rightwards, ie adding more columns. The grid displays data structures created and filled programmatically, and not bound to a database table. For the purposes of easier presentation and code inspections in this forum, I have reduced everything to just the essentials.
Here is a pic:
This is obviously wrong, as I can't set the bindings properly. I can bind the fields in the StackPanel to the first (or any other) item of the indexed property, but not in an indexed/parameterized setting.
Here is my xaml (part of):
<Grid HorizontalAlignment="Left">
<DataGrid x:Name="grdSch" HorizontalAlignment="Left" Height="400" VerticalAlignment="Top" Margin="8,0,0,0" AutoGenerateColumns="False" SelectionUnit="Cell"/>
</Grid>
<Page.Resources>
<DataTemplate x:Key="SPI_Template">
<StackPanel>
<TextBlock Text="{Binding Path=shftDate}"/>
<TextBlock Text="{Binding Path=shftKind}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SEI_Template">
<StackPanel>
<TextBlock Text="{Binding Path=data[0].emplName}"/>
<TextBlock Text="{Binding Path=data[0].shftTime}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
And the code generating the columns:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Create the grid columns
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = "Shift";
col.CellTemplate = (DataTemplate)FindResource("SPI_Template");
grdSch.Columns.Add(col);
for (int i = 0; i < 30; i++)
{
col = new DataGridTemplateColumn();
col.Header = "Employee " + (i + 1);
col.Width = 100;
col.CellTemplate = (DataTemplate)FindResource("SEI_Template");
// for now show only the 5 first elements
col.Visibility = i >= 5 ? Visibility.Hidden : Visibility.Visible;
grdSch.Columns.Add(col);
}
}
The data[]
property is indexed. The bindings as they are now (eg "{Binding Path=data[0].emplName}"
) work for the 1st item of the property. If I change it to "{Binding Path=data[1].emplName}"
it will bind to the 2nd one and so on. But this is not what I need of course. Is there some way to change the binding to something like.... "{Binding Path=data[Grid.ColumnIndex-1].emplName}"
? Searched and found some posts about "parameterized binding" etc, but couldn't find something quite obvious or straightforward. Of course I could go the dumb way and add 30 similar templates in the xaml (differing only in the index of the indexed property) - and this will definitely work, but I'm looking into making it in a better way.
Could someone help please?
Thank you in advance
Upvotes: 3
Views: 1208
Reputation: 10349
One solution is to create custom column type (deriving from DataGridTemplateColumn
) which exposes an additional Binding
property, which we will use to bind cell content. Here's the column code:
public class DataGridBoundColumn : DataGridTemplateColumn
{
public BindingBase Binding { get; set; }
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
var element = base.GenerateEditingElement(cell, dataItem);
if (element != null && Binding != null)
element.SetBinding(ContentPresenter.ContentProperty, Binding);
return element;
}
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var element = base.GenerateElement(cell, dataItem);
if (element != null && Binding != null)
element.SetBinding(ContentPresenter.ContentProperty, Binding);
return element;
}
}
Then we need to slightly modify the column generation code:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var col = new DataGridTemplateColumn
{
Header = "Shift",
CellTemplate = (DataTemplate)FindResource("SPI_Template")
};
grdSch.Columns.Add(col);
for (int i = 0; i < 30; i++)
{
col = new DataGridBoundColumn
{
Header = "Employee " + (i + 1),
Binding = new Binding($"data[{i}]"),
Width = 100,
CellTemplate = (DataTemplate)FindResource("SEI_Template"),
};
grdSch.Columns.Add(col);
}
}
Lastly, since our generated columns are already displaying data[i]
, we need to modify the SEI_Template
accordingly:
<DataTemplate x:Key="SEI_Template">
<StackPanel>
<TextBlock Text="{Binding emplName}" />
<TextBlock Text="{Binding shftDate}" />
</StackPanel>
</DataTemplate>
Upvotes: 2