0

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

Kappawaii
  • 3
  • 2
  • 1
    You can bind only to properties. Try making your `Columns` field into a public property. Like: `public List Columns {get; set;}` – Alex May 21 '19 at 13:41
  • @Alex thanks, I did it for all my other properties but forgot about this one apparently, the other problem is still here though – Kappawaii May 21 '19 at 13:55
  • Try using `{Binding .}` as a binding expression – Alex May 21 '19 at 13:59
  • 1
    Also you should use `ObservableCollection` instead of the `List` to have the UI automatically updated when adding removing an object from the collection – Alex May 21 '19 at 13:59

1 Answers1

0

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/

Andy
  • 11,864
  • 2
  • 17
  • 20