WPF/XAML Binding: Work with real DataContext - c#

This is my XAML code:
<ComboBox Grid.Row="0" Margin="5" ItemsSource="{Binding Path=Clvm.Categories}">
</ComboBox>
<GridSplitter Grid.Row="0" Height="3" />
<DataGrid Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Email" Binding="{Binding Email}" />
<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
ItemsSource="{Binding Path=Clvm.Categories}" />
</DataGrid.Columns>
</DataGrid>
The ComboBox above is just for testing - I want to see if I can display all my Categories and it works well.
The DataGrid below is the control I really want. I want that Categories ComboBox as a column of the DataGrid. But the way I do it in the snippet obviously doesn't work as the data bindings for the columns are relative to the DataGrid's ItemsSource and not to the old DataContext. How can I switch it back for that binding?
The Control class:
public partial class ArtistManagementControl : UserControl {
public ArtistManagementControl() {
InitializeComponent();
IDatabase db = new MYSQLDatabase("Server = localhost; Database = ufo; Uid = root;");
SharedServices.Init(db);
Alvm = new ArtistListViewModel(SharedServices.GetInstance().ArtistService, SharedServices.GetInstance().CategoryService);
Clvm = new CategoryListViewModel(SharedServices.GetInstance().CategoryService);
this.DataContext = this;
}
public ArtistListViewModel Alvm {
get; private set;
}
public CategoryListViewModel Clvm {
get; private set;
}
}
The ViewModel for the categories:
public class CategoryListViewModel {
private ICategoryService categoryService;
public CategoryListViewModel(ICategoryService categoryService) {
this.categoryService = categoryService;
Categories = new ObservableCollection<CategoryViewModel>();
UpdateCategories();
}
public ObservableCollection<CategoryViewModel> Categories {
get; set;
}
public void UpdateCategories() {
Categories.Clear();
foreach(var cat in categoryService.GetAllCategories()) {
Categories.Add(new CategoryViewModel(cat));
}
}
}

Since DataGridComboBoxColumn or any other supported data grid columns are not part of visual tree of datagrid so they don't inherit the DataContext of datagrid. Since, they don't lie in visual tree so any try to get DataContext using RelativeSource won't work.
Solution - You can create a proxy element to bind the data context of window; use that proxy element to bind the ItemsSource of DataGridComboBoxColumn.
<Grid>
<Grid.Resources>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
</Grid.Resources>
<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"></ContentControl>
<DataGrid x:Name="datagrid" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Email" Binding="{Binding Email}" />
<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
ItemsSource="{Binding DataContext.Clvm.Categories, Source={StaticResource ProxyElement}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>

You can bind to the DataGrids DataContext by using an ElementName Binding:
<DataGrid x:Name="datagrid" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Email" Binding="{Binding Email}" />
<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
ItemsSource="{Binding Path=DataContext.Clvm.Categories, ElementName=datagrid}" />
</DataGrid.Columns>
</DataGrid>
Note that you have to give the DataGrid a name for that to work.
Another option is to bind to an Ancestor. With this solution you dont have to give the DataGrid a name, but it is more complex:
<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.Clvm.Categories}" />

Related

WPF DataGrid group nested list

