I am trying to create a generic DataGrid
in my WPF application where the number of columns and the value of each of their headers is defined through a binding to the class property ResultsHeader
, which is an array of strings.
Here is what I have so far :
<DataGrid Grid.Row="1" HorizontalAlignment="Left" ItemsSource="{Binding Results}" Margin="10" IsReadOnly="True" AutoGenerateColumns="False"
Visibility="{Binding ShowResults, Converter={StaticResource BooleanToVisibilityConverter}}">
<DataGrid.Columns>
<DataGridTextColumn Width="auto" Binding="{Binding [0]}"
Header="{Binding ResultsHeader[0], RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Visibility="{Binding ShowResultsColumns[0], Converter={StaticResource BooleanToVisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
<DataGridTextColumn Width="auto" Binding="{Binding [1]}"
Header="{Binding ResultsHeader[1], RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Visibility="{Binding ShowResultsColumns[1], Converter={StaticResource BooleanToVisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
<DataGridTextColumn Width="auto" Binding="{Binding [2]}"
Header="{Binding ResultsHeader[2], RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Visibility="{Binding ShowResultsColumns[2], Converter={StaticResource BooleanToVisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
<DataGridTextColumn Width="auto" Binding="{Binding [3]}"
Header="{Binding ResultsHeader[3], RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Visibility="{Binding ShowResultsColumns[3], Converter={StaticResource BooleanToVisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</DataGrid.Columns>
</DataGrid>
In which Results
represents the content of the cells of the table, ShowResults
is the boolean that controls whether or not the DataGrid
is visible or collapsed, and ShowResultsColumns
is an array of booleans that controls whether each of the individual columns is visible or collapsed. Here is how each of the variables and properties is coded within the ViewModel class :
private static readonly int maxNumberOfColumnsInResults = 4;
private ObservableCollection<string[]> _results;
public ObservableCollection<string[]> Results
{
get { return _results; }
set {
_results = value;
OnPropertyChanged(nameof(Results));
OnPropertyChanged(nameof(ShowResults));
}
}
public bool ShowResults
{
get { return _results != null; }
}
private string[] _resultsHeader;
public string[] ResultsHeader
{
get { return _resultsHeader; }
set {
_resultsHeader = value;
OnPropertyChanged(nameof(ResultsHeader));
OnPropertyChanged(nameof(ShowResultsColumns));
}
}
public bool[] ShowResultsColumns
{
get
{
return _resultsHeader == null
? Enumerable.Repeat(false, maxNumberOfColumnsInResults).ToArray()
: Enumerable.Range(0, maxNumberOfColumnsInResults).Select(x => x < ResultsHeader.Length).ToArray();
}
}
BooleanToVisibilityConverter
and OnPropertyChanged
seem to be correctly implemented based on their behaviors on other features of the application, and my variables are assigned the correct values. The binding works on Results
and ShowResults
fine, but not on ResultsHeader
and ShowResultsColumns
(all the columns are visible with an empty header regardless of the value of _resultsHeader
). Is there something I am missing ? Should I change my approach ?
Thanks in advance.
Is there something I am missing ?
Yes, the fact that a DataGridColumn
has no DataContext
that you can bind to.
Should I change my approach ?
You could use a binding proxy that captures the DataContext
as explained here.