【问题标题】:BindingList projection wrapperBindingList 投影包装器
【发布时间】:2010-03-03 08:59:58
【问题描述】:

有没有一种简单的方法来创建BindingList 包装器(带投影),它会随着原始列表的更新而更新?

例如,假设我有一个可变的数字列表,我想在 ComboBox 中将它们表示为十六进制字符串。使用这个包装器我可以做这样的事情:

BindingList<int> numbers = data.GetNumbers();
comboBox.DataSource = Project(numbers, i => string.Format("{0:x}", i));

我可以将列表包装成一个新的BindingList,处理所有源事件,更新列表并再次触发这些事件,但我觉得已经有更简单的方法了。

【问题讨论】:

    标签: c# .net binding projection bindinglist


    【解决方案1】:

    我刚刚偶然发现了这个问题,我意识到我可能会发布我最终得到的代码。

    因为我想要一个快速的解决方案,所以我做了一个穷人的实现。它用作现有源列表的包装器,但它会创建完整的项目列表并根据需要对其进行更新。起初我希望我可以在访问项目时即时进行投影,但这需要从头开始实现整个IBindingList 接口。

    作用:对源列表的任何更新也将更新目标列表,因此绑定控件将被正确更新。

    它不做什么:当目标列表改变时它不会更新源列表。那将需要一个倒置投影功能,而我无论如何都不需要该功能。因此,必须始终在源列表中添加、更改或删除项目。

    使用示例如下。假设我们有一个数字列表,但我们想在数据网格中显示它们的平方值:

    // simple list of numbers
    List<int> numbers = new List<int>(new[] { 1, 2, 3, 4, 5 });
    
    // wrap it in a binding list
    BindingList<int> sourceList = new BindingList<int>(numbers);
    
    // project each item to a squared item
    BindingList<int> squaredList = new ProjectedBindingList<int, int>
        (sourceList, i => i*i);
    
    // whenever the source list is changed, target list will change
    sourceList.Add(6);
    Debug.Assert(squaredList[5] == 36);
    

    这里是源代码:

    public class ProjectedBindingList<Tsrc, Tdest> 
        : BindingList<Tdest>
    {
        private readonly BindingList<Tsrc> _src;
        private readonly Func<Tsrc, Tdest> _projection;
    
        public ProjectedBindingList(
            BindingList<Tsrc> source, 
            Func<Tsrc, Tdest> projection)
        {
            _projection = projection;
            _src = source;
            RecreateList();
            _src.ListChanged += new ListChangedEventHandler(_src_ListChanged);
        }
    
        private void RecreateList()
        {
            RaiseListChangedEvents = false;
            Clear();
    
            foreach (Tsrc item in _src)
                this.Add(_projection(item));
    
            RaiseListChangedEvents = true;
        }
    
        void _src_ListChanged(object sender, ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemAdded:
                    this.InsertItem(e.NewIndex, Proj(e.NewIndex));
                    break;
    
                case ListChangedType.ItemChanged:
                    this.Items[e.NewIndex] = Proj(e.NewIndex);
                    break;
    
                case ListChangedType.ItemDeleted:
                    this.RemoveAt(e.NewIndex);
                    break;
    
                case ListChangedType.ItemMoved:
                    Tdest movedItem = this[e.OldIndex];
                    this.RemoveAt(e.OldIndex);
                    this.InsertItem(e.NewIndex, movedItem);
                    break;
    
                case ListChangedType.Reset:
                    // regenerate list
                    RecreateList();
                    OnListChanged(e);
                    break;
    
                default:
                    OnListChanged(e);
                    break;
            }
        }
    
        Tdest Proj(int index)
        {
            return _projection(_src[index]);
        }
    }
    

    我希望有人会觉得这很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-15
      • 2020-05-29
      • 1970-01-01
      • 2018-08-06
      • 1970-01-01
      • 1970-01-01
      • 2011-12-16
      • 2021-08-29
      相关资源
      最近更新 更多