I have a list of tasks which has a nested list of tasks.
Now I want to show this list grouped in a WPF DataGrid using MVVM pattern. The TaskName of the "outer" Task should be the Title of the Group, all nested Tasks should be shown below.
The result should look like this:
Example
public class ProjectTask
{
public Guid TaskUID { get; set; }
public string TaskName { get; set; }
public DateTime TaskStartDate { get; set; }
public DateTime TaskFinishDate { get; set; }
public List<ProjectTask> SubTasks { get; set; } = new();
}
public List<ProjectTask> ProjectTasks
{
get { return projectTasks; }
set { projectTasks = value; OnPropertyChanged(); }
}
What I tried so far, was creating a CollectionViewSource
<CollectionViewSource x:Key="GroupedTaskList" Source="{Binding ProjectTasks}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="SubTasks" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
and bind it to a DataGrid
<DataGrid AutoGenerateColumns="False" IsReadOnly="True"
ItemsSource="{Binding Source={StaticResource GroupedTaskList}}"
>
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="Name" Binding="{Binding Path=TaskName}" />
<DataGridTextColumn Header="Start" Binding="{Binding Path=TaskStartDate, StringFormat=d}" />
<DataGridTextColumn Header="End" Binding="{Binding Path=TaskFinishDate, StringFormat=d}" />
</DataGrid.Columns>
</DataGrid>
But how can I group the Tasks?
<DataGrid.Columns>
<DataGridTextColumn Header="" AutomationProperties.Name="" IsReadOnly="True" Binding="{Binding Path=TaskName}"/>
<DataGridTextColumn Header="" AutomationProperties.Name="" IsReadOnly="True" Binding="{Binding Path=TaskStartDate, StringFormat=d}"/>
<DataGridTextColumn Header="" AutomationProperties.Name="" IsReadOnly="True" Binding="{Binding Path=TaskEndDate, StringFormat=d}"/>
<DataGridTemplateColumn Header="Sub Tasks" AutomationProperties.Name="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding SubTaskList}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubTask}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
I was able to achieve it using a template column like this, Hope this helps.
Edited After Understanding Question better
<DataGrid ItemsSource="{Binding Tasks}" AutoGenerateColumns="false"
RowDetailsVisibilityMode="Visible"
CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Task Name" Binding="{Binding TaskName}"/>
<DataGridTextColumn Header="Task Start Date" Binding="{Binding TaskStartDate}"/>
<DataGridTextColumn Header="Task End Date" Binding="{Binding TaskStartDate}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding SubTasks}" AutoGenerateColumns="false" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Sub Task Name" Binding="{Binding TaskName}"/>
<DataGridTextColumn Header="Sub Task Start Date" Binding="{Binding TaskStartDate}"/>
<DataGridTextColumn Header="Sub Task End Date" Binding="{Binding TaskStartDate}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Just a quick view of what this does https://imgur.com/OIc9uCr

Binding Combobox inside DataGrid

I have a DataGrid with 3 column inside: Name -DataGridTextColumn, Value-DataGridTextColumn, ParamJson - DataGridTemplateColumn with combobox and has a ICollectionView of ParameterGrid as a source.
There is a ViewModel that provides ICollectionView of ParameterGrid. ParameterGrid contains 4 parameters: Name, Value, ParamJson, SelectedParamJson.
I need to handle all rows of DataGrid, so i use the command that get all dataGrid as a Command Parameter and than iterate over DataGrid.Items (I could use directly ICollectionView?).
The question is How to bind properly SelectedItem in Combobox in DataGrid to SelectedParamJson?
Xaml:
<DataGrid Grid.Row="0"
x:Name="parametersGridView"
AutoGenerateColumns="False"
VerticalScrollBarVisibility="Visible"
CanUserAddRows="False"
ItemsSource="{Binding PropertiesGrid}"
Margin="5"
AlternatingRowBackground="LightGoldenrodYellow">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Path=Name}"
IsReadOnly="True" />
<DataGridTextColumn Header="Value"
Binding="{Binding Path=Value}" />
<DataGridTemplateColumn Header="Parameter" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox MinWidth="100"
MaxWidth="150"
ItemsSource="{Binding Path=ParamsJson}"
SelectedItem="{Binding Path=SelectedParamJson, RelativeSource={RelativeSource AncestorType=DataGrid}}"
StaysOpenOnEdit="True"
IsEditable="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid > <DataGrid Grid.Row="0"
x:Name="parametersGridView"
AutoGenerateColumns="False"
VerticalScrollBarVisibility="Visible"
CanUserAddRows="False"
ItemsSource="{Binding PropertiesGrid}"
Margin="5"
AlternatingRowBackground="LightGoldenrodYellow">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Path=Name}"
IsReadOnly="True" />
<DataGridTextColumn Header="Value"
Binding="{Binding Path=Value}" />
<DataGridTemplateColumn Header="Parameter" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=ParamsJson}"
SelectedItem="{Binding Path=SelectedParamJson, RelativeSource={RelativeSource AncestorType=DataGrid}}"
StaysOpenOnEdit="True"
IsEditable="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid >`
ViewModel
class DataGridViewModel : DependencyObject
{
public DataGridViewModel()
{
var data = new DataEmulator();
PropertiesGrid = CollectionViewSource.GetDefaultView(data.GetCommonParameters());
}
public ICollectionView PropertiesGrid
{
get { return (ICollectionView)GetValue(PropertiesGridProperty); }
set { SetValue(PropertiesGridProperty, value); }
}
public static readonly DependencyProperty PropertiesGridProperty =
DependencyProperty.Register("PropertiesGrid", typeof(ICollectionView), typeof(DataGridViewModel), new PropertyMetadata(null));
public ICommand TestCommand
{
get
{
return new RelayCommand
{
ExecuteAction = a =>
{
// here SelectedParamJson should be as Selected in combobox,
// but it is always null because of the wrong binding
},
CanExecutePredicate = p =>
{
return true;
}
};
}
}
}
public class ParameterGrid
{
public string Name { get; set; }
public string Value { get; set; }
public List<SamResult> ParamsJson { get; set; }
public SamResult SelectedParamJson { get; set; }
}
If you want to bind to the SelectedParamJson property that is defined in the same class as the ParamsJson property, you should not set the RelativeSource property to anything at all:
ItemsSource="{Binding Path=ParamsJson}"
SelectedItem="{Binding Path=SelectedParamJson}"
You should also move the ComboBox to the CellEditingTemplate of the DataGridTemplateColumn.
The thing is you have to define <DataGridTemplateColumn.CellEditingTemplate>, where you should do all bindings and <DataGridTemplateColumn.CellTemplate>, that present value when the cell is not active.
That seems to be strange that you could not bind everything inside <DataGridTemplateColumn.CellTemplate>, because you have to do an additional klick to activate combobox.
Xaml that works for me:
<DataGridTemplateColumn Header="Parameter" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=SelectedParamJson}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox MinWidth="100"
MaxWidth="150"
ItemsSource="{Binding Path=ParamsJson}"
SelectedItem="{Binding Path=SelectedParamJson}"
StaysOpenOnEdit="True"
IsEditable="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
your ancestor is not the datagrid its the datarow you want to get the datacontext from.
so you made a collection view of some source collection like list, this means every row in your Datagrid repesents a IJsonObject Object and there you should have your Collection for the ComboBox Column defined. Like a List that you can bind to the Column and in Turn you can have a IJsonProperty field that you can bind to the selected Item.
So then you should get a Combobox filled with yout List Items and if you select one of them the IJsonProerty Field will be set to the selected Item.
I hope its understandable since I dont really got a code snippet right now.

