I'm making a data visualisation app, and I've already got 3 columns written in XAML and successfully bound, but I'm trying to enable users to add columns at runtime.
I'm trying to bind the column header to a value entered in a TextBox, which goes into a class implementing INotifyPropertyChanged, and the value of the cell to an element in a List present in the List of objects in the DataContext.
If that's any use, I'm on .NET 4.7.2
the class in question :
public class ColumnDescriptor : INotifyPropertyChanged
{
private string nom;
public event PropertyChangedEventHandler PropertyChanged;
public string Nom
{
get
{
return nom;
}
set
{
nom = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Nom)));
}
}
the (unsuccessful) binding I tried using a converter, but it wasn't getting called
private void NewColumnEvent_Handler(ColumnDescriptor columnDescriptor, int columnIndex)
{
Binding b = new Binding("Columns[0]")
{
Converter = new NumberToColumnConverter(),
ConverterParameter = columnIndex.ToString(),
Mode = BindingMode.OneWay
};
GridViewColumn gridViewColumn = new GridViewColumn
{
Header = "Name",
DisplayMemberBinding = b
};
mainWindow.lvGridView.Columns.Add(gridViewColumn);
UpdateListViewDataContext();
}
the XAML code of the listView.View
<ListView.View>
<GridView x:Name="lvGridView">
<GridViewColumn Header="Id" Width="125">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}" Foreground="{Binding MessageColor}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Titre" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Titre}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="XPath_Resultat" Width="262">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath_Resultat}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
this is an extract of the class used in the DataContext, with the List I want to use in the binding process
class DataToBind
{
public List<string> Columns{get;set;}
}
I get this error when adding rows to the listview
System.Windows.Data Error: 40 : BindingExpression path error: 'Columns' property not found on 'object' ''DataToBind' (HashCode=5641212)'. BindingExpression:Path=Columns[0]; DataItem='DataToBind' (HashCode=5641212); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
Any help would be greatly appreciated
EDIT : The binding was fixed by changing the variable into a property ->
public List<string> Columns{get;set;}
I'm still searching for the Header binding though
There are several ways you could do this sort of thing for your row viewmodel ( rowvm ).
If you don't need change notification from the viewmodel then you could use an expandoobject. This allows you to dynamically add properties.
https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.8
You could alternatively use a dependency object as a rowvm.
You can then dynamically add attached dependency properties. Create attached property dynamically
These will notify changes.
If you particularly want a plain class as a rowvm then you could use dr wpf's observabledictionary:
http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/
You can then add a column to each by adding a keyvalue pair to your observabledictionary.
Bind using the name.
You would of course need to iterate your entire collection of rowvm to add the keyvalue pairs for a given column.
Another option is to dynamically build a rowvm using ICustomTypeProvider. https://www.c-sharpcorner.com/article/wpf-data-binding-with-icustomtypeprovider/