绑定到WPF DataGrid时,DataGrid的编辑功能'EditItem'在此视图中不允许。

22

我已经阅读了至少4个小时关于这个问题,似乎它是列表类型,但我有一个情况:

一个拥有集合属性的ObservableCollection。

我定义了第一个DataGrid,在该部分中:

<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <!-- second Datagrid here, binding to Level2 property of my Observable collection -->
    </DataTemplate>
<DataGrid.RowDetailsTemplate>

一切都很好,屏幕上的所有东西都符合我的预期......但是:

  1. 如果尝试修改DataGrid1单元格,它会允许我这样做。
  2. 如果尝试修改DataGrid2单元格,它会抛出异常'EditItem' is not allowed for this view

我错过了什么吗?

这是我的模型:

public partial class Level1
{
    public Level1()
    {
        this.Level2 = new HashSet<Level2>();
    }

    public decimal IdLevel1 { get; set; }
    public decimal IdLevel2 { get; set; }
    public string StrDescripcionTipoAsociado {get;set;}

    public virtual Level2 Level2{ get; set; }
}

public partial class Level2
{
    public decimal IdLevel1 { get; set; }
    public decimal IdLevel3 { get; set; }

    public virtual Level3 Level3{ get; set; }
}

public partial class Level3
{
    public decimal IdLevel3 { get; set; }
    public decimal NumIdConcepto {get;set;}
    public string StrDescripcionConcepto {get;set;}
}

编辑:XAML 代码:

    <DataGrid Grid.Row="1" 
              ItemsSource="{Binding Level1}" 
              AutoGenerateColumns="False" 
              SelectionMode="Single"
              GridLinesVisibility="Vertical"
              CanUserAddRows="True"
              CanUserDeleteRows="True"
              x:Name="GridTipoAsociado">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Tipo de asociado" x:Name="TipoUsuarioSeleccionado">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Label Style="{StaticResource ResourceKey=FontElemNivel1}" Content="{Binding StrDescripcionTipoAsociado}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <TextBox Style="{StaticResource ResourceKey=FontElemNivel2}" Text="{Binding StrDescripcionTipoAsociado }"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid Grid.Row="1" 
                          ItemsSource="{Binding Level2}" 
                          AutoGenerateColumns="False" 
                          SelectionMode="Single"
                          SelectionUnit="Cell"
                          GridLinesVisibility="Vertical"
                          CanUserAddRows="True"
                          CanUserDeleteRows="True"                            
                          x:Name="GridItems">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="Id Item">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Label Content="{Binding NumIdConcepto}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTemplateColumn Header="Items">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Label Content="{Binding Level3.StrDescripcionConcepto}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                            <DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Level3.StrDescripcionConcepto}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellEditingTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>

你能发布DataGrid2的XAML和其itemsSource集合的代码吗? - Nitin
是的,我将发布XAML文件,但我没有任何后台代码。 - Juan Pablo Gomez
Level3.StrDescripcionConcepto. 这个属性在哪里? - Nitin
抱歉,在我的模型样本中丢失了它。 - Juan Pablo Gomez
你如何将ItemsSource绑定到单个对象ItemsSource="{Binding Level2}"?它应该是一个集合。 - Nitin
显示剩余4条评论
9个回答

22

我尝试过这种方式,问题在于你将Level2集合初始化为Hashset<>。在IEditableCollectionView.EditItem()尝试更新Hashset<>中的项时,会抛出此错误。 我将集合初始化为 List<>,它可以正常工作。

我不确定为什么无法更新hashset中的项,需要深入研究一下。但是将Hashset<>更改为List<>将解决此错误。

希望能有所帮助。

谢谢


12
Datagriditemsource绑定到Hashset不会生成实现IEditableCollectionView的集合视图,而使用List会生成实现该接口的ListCollectionView,因此可以正常工作。请注意,本句中的"the reason is because"是重复的用法,建议改为"because"或"the reason is"。 - jamesSampica
此代码是由 EF 自动生成的,这种更改会影响性能吗?或者对我的关系模型是否有其他问题?如果没有,为什么 EF 将集合定义为 HashSet? - Juan Pablo Gomez
@nit 非常感谢你的帮助,现在我需要处理 EF 生成代码,以维护使用 List 生成的类,而不是 Hashset。再次非常感谢你的帮助。 - Juan Pablo Gomez
2
这让我意识到了我的问题;在我的情况下,我只是愚蠢地将网格绑定到了一个LINQ查询的结果上。解决方案很简单,只需在末尾添加.ToList()即可。 - dlf

