Combine data from ObservableCollection in the View Model - c#

I have integrated two interfaces as Observable Collections in the View Model. They are as follows,
Product Results:
public interface IProdResult
{
string Name { get; set; } // product name
IMeasureResult Result { get; } // product result
IPos NominalPos { get; } // nominal pose
IPos Deviation { get; } // deviation
string PinType { get; } // product type
IPresentationData Presentation { get; } //Presentation Data
}
Group Results:
public interface IGroupResult
{
IList<IPocketResult> PocketResults { get; }
ResultEnum Result { get; } // ProductResult
string Identifier { get; } // Identifier
DateTime TimeStamp { get; } // Timestamp
IPresentationData Presentation { get; } //Presentation data
}
My View Model is as follows
public class AnalysisDataGridViewModel : SubPageViewModel
{
#region Fields
private ObservableCollection<IProdResult> _product
= new ObservableCollection<IProdResult>();
private ObservableCollection<IGroupResult> _group
= new ObservableCollection<IGroupResult>();
public MainViewModel()
{
Coordinates = new Dictionary<Coordinate, NotifingBoolean>();
var services = App.Container.Resolve<IProductionServices>();
services.ProductSelectionOnservable
.SelectMany(group=> group.GroupResults)
.SelectMany(products => products.ProductResults)
.Subscribe(product=> ProdView.Add(product));
services.ProductSelectionOnservable
.SelectMany(group => group.GroupResults)
.Subscribe(group=> GroupView.Add(group));
}
public ObservableCollection<IProdResult> ProdView=> _product;
public ObservableCollection<IGroupResult> GroupView=> _group;
}
Now I need to take the Identifier from IGroupResult and combine it with the IProdResult so that I can display them in a datagrid column as Identifier. Every Group has a list of products with the same group identifier and they need to be displayed for every product row in the data grid.
My datagrid is as follows
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid VerticalAlignment="Top" HorizontalAlignment="Left">
<DataGrid Grid.Row="1"
AutoGenerateColumns="False"
HeadersVisibility="All"
SelectionMode="Extended"
SelectionUnit="FullRow"
CanUserReorderColumns="True"
ItemsSource="{Binding ProdView}">
<DataGrid.Columns>
<DataGridTextColumn Header="{DynamicResource Name}"
Width="Auto"
Binding="{Binding Name}" />
<DataGridTextColumn Header="{DynamicResource NominalPoseX}"
Width="Auto"
Binding="{Binding NominalPos.X}" />
<DataGridTextColumn Header="{DynamicResource NominalPoseY}"
Width="Auto"
Binding="{Binding NominalPos.Y}" />
<DataGridTextColumn Header="{DynamicResource NominalPoseZ}"
Width="Auto"
Binding="{Binding NominalPos.Z}" />
<DataGridTextColumn Header="{DynamicResource DeviationX}"
Width="Auto"
Binding="{Binding Deviation.X}" />
<DataGridTextColumn Header="{DynamicResource DeviationY}"
Width="Auto"
Binding="{Binding Deviation.Y}" />
<DataGridTextColumn Header="{DynamicResource DeviationZ}"
Width="Auto"
Binding="{Binding Deviation.Z}" />
<DataGridTextColumn Header="{DynamicResource ResultX}"
Width="Auto"
Binding="{Binding Result.X}" />
<DataGridTextColumn Header="{DynamicResource ResultY}"
Width="Auto"
Binding="{Binding Result.Y}" />
<DataGridTextColumn Header="{DynamicResource ResultZ}"
Width="Auto"
Binding="{Binding Result.Z}" />
</DataGrid.Columns>
I looked into the following answers in Link1 and using either Composite Collection or CollectionViewSource but could not find a proper solution. Any help will be greately useful.

Related

new row in datagrid with custom data