XAML binding to CompositeCollection

I have only one datagrid in a single view but the collections which are ItemsSource's of this datagrid are in different View Models. So is it possible to bind this single datagrid in view with the collections in two different View Models?
For each row in the grid, display an item from one collection, and an item from the other collection..! to display all columns in one row.
xaml:
DataContext="{DynamicResource ViewModelCombine}">
<Window.Resources>
<vm:ViewModelCombine x:Key="ViewModelCombine"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid>
<DataGrid.Resources>
<CollectionViewSource x:Key="ViewModelPulse" Source="{Binding VP}"/>
<CollectionViewSource x:Key="ViewModeltherapy" Source="{Binding VT}"/>
</DataGrid.Resources>
<DataGrid.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource ViewModelCombine}, Path=VP}" />
<CollectionContainer Collection="{Binding Source={StaticResource ViewModelCombine}, Path=VT}" />
</CompositeCollection>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="AMP" Binding="{Binding AMP}" Width="100"/>
<DataGridTextColumn Header="PW" Binding="{Binding PW}" Width="100" />
<DataGridTextColumn Header="DZ0" Binding="{Binding DZ0}" Width="100" />
<DataGridTextColumn Header="DELTA" Binding="{Binding DELTA}" Width="100" />
<DataGridTextColumn Header="DZ1" Binding="{Binding DZ1}" Width="100"/>
<DataGridTextColumn Header="M" Binding="{Binding M}" Width="100" />
<DataGridTextColumn Header="DZ2" Binding="{Binding DZ2}" Width="100" />
<DataGridTextColumn Header="N" Binding="{Binding N}" Width="100" />
</DataGrid.Columns>
</DataGrid>
</Grid>
xaml.cs:
public MainWindow()
{
InitializeComponent();
ViewModelCombine VMC = new ViewModelCombine();
this.DataContext = VMC;
}
ViewModelCombine.cs
public class ViewModelCombine
{
public ViewModelTherapy VT { get; set; }
public ViewModelPulse VP { get; set; }
public ViewModelCombine()
{
VT = new ViewModelTherapy();
VP = new ViewModelPulse();
}
}
As per the above code, it displays like this Output..but, wanted to display all columns in one row.
So is it possible to bind this single datagrid in view with the collections in two different View Models?
Thanks for your help.
And here is a working example of your code. I replaced the Itemsource of the Datagrid and made ViewModelTherapy and ViewModelPulse to Observable collections.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModelCombine VMC = new ViewModelCombine();
VMC.VP.Add(new ViewModelPulse() { ID = 1, Name = "test1", xaxa = "xaxa" });
VMC.VT.Add(new ViewModelTherapy() { ID = 2 Name = "test2", Description = "desc" });
this.DataContext = VMC;
}
}
public class ViewModelCombine
{
public ObservableCollection<ViewModelTherapy> VT { get; set; }
public ObservableCollection<ViewModelPulse> VP { get; set; }
public ViewModelCombine()
{
VT = new ObservableCollection<ViewModelTherapy>();
VP = new ObservableCollection<ViewModelPulse>();
}
}
public class ViewModelTherapy
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class ViewModelPulse
{
public int ID { get; set; }
public string Name { get; set; }
public string xaxa { get; set; }
}
Xaml:
<Window.Resources>
<CollectionViewSource x:Key="ViewSource1" Source="{Binding VT}"/>
<CollectionViewSource x:Key="ViewSource2" Source="{Binding VP}"/>
<CompositeCollection x:Key="CombinedCollection">
<CollectionContainer Collection="{Binding Source={StaticResource ViewSource1}}" />
<CollectionContainer Collection="{Binding Source={StaticResource ViewSource2}}" />
</CompositeCollection>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{StaticResource CombinedCollection}">
<DataGrid.Columns>
<DataGridTextColumn Header="AMP" Binding="{Binding ID}" Width="100"/>
<DataGridTextColumn Header="PW" Binding="{Binding Name}" Width="100" />
<DataGridTextColumn Header="DZ0" Binding="{Binding xaxa}" Width="100" />
<DataGridTextColumn Header="DELTA" Binding="{Binding Description}" Width="100" />
</DataGrid.Columns>
</DataGrid>
</Grid>
And here is a screenshot of the result
Have you tried the following:
First set the Datagrid itemssource to ViewModelCombine. Then make the following changes in xaml (This example is only a demonstration cause i don't know which values are included in VT and VP but i think you could figure this out):
public MainWindow()
{
InitializeComponent();
ViewModelCombine VMC = new ViewModelCombine();
dgr.DataContext = VMC;
}
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgr">
<DataGrid.Resources>
<CollectionViewSource x:Key="ViewModelPulse" Source="{Binding VP}"/>
<CollectionViewSource x:Key="ViewModeltherapy" Source="{Binding VT}"/>
</DataGrid.Resources>
<DataGrid.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource ViewModelCombine}, Path=VP}" />
<CollectionContainer Collection="{Binding Source={StaticResource ViewModelCombine}, Path=VT}" />
</CompositeCollection>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="AMP" Binding="{Binding VT.AMP}" Width="100"/>
<DataGridTextColumn Header="PW" Binding="{Binding VP.PW}" Width="100" />
<DataGridTextColumn Header="DZ0" Binding="{Binding VP.DZ0}" Width="100" />
<DataGridTextColumn Header="DELTA" Binding="{Binding VP.DELTA}" Width="100" />
<DataGridTextColumn Header="DZ1" Binding="{Binding VT.DZ1}" Width="100"/>
<DataGridTextColumn Header="M" Binding="{Binding VP.M}" Width="100" />
<DataGridTextColumn Header="DZ2" Binding="{Binding VP.DZ2}" Width="100" />
<DataGridTextColumn Header="N" Binding="{Binding VP.N}" Width="100" />
</DataGrid.Columns>
</DataGrid>
</Grid>
UPDATE:
In this link you will find a very good example for the composite collections in wpf. It includes a solution with Listview, Listbox and Gridview.
https://code.msdn.microsoft.com/windowsdesktop/Combining-item-sources-in-65408473
<Window.Resources>
<!--Collection views for the ObservableCollections in the view model or code behind.-->
<CollectionViewSource x:Key="BooksViewSource" Source="{Binding Books}"/>
<CollectionViewSource x:Key="MoviesViewSource" Source="{Binding Movies}"/>
<CollectionViewSource x:Key="AlbumsViewSource" Source="{Binding Albums}"/>
<!--Combine the colection views into a single composite collection-->
<CompositeCollection x:Key="CombinedCollection">
<CollectionContainer Collection="{Binding Source={StaticResource BooksViewSource}}" />
<CollectionContainer Collection="{Binding Source={StaticResource MoviesViewSource}}" />
<CollectionContainer Collection="{Binding Source={StaticResource AlbumsViewSource}}" />
</CompositeCollection>
</Window.Resources>
And then they bind the collection to the controls. I would like to mention that in your code you are missing the Lists. The classes ViewModelPulse and ViewModeltherapy are not Lists of objects so your binding cannot work(as i cannot see their definitions in your question).
Hope that helps