13
你可以尝试这样做。将BeginningEdit处理程序附加到你的DataGrid,并指向以下代码:
    private void Grid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        //// Have to do this in the unusual case where the border of the cell gets selected.
        //// and causes a crash 'EditItem is not allowed'
        e.Cancel = true;
    }

只有在您以某种方式成功地轻敲单元格的边框时,此代码才会触发。该事件的OriginalSource是一个Border,我认为这里可能发生的情况是,预期的TextBox或其他可编辑元素不是事件源,而是这个不可编辑的Border被视为可编辑,这将导致一个异常被埋藏在“EditItem不允许”的异常中。 在它的无效OriginalSource传递之前取消这个RoutedEvent可以停止该错误的发生。


1
这种方法对我来说很有效。我还添加了一点“小技巧”,使它更加精细:MyGrid.BeginningEdit += (sender,args)=> args.Cancel = args.EditingEventArgs.OriginalSource is Border; - Chris C
那是一个很好的额外检查,我当时考虑过,但对于我们的用户界面来说并不是必要的。 - ouflak

8
感谢@nit给我正确的方向。当然,问题出在EF集合的基本类型上。
HashSet 和DataGrid至少需要一个List,改变所有由Entity Framework生成的类会给我带来另一个问题,必须手动进行更改,而且我有很多这样的类。
我的解决方案是创建一个转换器,让它为我完成繁琐的工作:
public class listToObservableCollection : BaseConverter, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        HashSet<Level2> observableList = (HashSet<Level2>)value;
        return new ObservableCollection<Level2>(observableList);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return (HashSet<Level2>)value;
    }
}

public abstract class BaseConverter : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

把它放在我的Datagrid2的绑定上:
<!--part of my window definition--!>
xmlns:l="clr-namespace:Recursos;assembly=Recursos"
...
<!--part of my resources section--!>
<l:listToObservableCollection x:Key="listoToObservable"/>
...
<!--part of my datagrid definition--!>
ItemsSource="{Binding Level2,Converter={StaticResource listoToObservable}}"

现在唯一需要的是如何制作通用转换器,但目前它可以正常工作。


我尝试过了,但是在 XAML 的以下一行中遇到了错误:ItemsSource="{Binding Source={StaticResource ModeloItensViewSource}, Converter={StaticResource collectionConverter}}"。错误提示是“值不能为 null。参数名: Collection”。 - Marlon
@Marlon 抱歉耽搁了,只是度过了几天假期。你是否正确定义了“ModeloItensViewSource”?为什么它是静态资源? - Juan Pablo Gomez
我在互联网上的一些示例中看到过这种做法。但是我已经放弃了这个项目中的实体框架。我遇到了太多的错误和问题,而且对于一个小项目来说工作量太大了。也许以后我会花些时间更好地研究它。但无论如何,谢谢关注。 - Marlon
1
有些事情需要付出努力才能得到回报,EF就是其中之一。也许EF并不是问题所在,可能是设计方面的问题,你是否使用MVVM作为开发模式呢? 我曾经遇到了很多问题,大约持续了2个月,但现在我正在享受EF和MVVM的多样性和强大功能。这种努力是值得的,可以长期受益。如果需要更多帮助,请告诉我。 - Juan Pablo Gomez
好的,谢谢。可能问题更像你所说的,与EF无关,而是与设计有关。但考虑到项目的规模和研究所需的时间,这并不值得。但我肯定会在未来的项目中考虑它。 - Marlon

7

我也通过使用IsReadOnly="True"解决了这个问题。


5
这是我使用的通用转换器。
public class ObservableCollectionConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var observableType= typeof (ObservableCollection<>).MakeGenericType(value.GetType().GetGenericArguments());
        return Activator.CreateInstance(observableType, value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var observableType = typeof(HashSet<>).MakeGenericType(value.GetType().GetGenericArguments());
        return Activator.CreateInstance(observableType, value);
    }
}

2

简单来说,你可以这样写:

DataGrid.BeginningEdit += (s, ss) => ss.Cancel = true;

1

您可以设置IsReadOnly属性。也许不会出现异常......在xaml文件中尝试一下吧。

IsReadOnly="True"

1

鉴于没有人发布过这个问题的答案,原因是您所绑定的集合需要实现 IList(以及可能需要实现ICollection)。

这是由于非泛型(类似于 object)的 DataGrid(以及我相信也包括 CollectionView)的特性造成的。


-3

我将我的数据表格设置为只读模式来解决这个问题

<DataGrid 
                Name="dtgBulkInsert"
                CanUserSortColumns="True" 
                Height="300" Visibility="Collapsed" IsReadOnly="True">

....


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接