将包装对象的集合与一组未包装的对象同步

时间:2010-03-11 15:35:05

标签: c# collections synchronization

我有两个课程:EmployeeEmployeeGridViewAdapterEmployee由几种复杂类型组成。 EmployeeGridViewAdapter包装一个Employee并将其成员公开为一组扁平的系统类型,以便DataGridView可以处理显示,编辑等。

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

问题是适配器的集合以及员工集合未被同步。因此,我创建运行时的所有员工永远不会被持久化。这是生成EmployeeGridViewAdapter的集合的代码片段:

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

非常简单,但我无法弄清楚如何将更改同步回原始集合。我想已经处理了编辑,因为两个集合都引用了相同的对象,但创建新员工和删除员工的情况并没有发生,所以我无法确定。

2 个答案:

答案 0 :(得分:3)

您还可以考虑使用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 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 System.ComponentModel;

答案 1 :(得分:1)

第一个问题似乎是你正在创建一个新的列表和数据绑定到那个。当您添加元素时,这些元素将添加到集合中,但您的原始员工列表仍未修改。

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

为了避免将可编辑集合绑定到网格的许多其他问题,您应该实现数据绑定接口,如下所述。这些接口的存在允许可视控件向底层集合通知诸如“插入已取消”之类的操作(当用户中止新记录的输入时),并且类似地允许信息以相反的方向流动(在收集或个人时更新UI)条目变更)。

首先,您需要在数据绑定集合中的各个项目上实现至少IEditableObject,INotifyPropertyChanged和IDataErrorInfo,在您的情况下,它们将是EmployeeGridViewAdaper类。

此外,您希望您的集合实现ITypedList和INotifyCollectionChanged。 BCL包含一个BindingList实现,为此提供了一个很好的起点。建议使用此代替普通列表。

我可以推荐Data Binding with Windows Forms 2.0详尽地介绍这个主题。