I have two datagrids. The first one is an overview about a time tracking. The second one shows some details.
For example:
The first datagrid contains each day, where a employee works. The second datagrid contains the time stamping for a day.
However, when I add a new stamping row, the controls in the datagrid are empty. That is not really a problem but the datepicker shows the date 01/01/0001.
what i want is when i add a new row, the date field should be filled with the date from the first datagrid.
VIEW:
<DataGrid Grid.Column="0"
IsReadOnly="True"
AutoGenerateColumns="False"
Grid.Row="0"
x:Name="DgStundenView"
Margin="0 0 10 0"
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding TimeOverviewList}">
<DataGrid.Columns>
<DataGridTextColumn Header="Wochentag" Binding="{Binding Wochentag}" />
<DataGridTextColumn Header="Datum" Binding="{Binding Tag}" />
<DataGridTextColumn Header="Arbeitszeit Soll" Binding="{Binding ArbeitszeitInStunden}" />
<DataGridTextColumn Header="Arbeitszeit gesamt" Binding="{Binding GesamtDauerInStunden}" />
<DataGridTextColumn Header="Pausenzeit" Binding="{Binding Pausenzeit}" />
<DataGridTextColumn Header="Pausendifferenz" Binding="{Binding PausenDifferenzInStunden}" />
<DataGridTextColumn Header="Arbeitszeit inkl. Pause" Binding="{Binding TatsaechlicheDauerInStunden}" />
<DataGridCheckBoxColumn Header="Status" Binding="{Binding StempelungVorhanden,Mode=OneWay}" />
<DataGridTextColumn Header="Info" Binding="{Binding Info}" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Wochentag}" Value="Sa">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
<DataTrigger Binding="{Binding Wochentag}" Value="So">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<DataGrid.Resources>
<x:Array x:Key="Reasons" Type="system:String">
<system:String>NICHT ANWENDBAR</system:String>
<system:String>KRANK</system:String>
<system:String>GANZER TAG URLAUB</system:String>
</x:Array>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}"/>
<DataGridTemplateColumn Header="KOMMEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding ComeBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="KOMMEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding ComeBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN DATUM">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding GoBooking}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="GEHEN UHRZEIT">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<materialDesign:TimePicker Text="{Binding GoBookingTime, StringFormat=t}"
Is24Hours="True">
</materialDesign:TimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn Header="GRUND"
ItemsSource="{StaticResource Reasons}"
TextBinding="{Binding Info}"/>
</DataGrid.Columns>
</DataGrid>
VIEW MODEL
[CanBeNull] private ClassTimeTrackingDayStamp _selectedItem;
[CanBeNull]
public ClassTimeTrackingDayStamp SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
if (_selectedItem != null)
TimeDetailList = new ObservableCollection<ClassTimeTrackingTimeStamp>(_selectedItem.Stempelungen);
}
}
private ObservableCollection<ClassTimeTrackingDayStamp> _timeOverviewList;
public ObservableCollection<ClassTimeTrackingDayStamp> TimeOverviewList
{
get { return _timeOverviewList; }
set
{
_timeOverviewList = value;
OnPropertyChanged();
}
}
[CanBeNull] private ObservableCollection<ClassTimeTrackingTimeStamp> _timeDetailList;
[CanBeNull]
public ObservableCollection<ClassTimeTrackingTimeStamp> TimeDetailList
{
get { return _timeDetailList; }
set
{
_timeDetailList = value;
OnPropertyChanged();
}
}
MODEL
public class ClassTimeTrackingTimeStamp : ViewModelBase
{
public DateTime ComeBooking { get; set; }
public DateTime GoBooking { get; set; }
public TimeSpan ComeBookingTime { get; set; }
public TimeSpan GoBookingTime { get; set; }
public int Id { get; set; }
public string Info { get; set; }
}
Update
EVENT OVER MVVM VIEW
<DataGrid Grid.Column="0"
Grid.Row="1"
x:Name="DgStempelungen"
Margin="0 10 10 0"
AutoGenerateColumns="False"
CanUserAddRows="True"
SelectedItem="{Binding SelectedValue}"
ItemsSource="{Binding TimeDetailList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AddingNewItem">
<i:InvokeCommandAction Command="{Binding Path=AddNewItemCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
VIEW MODEL
public ICommand AddNewItemCommand { get; set; }
#endregion Properties
#region Events
public event EventHandler AddNewItemEventHandler;
#endregion
#region Constructor
public WTimeClockOverviewVM()
{
AddNewItemCommand = new RelayCommand(o =>
{
this.AddNewItemEventHandler?.Invoke(this, EventArgs.Empty);
});
}
CODE BEHIND
public WTimeClockOverview()
{
InitializeComponent();
var vm = (WTimeClockOverviewVM)DataContext;
if(vm == null) return;
vm.AddNewItemEventHandler += (sender, args) =>
{
var selectedItemInFirstDg = vm.SelectedItem;
if (selectedItemInFirstDg != null)
{
var newItem = new ClassTimeTrackingTimeStamp();
newItem.ComeBooking = selectedItemInFirstDg.Tag;
}
};
}
Thank you for your help :)
Best regards
Levin
In second DataGrid, you can subscribe to AddingNewItem event
<DataGrid x:Name="DgStempelungen"
AddingNewItem="DgStempelungen_OnAddingNewItem"
...
In code behind
private void DgStempelungen_OnAddingNewItem(object sender, AddingNewItemEventArgs e)
{
// I'll assume that the Model's Name in first DataGrid collection is StudentItem!
var selectedItemInFirstDg = DgStundenView.SelectedItem as StudentItem;
// or you can get the selected item from the DataContext:
// (DgStundenView.DataContext as MyViewModel)?.SelectedItem;
if (selectedItemInFirstDg != null){
// create the new row with data from selectedItemInFirstDg, ex. date, etc...
e.NewItem = new ClassTimeTrackingDayStamp
{
ComeBooking = selectedItemInFirstDg.Date; // for example
};
}
}

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

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 add empty row to the DataGrid?

