简体   繁体   中英

SortableBindingList, Index was out of range Error, how to make it Thread-Safe?

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace MyApplication
{
    public class SortableBindingList<T> : BindingList<T>
    {
        private static object syncLock = new object();
        private readonly Dictionary<Type, PropertyComparer<T>> comparers;
        private bool isSorted;
        private ListSortDirection listSortDirection;
        private PropertyDescriptor propertyDescriptor;

        private System.ComponentModel.ISynchronizeInvoke _SyncObject;
        private System.Action<System.ComponentModel.ListChangedEventArgs> _FireEventAction;

        public SortableBindingList()
            : base(new List<T>())
        {
            this.comparers = new Dictionary<Type, PropertyComparer<T>>();
        }

        public SortableBindingList(IEnumerable<T> enumeration, System.ComponentModel.ISynchronizeInvoke syncObject)
            : base(new List<T>(enumeration))
        {
            _SyncObject = syncObject;
            _FireEventAction = FireEvent;

            this.comparers = new Dictionary<Type, PropertyComparer<T>>();
        }

        protected override bool SupportsSortingCore
        {
            get { return true; }
        }

        protected override bool IsSortedCore
        {
            get { return this.isSorted; }
        }

        protected override PropertyDescriptor SortPropertyCore
        {
            get { return this.propertyDescriptor; }
        }

        protected override ListSortDirection SortDirectionCore
        {
            get { return this.listSortDirection; }
        }

        protected override bool SupportsSearchingCore
        {
            get { return true; }
        }

        protected override void InsertItem(int index, T item)
        {
            lock (syncLock)
            {
                base.InsertItem(index, item);
            }
        }

        protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
        {
            List<T> itemsList = (List<T>)this.Items;

            Type propertyType = property.PropertyType;
            PropertyComparer<T> comparer;
            if (!this.comparers.TryGetValue(propertyType, out comparer))
            {
                comparer = new PropertyComparer<T>(property, direction);
                this.comparers.Add(propertyType, comparer);
            }

            comparer.SetPropertyAndDirection(property, direction);
            itemsList.Sort(comparer);

            this.propertyDescriptor = property;
            this.listSortDirection = direction;
            this.isSorted = true;

            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override void RemoveSortCore()
        {
            this.isSorted = false;
            this.propertyDescriptor = base.SortPropertyCore;
            this.listSortDirection = base.SortDirectionCore;

            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override int FindCore(PropertyDescriptor property, object key)
        {
            int count = this.Count;
            for (int i = 0; i < count; ++i)
            {
                T element = this[i];
                if (property.GetValue(element).Equals(key))
                {
                    return i;
                }
            }

            return -1;
        }

        protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args)
        {
            lock (syncLock)
            {
                if (_SyncObject == null)
                {
                    FireEvent(args);
                }
                else
                {
                    _SyncObject.Invoke(_FireEventAction, new object[] { args });
                }
            }
        }

        private void FireEvent(System.ComponentModel.ListChangedEventArgs args)
        {
            base.OnListChanged(args);
        }
    }
}

I am getting following error:

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index

  1. The SortableBindingList is bound to DataGridView, Virtual mode
  2. Multiple threads fires event which adds data to SortableBindingList

private void ProxyScraper_OnProxyFound(Proxy Proxy, int Count)
{       
    ProxyProcessedCount.Text = Count.ToString();
    _ProxyList.Add(Proxy);
}

I have tried to lock the SortableBindingList but still getting error, searched a lot but unable to find a solution.

Ultimately I suspect it is a false hope to make a truly thread-safe binding list, as there will be cases where multiple operations are performed - regardless of whether that is a "check the Count then iterate rows to Count-1", or "enumerate with foreach " - and there is no easy way to lock over the duration of those, since the calling code is outside of your control.

Even for a half-working version, you'd need to add your syncLock code to every access, via overriding all the available methods - however, I can't see a virtual method for the get on this[index] , which might render that futile - it is only synchronized if all callers agree to use the lock.

Ultimately, I suspect that trying to use threading with tight UI coupling is fairly doomed. IMO, you might have more success separating the two things, and having the UI worry about handling an event and calling .Invoke(...) to update the binding on the UI thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM