h607732
h607732

Reputation: 3

Showing dynamically generated ListView or Datagrid in WPF

I have a double list I need to have shown sort of in a spreadsheet-like way, and I'm not sure what would be the best approach.

C#:

public class ViewModelClass
{
public List<ClassA> Data {get;set;}

//more code
}

public class ClassA
{
 public string ColumnName {get;set;}
public List<ClassB> Values {get;set;}
//more code
}

public class ClassB
{
public string DisplayValue {get;set;}
//more code
}

It should be shown as

Row Item1 Item2 Item3
1 Value1.1 Value2.1 Value3.1
2 Value2.2 Value3.1
3 Value 2.3

from a dataset of

ViewModelClass vm;
...
vm.Data.Add(new ClassA { ColumnName="Item1", Values=new List<ClassB>() { new ClassB { DisplayValue="Item1.1" }}};
vm.Data.Add(new ClassA { ColumnName="Item2", Values=new List<ClassB>() { new ClassB { DisplayValue="Item2.1" },
new ClassB { DisplayValue="Item2.2" },new ClassB { DisplayValue="Item2.3" }}};
vm.Data.Add(new ClassA { ColumnName="Item3", Values=new List<ClassB>() { new ClassB { DisplayValue="Item3.1" },
new ClassB { DisplayValue="Item3.2" }}};

I found this thread, though it wasn't quite what I wanted: https://social.msdn.microsoft.com/Forums/vstudio/en-US/da5f36df-91e1-4a1a-9265-25c9a2b56414/binding-list-of-lists-to-datagrid?forum=wpf

My current code is

    var vm = DataContext as ViewModelClass;
    MyDataGrid.Columns.Clear();
    MyDataGrid.Columns.Add(new DataGridTextColumn { Header = "Row" });
    int count = vm.Data.Count;
    for (int i = 0; i < count; i++)
    {
        var col = new DataGridTextColumn { Header = vm.Data[i].ColumnName, Binding = new Binding($"Values") };
        MyDataGrid.Columns.Add(col);
    }

Which gives me the right headers, but the rows are all "(Collection)"

Upvotes: 0

Views: 82

Answers (1)

BionicCode
BionicCode

Reputation: 28988

To create a table like the one in your image you must redesign your class to represent a row based data structure.

If you don't want to use DataTable as data source for the DataGrid, then your class must be designed that it contains properties that each map to a column. The property's name is the column name by default. The class itself their represents a row.

The following example creates a type that has three columns named Item1...Item3:

class MyRowData : INotifyPropertyChanged 
{
  // TODO:: Let all property setters raise the PropertyChanged event
  public string Item1 { get; set; }
  public string Item2 { get; set; }
  public string Item3 { get; set; }
}

This is your basic data structure.

To create the rows, you create the instances of MyRowData and add them to a collection that binds to the DataGrid.ItemsSource property:

// ItemsSource for the DataGrid 
public ObservableCollection<MyRowData> TableData { get; }

for (int i = 1; i < 4; i++)
{
  var newRow = new MyRowData 
  {
    Item1 = $"Value1.{i}",
    Item2 = $"Value2.{i}",
    Item3 = $"Value3.{i}"
  };

  this.TableData.Add(newRow);
}

To enable the DataGrid to update itself with modified data, your data model (in this case MyRowData) must implement INotifyPropertyChanged.

Upvotes: 2

Related Questions