【问题标题】:DataGrid MultiSelect bound to ObservableCollection unstable when ObservableCollection is modified in the ViewModel在 ViewModel 中修改 ObservableCollection 时绑定到 ObservableCollection 的 DataGrid MultiSelect 不稳定
【发布时间】:2021-08-25 07:47:43
【问题描述】:

我正在构建一个 WPF C# 应用程序,它有多个 DataGrids 绑定到它们各自的包含对象的 ObservableCollections

我将重点关注绑定到Conduits ObservableCollectionDataGrid 以保持简单。

DataGrids 设置为多选 SelectionMode="Extended"DataGrids 中的数据也通过 Canvas 和绘图元素在 2D 视图中表示。

这个想法是用户可以选择 2D 或 DataGrids 中的对象,作为单个项目,或多个项目/行,DataGrid 行或 2D 对象将被突出显示。

这会产生一些不稳定的结果。太多无法列出,所以我将专注于删除项目。当 DataGrid ViewModels 尚未初始化时,我可以毫无问题地删除 2D 中的对象。初始化它们后,在 2D 中删除时出现以下错误。

`System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'`

在 2D 中删除对象如下:

foreach (object _conduit in SelectedConduitList)
    {
        if (_conduit is Conduit conduit)
        {
                Conduits.Remove(conduit);
        }
    }

关联的DataGrid与对象绑定,选中对象如下:

<custom:ConduitDataGrid
    ItemsSource="{Binding Path=NetworkMain.Conduits}" 
    SelectionMode="Extended"
    SelectedItemsList="{Binding NetworkMain.SelectedConduitList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

这里是导管 DataGridsObservableCollection 和所选导管的列表

public ObservableCollection<Conduit> Conduits { get; set; } = new();

private IList _selectedConduitList = new ArrayList();
    public IList SelectedConduitList
    {
        get { return _selectedConduitList; }
        set
        {
            _selectedConduitList = value;
            //changes the IsSelected property of all objects in the ObserbservableCollection to false 
            DeselectAll();
            //changes the IsSelected property of all objects in the ObserbservableCollection to true if the object exists in the SelectedConduitList
            SelectConduits();
            NotifyOfPropertyChange(nameof(SelectedConduitList));
        }
    }

为了让DataGrids 将多个选定的行绑定到SelectedConduitList,使用了自定义datagrid,如下所示:

public class ConduitDataGrid : DataGrid
{
    public ConduitDataGrid()
    {
        this.SelectionChanged += CustomDataGrid_SelectionChanged;
    }

    void CustomDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }

    #region SelectedItemsList

    public IList SelectedItemsList
    {
        get { return (IList)GetValue(SelectedItemsListProperty); }
        set 
        { 
            SetValue(SelectedItemsListProperty, value); 
        
        }
    }

    public static readonly DependencyProperty SelectedItemsListProperty = 
        DependencyProperty.Register(nameof(SelectedItemsList), typeof(IList), typeof(ConduitDataGrid), new PropertyMetadata(null));

    #endregion
}

有人知道为什么我不能在 DataGrid ViewModels 初始化后从我的 2D 布局 ViewModel 中修改(例如删除)SelectedConduitList 中的对象而不抛出错误吗?

【问题讨论】:

    标签: c# wpf data-binding datagrid


    【解决方案1】:

    异常消息意味着您无法在使用 foreach 循环枚举时从 IEnumerable 中删除项目。

    一般的解决方案是将foreach循环替换为for循环并向后遍历集合:

    for (int i = SelectedConduitList.Count - 1; i >= 0; i--)
    {
        Conduit conduit = SelectedConduitList[i] as Conduit;
        if (conduit != null)
            Conduits.Remove(conduit);
    }
    

    更多信息请参考this博文。

    【讨论】:

      【解决方案2】:

      当 DataGrid ViewModel 尚未初始化时,我可以毫无问题地删除 2D 中的对象。初始化它们后,在 2D 中删除时出现以下错误。

      从附加源的集合中删除会导致从选定元素的集合中删除,在该集合上您有一个 foreach 循环。 不允许在运行时更改 foreach 源。
      这是一个常见问题,最简单的解决方案是复制列表,然后循环遍历它。

          foreach (Conduit conduit in SelectedConduitList.OfType<Conduit>().ToList())
          {
              Conduits.Remove(conduit);
          }
      

      【讨论】:

      • 请注意,这种方法会分配一个新的完全不必要的列表。
      • 我同意。但是与包含大量 UI 元素的 GUI 使用的内存相比,一个简单的 List 消耗的内存如此之少,甚至可能无法确定其大小。也就是说,对于这种情况,它是完全不相关的。
      猜你喜欢
      • 2013-08-07
      • 2016-09-13
      • 2017-12-28
      • 2013-09-07
      • 2013-10-14
      • 1970-01-01
      • 2012-12-19
      • 2012-12-16
      相关资源
      最近更新 更多