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

9
我有两个类: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;

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


@Kenneth:你是怎么解决你的问题的? - VoidDweller
将 ObservableViewModelCollection<TViewModel, TModel> 解决方案应用于 https://dev59.com/nHM_5IYBdhLWcg3wt1rD,这个想法怎么样? - Tim Lovell-Smith
2个回答

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语句。
using System.ComponentModel;

1
第一个问题似乎是您正在创建一个新列表并将数据绑定到该列表。当您添加元素时,这些元素将被添加到集合中,但原始的员工列表仍然未被修改。
为了避免这种情况,您应该提供一个自定义的集合类,以便将更改迁移到基础员工列表中,或在数据绑定之前连接适当的事件(以在插入/删除时执行迁移)。
为了避免将可编辑集合绑定到网格时出现其他问题,您应该实现以下数据绑定接口。这些接口的存在允许可视化控件通知基础集合有关操作,例如“插入取消”(当用户中止输入新记录时),并且同样允许信息在相反方向流动(当集合或单个条目更改时更新UI)。
首先,您需要在数据绑定的集合中的每个项目上至少实现IEditableObject、INotifyPropertyChanged和IDataErrorInfo,这在您的情况下将是EmployeeGridViewAdaper类。
此外,您需要使您的集合实现ITypedList和INotifyCollectionChanged。BCL包含了一个BindingList实现,为此提供了一个很好的起点。建议使用它而不是普通的List。

我可以推荐 Windows Forms 2.0 数据绑定,它详尽地涵盖了这个主题。


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