Unexpected behavior of DataGrid

c#, wpf, xaml

Solution

The Problem is solved. Please see Edit 1 and Edit 2 in question for half of the solution.

After Edit 2 I was not able to Sort the data in ComboBox using CollectionViewSource. So, I used OrderBy in my query. Now the data in ComboBox is sorted as expected. Now, my query looks like:

ParentGroups = new ObservableCollection<Group>(db.Groups.OrderBy(x => x.GroupName));

Now the DataGrid works as expected.

I have posted this solution because I think it might help someone in future.

Thanks to everybody to took interest in solving my problem.

Here you can find the working sample:

Project : https://drive.google.com/file/d/0B5WyqSALui0beC05VnpMY1hzV3c/view?usp=sharing

Database : https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing

Problem

I have a very complicated problem. I have tried to check everything and I have worked for near-about 6 hours on this problem. But I am not successful to solve this problem. So, here is the question. Problem: Initially when program loads up: When I click on edit button on any row: When I save that row: When I click on edit button on another row having different Parent Group: XAML looks like below : ``` <CollectionViewSource x:Key="GroupsViewSource" Source="{Binding Groups, UpdateSourceTrigger=PropertyChanged}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="GroupName"/> </CollectionViewSource.SortDescriptions> </CollectionViewSource> <CollectionViewSource x:Key="ParentGroupsViewSource" Source="{Binding ParentGroups}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="GroupName"/> </CollectionViewSource.SortDescriptions> </CollectionViewSource> <DataGrid Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Source={StaticResource GroupsViewSource}}" SelectedItem="{Binding SelectedGroup}" x:Name="dataGrid" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Single" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True" EnableRowVirtualization="False" VirtualizingPanel.IsContainerVirtualizable="False" RowEditEnding="DataGrid_RowEditEnding"> <DataGrid.Resources> <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}"> <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" /> </Style> </DataGrid.Resources> <i:Interaction.Triggers> <i:EventTrigger EventName="RowEditEnding"> <i:InvokeCommandAction Command="{Binding DataGridRowEditEndingCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> <DataGrid.Columns> <DataGridTemplateColumn Header="Group Name" Width="*" SortMemberPath="GroupName"> <DataGridTemplateColumn.HeaderTemplate> <DataTemplate> <Grid IsHitTestVisible="True"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{TemplateBinding Content}"/> <!--FILTER EXPANDER--> <Expander Grid.Column="1" IsHitTestVisible="True" VerticalAlignment="Top" Margin="30 0 0 0" ToolTip="Filter"> <Border IsHitTestVisible="True" BorderThickness="1" Margin="-160 5 0 0" MinWidth="200" Height="31" > <TextBox Text="{Binding DataContext.SearchGroupName, ElementName=uc, UpdateSourceTrigger=PropertyChanged}" TextChanged="SearchTextBox_TextChanged" ToolTip="Enter Group Name to search" FontSize="16" BorderThickness="1" /> </Border> </Expander> </Grid> </DataTemplate> </DataGridTemplateColumn.HeaderTemplate> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding GroupName}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellEditingTemplate> <DataTemplate> <TextBox Text="{Binding GroupName}" /> </DataTemplate> </DataGridTemplateColumn.CellEditingTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Parent Group" Width="*" SortMemberPath="ParentID"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellEditingTemplate> <DataTemplate> <ComboBox ItemsSource="{Binding Source={StaticResource ParentGroupsViewSource}}" DisplayMemberPath="GroupName" SelectedValue="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" SelectedValuePath="GroupName"/> </DataTemplate> </DataGridTemplateColumn.CellEditingTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Edit" Width="50" IsReadOnly="True"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Button x:Name="btnEdit" Style="{StaticResource ResourceKey=EditButton}" Height="35" Width="35" Visibility="{Binding DataContext.IsInEdit, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Converter={StaticResource boolToVisibilityInverseConverter}}" Click="EditButton_Click" Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/> <Button x:Name="btnSave" Grid.Row="1" Style="{StaticResource ResourceKey=SaveButton}" Height="35" Width="35" Visibility="{Binding DataContext.IsInEdit, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Converter={StaticResource boolToVisibilityConverter}}" Click="SaveButton_Click" Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/> </Grid> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Delete" Width="70" IsReadOnly="True"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Button x:Name="btnDelete" Style="{StaticResource ResourceKey=DeleteButton}" Height="35" Width="35" Visibility="{Binding DataContext.IsInEdit, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Converter={StaticResource boolToVisibilityInverseConverter}}" Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/> <Button x:Name="btnCancel" Grid.Row="1" Style="{StaticResource ResourceKey=CancelButton}" Height="35" Width="35" Visibility="{Binding DataContext.IsInEdit, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Converter={StaticResource boolToVisibilityConverter}}" Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" Click="CancelButton_Click"/> </Grid> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> ``` Here is the code-Behind: ``` public partial class ListView : UserControl { ERPLiteDBContext db = new ERPLiteDBContext(); public ListView() { InitializeComponent(); } private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DependencyObject dep = (DependencyObject)e.OriginalSource; if (dep == null) return; while (dep != null && !(dep is DataGridCell)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; if (dep is DataGridCell) { if (!((DataGridCell)dep).IsReadOnly) { if (!((DataGridCell)dep).IsEditing) e.Handled = true; } } while (dep != null && !(dep is DataGridRow)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; if (dep is DataGridRow) { ((DataGridRow)dep).IsSelected = true; } while (dep != null && !(dep is DataGrid)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; if (dep is DataGrid) { ((DataGrid)dep).Focus(); } } private void EditButton_Click(object sender, RoutedEventArgs e) { int rowIndex = 0; DependencyObject dep = (DependencyObject)e.OriginalSource; while (dep != null && !(dep is DataGridCell)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; DataGridRow row = null; if (dep is DataGridCell) { while (dep != null && !(dep is DataGridRow)) { dep = VisualTreeHelper.GetParent(dep); } row = (DataGridRow)dep; rowIndex = FindRowIndex(row); } while (dep != null && !(dep is DataGrid)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; DataGrid dg = (DataGrid)dep; dg.CurrentCell = new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[0]); dg.BeginEdit(); for (int column = 0; column <= dg.Columns.Count - 1; column++) { if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly)) { GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = true; } } var rows = GetDataGridRows(dg); foreach (DataGridRow r in rows) { if (!(r.IsEditing)) { r.IsEnabled = false; } } } private void SaveButton_Click(object sender, RoutedEventArgs e) { int rowIndex = 0; DependencyObject dep = (DependencyObject)e.OriginalSource; while (dep != null && !(dep is DataGridCell)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; DataGridRow row = null; if (dep is DataGridCell) { while (dep != null && !(dep is DataGridRow)) { dep = VisualTreeHelper.GetParent(dep); } row = (DataGridRow)dep; rowIndex = FindRowIndex(row); } while (dep != null && !(dep is DataGrid)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; DataGrid dg = (DataGrid)dep; dg.CommitEdit(DataGridEditingUnit.Row, true); for (int column = 0; column <= dg.Columns.Count - 1; column++) { if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly)) { GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = false; } } var rows = GetDataGridRows(dg); foreach (DataGridRow r in rows) { r.IsEnabled = true; } } private void CancelButton_Click(object sender, RoutedEventArgs e) { DependencyObject dep = (DependencyObject)e.OriginalSource; int rowIndex = 0; DataGridRow row = null; while (dep != null && !(dep is DataGridRow)) { dep = VisualTreeHelper.GetParent(dep); } row = (DataGridRow)dep; rowIndex = FindRowIndex(row); while (dep != null && !(dep is DataGrid)) { dep = VisualTreeHelper.GetParent(dep); } if (dep == null) return; DataGrid dg = (DataGrid)dep; var rows = GetDataGridRows(dg); dg.CancelEdit(DataGridEditingUnit.Row); for (int column = 0; column <= dg.Columns.Count - 1; column++) { if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly)) { GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = false; } } foreach (DataGridRow r in rows) { r.IsEnabled = true; } } private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e) { DataGrid dg = (DataGrid)sender; foreach (DataGridRow row in GetDataGridRows(dg)) { row.IsEnabled = true; } } private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) { if (dataGrid.SelectedItem != null) { dataGrid.ScrollIntoView(dataGrid.SelectedItem); } } public DataGridCell GetDataGridCell(DataGridCellInfo cellInfo) { var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item); if (cellContent != null) return (DataGridCell)cellContent.Parent; return null; } private int FindRowIndex(DataGridRow row) { DataGrid dataGrid = ItemsControl.ItemsControlFromItemContainer(row) as DataGrid; int index = dataGrid.ItemContainerGenerator.IndexFromContainer(row); return index; } public IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid) { var itemsSource = grid.ItemsSource as IEnumerable; if (null == itemsSource) yield return null; foreach (var item in itemsSource) { var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow; if (null != row) yield return row; } } } ``` At Last here is the code for ViewModel: ``` public class ListViewModel : ViewModelBase { ERPLiteDBContext db = new ERPLiteDBContext(); public ListViewModel() { Groups = new ObservableCollection<Group>(db.Groups); ParentGroups = new ObservableCollection<Group>(db.Groups); EditCommand = new RelayCommand(Edit); SaveCommand = new RelayCommand(Save); DeleteCommand = new RelayCommand(Delete); CancelCommand = new RelayCommand(Cancel); DataGridRowEditEndingCommand = new RelayCommand(DataGridRowEditEnding); SearchGroupName = ""; IsInEdit = false; } public RelayCommand EditCommand { get; set; } public RelayCommand SaveCommand { get; set; } public RelayCommand DeleteCommand { get; set; } public RelayCommand CancelCommand { get; set; } public RelayCommand DataGridRowEditEndingCommand { get; set; } private string _searchGroupName; public string SearchGroupName { get { return _searchGroupName; } set { if (value == null) { SearchGroupName = ""; } else { _searchGroupName = value; } OnPropertyChanged("SearchGroupName"); SelectedGroup = db.Groups.AsEnumerable().OrderBy(x => x.GroupName).Where(x => x.GroupName.StartsWith(SearchGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (SelectedGroup == null) { SelectedGroup = db.Groups.AsEnumerable().OrderBy(x => x.GroupName).Where(x => x.GroupName.Contains(SearchGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); } } } private ObservableCollection<Group> _groups; public ObservableCollection<Group> Groups { get { return _groups; } set { _groups = value; OnPropertyChanged("Groups"); } } private Group _selectedGroup; public Group SelectedGroup { get { return _selectedGroup; } set { _selectedGroup = value; OnPropertyChanged("SelectedGroup"); if (value != null) { ParentGroups = new ObservableCollection<Group>(db.Groups.Where(x => x.GroupID != value.GroupID)); ParentGroups.Add(new Group { GroupID = -1, GroupName = "Primary" }); } } } private ObservableCollection<Group> _parentGroups; public ObservableCollection<Group> ParentGroups { get { return _parentGroups; } set { _parentGroups = value; OnPropertyChanged("ParentGroups"); } } private Group _selectedParentGroup; public Group SelectedParentGroup { get { return _selectedParentGroup; } set { _selectedParentGroup = value; OnPropertyChanged("SelectedParentGroup"); } } private bool _isInEdit; public bool IsInEdit { get { return _isInEdit; } set { _isInEdit = value; OnPropertyChanged("IsInEdit"); } } private void Edit(object obj) { IsInEdit = true; } private void Save(object obj) { IsInEdit = false; SaveToDataBase(); } private void Delete(object obj) { } private void Cancel(object obj) { IsInEdit = false; } private void DataGridRowEditEnding(object obj) { IsInEdit = false; } public void SaveToDataBase() { Group currentGroup = db.Groups.Where(x => x.GroupID == SelectedGroup.GroupID).FirstOrDefault(); if (currentGroup != null) { currentGroup.GroupName = SelectedGroup.GroupName; if (SelectedGroup.ParentID == -1) { currentGroup.ParentID = null; } else { currentGroup.ParentID = SelectedGroup.ParentID; } db.SaveChanges(); } } } ``` I am not sure where the problem lies, so, I posted almost all the code here. I have created a sample project that can be downloaded from the link below: Project: https://drive.google.com/file/d/0B5WyqSALui0bTTNsMm5ISHV3VEk/view?usp=sharing DataBase: https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing Edit 1: The original problem(as shown in the images) is solved To solve the problem I removed CollectionViewSource and bound the ComboBox directly to the ViewModel Property. Now I get another error: When I see InnerException: I have searched the net for this error but then everywhere I can see that if I insert NULL value into the foreign Key column then I get this error. But my column is Nullable and I need nulls to be inserted. edit 2: I have solved the error mentioned in edit 1. Added a new record as first record in the table called Primary. And removed all the coding related to Primary Group. Replaced NULLs in database with the primary group. Now the only problem left is using a CollectionViewSource as the source of combobox to sort the data instead of Binding the combobox directly to the Property inside ViewModel. Can anybody answer this question????

Original source