I need to allow the user to add the information that is directly in the DataGrid but put the "CanUserAddRows" property does not work, only appears as follows:
This is my DataGrid:
<DataGrid x:Name="dtgPersons" Grid.Row="3" CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTextColumn Header="N°" Width="*" />
<DataGridTextColumn Header="Name" Width="*" />
<DataGridTextColumn Header="Carrer" Width="*" />
<DataGridTextColumn Header="Group" Width="*" />
<DataGridTextColumn Header="Age" Width="*" />
</DataGrid.Columns>
</DataGrid>
EDIT
This is the new Grid code:
<DataGrid x:Name="dtgPerson" Grid.Row="3" ItemsSource="{Binding lstPerson}" AutoGenerateColumns="False" CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTextColumn Header="N°" Width="*" Binding="{Binding No}" />
<DataGridTextColumn Header="Name" Width="*" Binding="{Binding Name}" />
<DataGridTextColumn Header="Carrer" Width="*" Binding="{Binding Carrer}" />
<DataGridTextColumn Header="Group" Width="*" Binding="{Binding Group}" />
<DataGridTextColumn Header="Age" Width="*" Binding="{Binding Age}" />
</DataGrid.Columns>
</DataGrid>
And this is my code behind:
My ObservableCollection:
public ObservableCollection<Person> lstPerson { get; set; }
My MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = this;
lstPerson = new ObservableCollection<Person>();
}
My Person class
public class Person
{
public int No { get; set; }
public string Name { get; set; }
public string Carrer { get; set; }
public string Group { get; set; }
public int Age { get; set; }
}
1) Change you grid xaml
<DataGrid ItemsSource="{Binding GridCollection}"
CanUserAddRows="True"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="N°" Binding="{Binding Number}" Width="*" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
<DataGridTextColumn Header="Carrer" Binding="{Binding Career}" Width="*" />
<DataGridTextColumn Header="Group" Binding="{Binding Group}" Width="*" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="*" />
</DataGrid.Columns>
</DataGrid>
2) Define your collection in ViewModel or CodeBehind
public ObservableCollection<gridDataModel> GridCollection { get; set; }
3) Init collection before use
public MainWindow()
{
InitializeComponent();
DataContext = this;
GridCollection = new ObservableCollection<gridDataModel>();
}
4) After all you will get this, you can add items in grid via GUI and they will store in collection
You must define ItemsSource.
In your code behind:
List<YourObj> list = new List<YourObj>();
dtgPersons.ItemsSource = list;

Binding datagrid WPF to two different classes with lists?

