代码之家  ›  专栏  ›  技术社区  ›  Kenneth Cochran

将包装对象集合与未包装对象集合同步

  •  9
  • Kenneth Cochran  · 技术社区  · 15 年前

    我有两个班: Employee EmployeeGridViewAdapter . 员工 由多个复杂类型组成。 员工网格视图适配器 包装单 员工 并将其成员公开为扁平的系统类型集,以便DataGridView可以处理显示、编辑等。

    我正在使用VS的内置支持将POCO转换为数据源,然后将其附加到 BindingSource 对象。当我连接 DataGridView 绑定源 它创建了预期的列,并且在运行时我可以执行预期的CRUD操作。到目前为止一切都很好。

    问题是适配器集合和员工集合没有同步。所以我创建运行时的所有员工都不会被持久化。下面是生成 员工网格视图适配器 的:

            var employeeCollection = new List<EmployeeGridViewAdapter>();
            foreach (var employee in this.employees)
            {
                employeeCollection.Add(new EmployeeGridViewAdapter(employee));
            }
            this.view.Employees = employeeCollection;
    

    非常直接,但我不知道如何将更改同步回原始集合。我想编辑已经被处理了,因为两个集合引用了相同的对象,但是创建新员工和删除员工并没有发生,所以我不能确定。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Morten Mertner    15 年前

    第一个问题似乎是您正在创建一个新的列表,并将数据绑定到该列表。添加元素时,这些元素将添加到集合中,但原始员工列表保持不变。

    为了避免这种情况,您应该提供一个自定义集合类,将更改迁移回底层员工列表,或者在数据绑定到它之前连接适当的事件(在插入/删除时执行迁移)。

    为了避免将可编辑集合绑定到网格中的其他许多问题,您应该实现数据绑定接口,如下所述。这些接口的存在允许可视化控件通知基础集合有关“insert cancelled”(当用户中止新记录的条目时)等操作,并同样允许信息反向流动(当集合或单个条目更改时更新UI)。

    首先,您将希望至少对数据绑定集合中的各个项实现IEditableObject、InotifyPropertiesChanged和IDataErrorInfo,在您的情况下,这些项将是EmployeeGridViewADaper类。

    此外,您还希望集合实现ITypedList和InotifyCollectionChanged。BCL包含一个bindingList实现,它提供了一个很好的起点。建议使用这个而不是简单的列表。

    我可以推荐 Data Binding with Windows Forms 2.0 关于这个主题的详尽报道。

        2
  •  3
  •   VoidDweller    13 年前

    你也可以考虑使用 System.Collections.ObjectModel.ObservableCollection 然后接线 CollectionChanged 事件。看起来像这样。

            ObservableCollection<EmployeeAdapter> observableEmployees = 
                        new ObservableCollection<EmployeeAdapter>();
    
            foreach (Employee emp in employees)
            {
                observableEmployees.Add(new EmployeeAdapter(emp));
            }
    
            observableEmployees.CollectionChanged += 
                (object sender, NotifyCollectionChangedEventArgs e) =>
                {
                    ObservableCollection<EmployeeAdapter> views = 
                            sender as ObservableCollection<EmployeeAdapter>;
                    if (views == null)
                        return;
                    switch (e.Action)
                    {
                         case NotifyCollectionChangedAction.Add:
                            foreach (EmployeeAdapter view in e.NewItems)
                            {
                                if (!employees.Contains(view.Employee))
                                    employees.Add(view.Employee);
                            }
                            break;
                         case NotifyCollectionChangedAction.Remove:
                            foreach (EmployeeAdapter view in e.OldItems)
                            {
                                if (employees.Contains(view.Employee))
                                    employees.Remove(view.Employee);
                            }
                            break;
                        default:
                            break;
                    }
                };
    

    代码采用以下using语句。

    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    

    如果你需要 IList 您也可以使用的接口 System.ComponentModel.BindingList 把它连接起来 ListChanged 事件。看起来像这样。

    BindingList<EmployeeAdapter> empViews = new BindingList<EmployeeAdapter>();
    
    foreach (Employee emp in employees)
    {
        empViews.Add(new EmployeeAdapter(emp));
    }
    
    empViews.ListChanged +=
            (object sender, ListChangedEventArgs e) =>
                {
                    BindingList<EmployeeAdapter> employeeAdapters = 
                            sender as BindingList<EmployeeAdapter>;
                    if (employeeAdapters == null)
                        return;
    
                    switch (e.ListChangedType)
                    {
                        case ListChangedType.ItemAdded:
                            EmployeeAdapter added = employeeAdapters[e.NewIndex];
                            if (!employees.Contains(added.Employee))
                                employees.Add(added.Employee);
                            break;
                        case ListChangedType.ItemDeleted:
                            EmployeeAdapter deleted = employeeAdapters[e.OldIndex];
                            if (employees.Contains(deleted.Employee))
                                employees.Remove(deleted.Employee);
                            break;
                        default:
                            break;
                    }
                };
    

    代码采用以下using语句。

    using System.ComponentModel;