我有一个非常复杂的问题。我试图检查一切,我已经在这个问题上工作了大约6个小时。但我没有成功解决这个问题。所以,问题就在这里。
问题:
最初程序加载时:
当我点击任何一行的编辑按钮时:
当我保存该行时:
当我点击具有不同父组的另一行上的编辑按钮时:
XAML如下所示:
<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>
这是代码 - 背后:
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这里是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();
}
}
}
我不确定问题出在哪里,因此,我在这里发布了几乎所有的代码。
我创建了一个示例项目,可以从以下链接下载:
项目:
https://drive.google.com/file/d/0B5WyqSALui0bTTNsMm5ISHV3VEk/view?usp=sharing
数据库:
https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing
修改1:
原始问题(如图中所示)已解决
为了解决这个问题,我删除了CollectionViewSource并将ComboBox直接绑定到ViewModel属性。现在我又收到了一个错误:
当我看到InnerException时:
我在网上搜索了这个错误,但随后我发现如果我将NULL值插入外键列,那么我就会收到此错误。但我的列是Nullable,我需要插入空值。
编辑2:
我已解决了编辑1中提到的错误。
在名为Primary的表中添加了一条新记录作为第一条记录。并删除了与Primary Group相关的所有编码。用主组替换数据库中的NULL。
现在唯一的问题是使用CollectionViewSource作为组合框的源来对数据进行排序,而不是将组合框直接绑定到ViewModel中的Property。谁能回答这个问题????
答案 0 :(得分:3)
您正在混合Commands
和事件处理程序 - 从来不是一个好主意。我相信我明白你要做什么。这将是纯MVVM解决方案,没有事件处理程序路由。
在开始之前,我们假设您的DataGrid
绑定到代表各行的ObservableCollection
个ViewModel
个对象。这解决了当前在XAML中对RelativeSource
绑定到父ViewModel
个对象的麻烦和复杂性,并简化了每行Commands
的实现(因为数据和命令)如果您使用DelegateCommand
,则封装在同一个类中 - 因为您正在使用PRISM我假设您在任何地方都使用它。)
首先,您应该为DataGridCell.IsEditing
属性添加一个样式,以将其绑定到IsEditing
上的ViewModel
布尔属性。更改您的修改按钮以绑定到Command
,IsEditing
将行ViewModel
上的true
设置为Mode=TwoWay
- 网格应通过进入编辑模式做出反应。
您可能需要考虑将编辑模板绑定到“编辑特定值”,以便在“取消编辑”时不会覆盖原始数据。另请记住将绑定设置为UpdateSourceTrigger=PropertyChanged
SaveCommand
然后,您可以将“交互事件到命令”映射器添加到各种“编辑”模板控件,这样,如果您按Enter键调用IsEditing
并通过将ViewModel上的{{1}}设置为false来结束编辑模式
答案 1 :(得分:3)
问题解决了。有关解决方案的一半,请参阅相关的编辑1和编辑2.
编辑2之后,我无法使用CollectionViewSource对ComboBox中的数据进行排序。所以,我在查询中使用了OrderBy。现在,ComboBox中的数据按预期排序。现在,我的查询如下:
ParentGroups = new ObservableCollection<Group>(db.Groups.OrderBy(x => x.GroupName));
现在DataGrid按预期工作。
我已经发布了这个解决方案,因为我认为它可能会对将来有所帮助。
感谢大家有兴趣解决我的问题。
在这里您可以找到工作样本:
项目:https://drive.google.com/file/d/0B5WyqSALui0beC05VnpMY1hzV3c/view?usp=sharing
数据库:https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing