【问题标题】:Synchronizing a collection of wrapped objects with a collection of unwrapped objects将已包装对象的集合与未包装对象的集合同步
【发布时间】:2010-03-11 15:35:04
【问题描述】:

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

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

问题是适配器的集合和员工的集合没有同步。因此,我创建运行时的所有员工都不会被持久化。这是生成 EmployeeGridViewAdapter 集合的代码的 sn-p:

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

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

【问题讨论】:

标签: c# collections synchronization


【解决方案1】:

您也可以考虑使用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;

【讨论】:

    【解决方案2】:

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

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

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

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

    此外,您还希望您的集合实现 ITypedList 和 INotifyCollectionChanged。 BCL 包含一个 BindingList 实现,它为此提供了一个很好的起点。推荐使用这个而不是普通的列表。

    我可以推荐Data Binding with Windows Forms 2.0 来详细了解这个主题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-23
      • 1970-01-01
      • 1970-01-01
      • 2014-03-20
      • 1970-01-01
      • 1970-01-01
      • 2023-02-13
      相关资源
      最近更新 更多