Of course I have searched everywhere on the web but I can't find the solution of my problem.
My problem
I have Patient Class that contains a list of Audiograms (Audiogramm Class).
Actually, I can display list of Patients in the DataGrid. But I want to display, for each patient, his Audiogram in a DataGrid (WPF).
class Patient
{
public Patient(string patientid, string genre, string createdate)
{
this.patientID = patientid;
this.genre = genre;
this.createDate = createdate;
audiogram = new List<Audiogram>();
}
public string patientID { get; set; }
public string genre{ get; set; }
public string createDate { get; set; }
public List<Audiogram> audiogram { get; set; }
}
class Audiogram
{
public Audiogram(string typeData, string actionDate)
{
this.typeData = typeData;
this.actionData = actionData;
}
public string typeData { get; set; }
public string actionData { get; set; }
}
XmlNodeList nodeList = root.SelectNodes("/pt:NOAH_Patients_Export/pt:Patient/pt:Patient", nsmgr);
foreach (XmlNode node in nodeList)
{
XmlNodeList nodeListAudio = node.SelectNodes("//pt:Actions", nsmgr);
Patient patient = new Patient(node["pt:NOAHPatientId"].InnerText, node["pt:Gender"].InnerText, node["pt:CreateDate"].InnerText);
foreach (XmlNode nodeAudio in nodeListAudio)
{
Audiogram audiogramme = new Audiogram(nodeAudio["pt:TypeOfData"].InnerText, nodeAudio["pt:ActionDate"].InnerText);
patient.audiogram.Add(audiogramme);
}
listPatient.Add(patient);
}
dataGrid_XML.ItemsSource = listPatient;
<DataGrid x:Name="dataGrid_XML" AutoGenerateColumns="false" ItemsSource="{Binding Patient}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding patientID}" Header="ID Patient"/>
<DataGridTextColumn Binding="{Binding genre}" Header="Genre"/>
<DataGridTextColumn Binding="{Binding createDate}" Header="Date création"/>
<DataGridTextColumn Binding="{Binding audiogram.typeData}" Header="Type de données"/>
<DataGridTextColumn Binding="{Binding audiogram.actionData}" Header="Date de dernière action"/>
</DataGrid.Columns>
</DataGrid>
You should go with RowDetails Template
<DataGrid Name="dgUsers" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Birthday" Binding="{Binding Birthday}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<TextBlock Text="{Binding audiogram.actionData}" Margin="10" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
why you set your itemssource twice? dataGrid_XML.ItemsSource = listPatient; and ItemsSource="{Binding Patient}"? and your xaml did not work because a List has no property typeData. :) its your item within your list which has this property.
so if you wanna bind your audiogram collection you need some kind of itemscontrol. if you wanna do all in one datagrid check out RowDetailsTemplate.
if you wanna have your mastergrid and a second list for eg your selected row, try
<DataGrid AutoGenerateColumns="false" ItemsSource="{Binding ElementName=dataGrid_XML, Path=SelectedItem.audiogram}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding typeData}" Header="Type de données"/>
<DataGridTextColumn Binding="{Binding actionData}" Header="Date de dernière action"/>
</DataGrid.Columns>
</DataGrid>
I've found one working solution, you made me think to datagrid.items.add()
I've one question, how to delete an empty cell (the first cell) of typeData and actionData column ?
C#
foreach (XmlNode node in nodeList)
{
XmlNodeList nodeListAudio = node.SelectNodes("//pt:Actions", nsmgr);
Patient patient = new Patient(node["pt:NOAHPatientId"].InnerText, node["pt:Gender"].InnerText, node["pt:CreateDate"].InnerText);
dataGrid_XML.Items.Add(patient);
foreach (XmlNode nodeAudio in nodeListAudio)
{
Audiogram audiogramme = new Audiogram(nodeAudio["pt:TypeOfData"].InnerText, nodeAudio["pt:ActionDate"].InnerText);
dataGrid_XML.Items.Add(audiogramme);
}
}
XAML
<DataGrid x:Name="dataGrid_XML" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding patientID}" Header="ID Patient" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding genre}" Header="Genre" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding createDate}" Header="Date création" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding typeData}" Header="Type de données" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding actionData}" Header="Date de dernière action" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>

Categories