Reputation: 45
I'm trying to bind a ObservableCollection with rows of data to my datagrid. I'm aiming on making the datagrid update itself (if data is added), it didn't worked when using a DataTable. I tested this approach and it works just fine, my datagrid ist bound to ResultTable property and the property names are the column names.
public class MainWindowViewModel
{
public MainWindowViewModel()
{
ResultTable = new ObservableCollection<Row>();
ResultTable.Add(new Row() { Name = "Peter", Age = "21", Sex = "Male" });
ResultTable.Add(new Row() { Name = "Sara", Age = "25", Sex = "Female" });
ResultTable.Add(new Row() { Name = "Mike", Age = "28", Sex = "Male" });
}
public ObservableCollection<Row> ResultTable { get; set; }
}
public class Row
{
public string Name { get; set; }
public string Age { get; set; }
public string Sex { get; set; }
}
Now my question is, how can I do this dynamically? This was my second approach and looks like nothing in the DataGrid...
public class MainWindowViewModel
{
public MainWindowViewModel()
{
ResultTable = new ObservableCollection<Row>();
var row = new Row();
row.Columns.Add(new Item() { Name = "Name", Value = "Peter" });
row.Columns.Add(new Item() { Name = "Age", Value = "21" });
row.Columns.Add(new Item() { Name = "Sex", Value = "Male" });
ResultTable.Add(row);
}
public ObservableCollection<Row> ResultTable { get; set; }
}
public class Row
{
public List<Item> Columns = new List<Item>();
}
public class Item
{
public string Name { get; set; }
public string Value { get; set; }
}
But now the question is, how can I say the DataGrid that each Name property in columns List is the column header. And each Value is the value of the row?
This is my xaml (nothing fancy):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel x:Name="MainWindowViewModel" />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding ResultTable}"
AutoGenerateColumns="True" />
</Grid>
</Window>
Upvotes: 0
Views: 6648
Reputation: 3612
Ok so have tested this and works as per your diagram in the comments below.
Row class
public class Row
{
private ObservableCollection<Item> columns = new ObservableCollection<Item>();
public Row(params Item[] items)
{
foreach (var item in items)
Columns.Add(item);
}
public ObservableCollection<Item> Columns
{
get { return columns; }
set { columns = value; }
}
}
Item.cs
public class Item
{
public Item(string name, string value)
{
this.Name = name;
this.Value = value;
}
public string Name { get; set; }
public string Value { get; set; }
}
Custom DataGRidBoundColumn
public class CustomBoundColumn : DataGridBoundColumn
{
public string TemplateName { get; set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var binding = new Binding(((Binding)Binding).Path.Path);
binding.Source = dataItem;
var content = new ContentControl();
content.ContentTemplate = (DataTemplate)cell.FindResource(TemplateName);
content.SetBinding(ContentControl.ContentProperty, binding);
return content;
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return GenerateElement(cell, dataItem);
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ObservableCollection<Row> ResultTable
{
get;
set;
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Loaded += (s, args) =>
{
this.ResultTable = new ObservableCollection<Row>();
ResultTable.Add(new Row(new Item("Name", "Peter"), new Item("Age", "21"), new Item("Sex", "Male")));
ResultTable.Add(new Row(new Item("Name", "Sara"), new Item("Age", "25"), new Item("Sex", "Female")));
ResultTable.Add(new Row(new Item("Name", "Mike"), new Item("Age", "28"), new Item("Sex", "Male")));
var columns = ResultTable.First()
.Columns
.Select((x, i) => new { Name = x.Name, Value = x.Value, Index = i })
.ToArray();
foreach (var column in columns)
{
var binding = new Binding(string.Format("Columns[{0}].Value", column.Index));
data.Columns.Add(new CustomBoundColumn()
{
Header = column.Name,
Binding = binding,
TemplateName = "CustomTemplate"
});
}
data.ItemsSource = this.ResultTable;
};
}
}
and finally the xaml nice and simple
<Window.Resources>
<DataTemplate x:Key="CustomTemplate">
<Border Padding="3">
<TextBlock Text="{Binding}" Foreground="Black" />
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid Name="data"
AutoGenerateColumns="False">
</DataGrid>
</Grid>
This is done in code behind but I imagine you can adjust for your viewmodel. More about the fix can be found Here
Upvotes: 1