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????
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
You are mixing Commands
and event handlers - never a good idea. I believe I understand what you are trying to do. This will be the pure MVVM solution with no event handlers route.
Before we begin, this assumes that your DataGrid
is bound to an ObservableCollection
of ViewModel
objects which represent the individual rows. This solves a lot of the hassle and complexity with RelativeSource
bindings to parent ViewModel
objects as you currently have in your XAML and simplifies the implementation of per row Commands
(because the data and the Command are encapsulated within the same class if you use DelegateCommand
- as you are using PRISM I am assuming you are using it everywhere).
Firstly you should add a style for DataGridCell.IsEditing
property to bind it to an IsEditing
boolean property on your ViewModel
. Change your Edit Button to bind to a Command
that sets IsEditing
on your row ViewModel
to true
- the grid should react by going into Edit mode.
You might want to consider binding your edit templates to Edit specific values so that you don't overwrite your original data in the event of Cancelling Edit. Also remember to set the Binding to Mode=TwoWay
with UpdateSourceTrigger=PropertyChanged
You can then add Interaction Event To Command mappers to the various Edit template controls such that if you hit Enter that it invokes the SaveCommand
and ends edit mode by setting IsEditing
on your ViewModel back to false.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With