Unexpected behavior of DataGrid

Vis*_*hal 8 c# wpf xaml

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>
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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????

Vis*_*hal 4

问题已经解决了。请参阅相关的编辑 1 和编辑 2,了解一半的解决方案。

编辑 2 后,我无法使用 CollectionViewSource 对 ComboBox 中的数据进行排序。因此,我在查询中使用了 OrderBy。现在 ComboBox 中的数据已按预期排序。现在,我的查询如下所示:

ParentGroups = new ObservableCollection<Group>(db.Groups.OrderBy(x => x.GroupName));
Run Code Online (Sandbox Code Playgroud)

现在 DataGrid 按预期工作。

我发布这个解决方案是因为我认为它可能会对将来的某人有所帮助。

感谢大家有兴趣解决我的问题。

您可以在这里找到工作示例:

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

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