How to create a DatagridTemplateColumn with checkbox and binding to a datasouce?

Apologies in advance if I'm overlooking something - I'm still finding my feet working with Xaml rather than windows forms.
I'm trying to bind a data source to a DataGrid where one of the columns is a checkbox. My original solution worked fine for this, but required the user to double click the checkbox:
<Window x:Class="ExecBoxInvoices.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="507" Width="676">
<Grid Margin="0,0,0,51">
<DataGrid x:Name="InvoiceDG" HorizontalAlignment="Left" Height="165" Margin="134,251,0,0" VerticalAlignment="Top" Width="400" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Generate" Binding="{Binding Generate}" Width="80"/>
<DataGridTextColumn Header="Table_Number" Binding="{Binding Table_Number}" Width="120"/>
<DataGridTextColumn Header="Transaction_Date" Binding="{Binding Transaction_Date}" Width="175"/>
<DataGridTextColumn Header="Transaction_ID" Visibility="Hidden" Binding="{Binding Transaction_ID}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
The new solution for single click checkboxes is displayed below, but doesn't work (error is shown on the Binding="{Binding Generate}"):
<Window x:Class="ExecBoxInvoices.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="507" Width="676">
<Grid Margin="0,0,0,51">
<DataGrid x:Name="InvoiceDG" HorizontalAlignment="Left" Height="165" Margin="134,251,0,0" VerticalAlignment="Top" Width="400" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Generate" Width="60" Binding="{Binding Generate}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Table_Number" Binding="{Binding Table_Number}" Width="120"/>
<DataGridTextColumn Header="Transaction_Date" Binding="{Binding Transaction_Date}" Width="175"/>
<DataGridTextColumn Header="Transaction_ID" Visibility="Hidden" Binding="{Binding Transaction_ID}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
For reference, the code used to set the source is:
InvoiceDG.ItemsSource = recordCollection;
Where recordCollection is a list of:
class InvoiceRow
{
public bool Generate { get; set; }
public string Table_Number { get; set; }
public string Transaction_Date { get; set; }
public string Transaction_ID { get; set; }
public InvoiceRow(bool generate, string table_Number, string transaction_Date, string transaction_ID)
{
this.Generate = generate;
this.Table_Number = table_Number;
this.Transaction_Date = transaction_Date;
this.Transaction_ID = transaction_ID;
}
}
Try this:
<DataGridTemplateColumn Header="Generate" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Generate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Row details in DataGrid - can I bind them to an 'inner' collection?

I have two data structures that look like this -
class Car
{
string CarModel;
string CarColor;
string CarAge;
}
class Person
{
int ID;
string FirstName;
string Surname;
ObservableCollection<Car> Cars;
}
public ObservableCollection<Person> People { get; set; }
So I have a collection of type Car within a collection of type Person. I want to display this in a DataGrid that looks like this where each Person can have zero or more cars -
The following xaml code takes care of the rows but it doesn't handle the row details -
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
<DataGridTextColumn Header="Firstname" Binding="{Binding Firstname}"/>
<DataGridTextColumn Header="Surname" Binding="{Binding Surname}"/>
</DataGrid.Columns>
</DataGrid>
Anyone know if it is possible to display a DataGrid with row details bound to an inner collection, (in this case ObservableCollection<Car> Cars)?
Just put something like an ItemsControl in the RowDetailsTemplate
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
<DataGridTextColumn Header="Firstname" Binding="{Binding Firstname}"/>
<DataGridTextColumn Header="Surname" Binding="{Binding Surname}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Cars}" ItemTemplate="{StaticResource CarTemplate}" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>

Categories