سؤال

أنا أستخدم أ BindingList<T> في نماذج Windows الخاصة بي التي تحتوي على قائمة "IComparable<Contact>"كائنات الاتصال.الآن أود أن يتمكن المستخدم من الفرز حسب أي عمود معروض في الشبكة.

هناك طريقة موصوفة على MSDN عبر الإنترنت توضح كيفية تنفيذ مجموعة مخصصة بناءً على BindingList<T> مما يسمح بالفرز.ولكن أليس هناك حدث فرز أو شيء يمكن اكتشافه في DataGridView (أو حتى أجمل، في BindingSource) لفرز المجموعة الأساسية باستخدام تعليمات برمجية مخصصة؟

لا أحب حقًا الطريقة التي وصفتها MSDN.وبالطريقة الأخرى يمكنني بسهولة تطبيق استعلام LINQ على المجموعة.

هل كانت مفيدة؟

المحلول

وأنا أقدر هيغلي ماتياس "حل للحصول على بساطته والجمال.

ولكن، في حين أن هذا يعطي نتائج ممتازة لأحجام البيانات منخفضة، وعند التعامل مع كميات كبيرة من البيانات أداء ليست جيدة جدا، وذلك بسبب انعكاس.

وجريت اختبار مع مجموعة من الكائنات بيانات بسيطة، عد 100000 العناصر. الترتيب بواسطة خاصية نوع عدد صحيح استغرق حوالي 1 دقيقة. تنفيذ انا ذاهب الى مزيد من التفاصيل تغيير هذا إلى ~ 200MS.

والفكرة الأساسية هي أن تستفيد مقارنة كتبته بقوة، مع الحفاظ على طريقة ApplySortCore عام. وفيما يلي محل مندوب مقارنة مع عام مكالمة مع المقارن محددة، تنفذ في فئة مشتقة:

والجديد في SortableBindingList :

protected abstract Comparison<T> GetComparer(PropertyDescriptor prop);

وApplySortCore يتغير إلى:

protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
    List<T> itemsList = (List<T>)this.Items;
    if (prop.PropertyType.GetInterface("IComparable") != null)
    {
        Comparison<T> comparer = GetComparer(prop);
        itemsList.Sort(comparer);
        if (direction == ListSortDirection.Descending)
        {
            itemsList.Reverse();
        }
    }

    isSortedValue = true;
    sortPropertyValue = prop;
    sortDirectionValue = direction;
}

والآن، في فئة مشتقة واحدة لديك لتنفيذ comparers عن كل الممتلكات للفرز:

class MyBindingList:SortableBindingList<DataObject>
{
        protected override Comparison<DataObject> GetComparer(PropertyDescriptor prop)
        {
            Comparison<DataObject> comparer;
            switch (prop.Name)
            {
                case "MyIntProperty":
                    comparer = new Comparison<DataObject>(delegate(DataObject x, DataObject y)
                        {
                            if (x != null)
                                if (y != null)
                                    return (x.MyIntProperty.CompareTo(y.MyIntProperty));
                                else
                                    return 1;
                            else if (y != null)
                                return -1;
                            else
                                return 0;
                        });
                    break;

                    // Implement comparers for other sortable properties here.
            }
            return comparer;
        }
    }
}

وهذا البديل يتطلب أكثر قليلا كود ولكن، إذا كان الأداء هو المشكلة، وأعتقد أنه قيمته هذا الجهد.

نصائح أخرى

وأنا googled وحاول على بلدي مزيدا من الوقت ...

وليس هناك طريقة مضمنة في. NET حتى الآن. لديك لتنفيذ فئة مخصصة بناء على BindingList<T>. يتم وصف طريقة واحدة في بيانات مخصصة ملزم، الجزء 2 (MSDN) . I تنتج أخيرا تنفيذ مختلف من ApplySortCore-طريقة لتوفير التنفيذ الذي لا تعتمد على المشروع.

protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
    List<T> itemsList = (List<T>)this.Items;
    if(property.PropertyType.GetInterface("IComparable") != null)
    {
        itemsList.Sort(new Comparison<T>(delegate(T x, T y)
        {
            // Compare x to y if x is not null. If x is, but y isn't, we compare y
            // to x and reverse the result. If both are null, they're equal.
            if(property.GetValue(x) != null)
                return ((IComparable)property.GetValue(x)).CompareTo(property.GetValue(y)) * (direction == ListSortDirection.Descending ? -1 : 1);
            else if(property.GetValue(y) != null)
                return ((IComparable)property.GetValue(y)).CompareTo(property.GetValue(x)) * (direction == ListSortDirection.Descending ? 1 : -1);
            else
                return 0;
        }));
    }

    isSorted = true;
    sortProperty = property;
    sortDirection = direction;
}

وباستخدام هذه واحدة، يمكنك فرز من قبل أي عضو أن تنفذ IComparable.

أفهم أن كل هذه الإجابات كانت جيدة في وقت كتابتها.ربما لا يزالون كذلك.كنت أبحث عن شيء مماثل ووجدت حلاً بديلاً للتحويل أي قائمة أو مجموعة للفرز BindingList<T>.

إليك المقتطف المهم (تتم مشاركة رابط العينة الكاملة أدناه):

void Main()
{
    DataGridView dgv = new DataGridView();
    dgv.DataSource = new ObservableCollection<Person>(Person.GetAll()).ToBindingList();
}    

يستخدم هذا الحل طريقة التمديد المتاحة في إطار كيان مكتبة.لذا يرجى مراعاة ما يلي قبل المضي قدمًا:

  1. إذا كنت لا ترغب في استخدام Entity Framework، فلا بأس، فهذا الحل لا يستخدمه أيضًا.نحن نستخدم فقط طريقة التمديد التي طوروها.حجم EntityFramework.dll هو 5 ميغابايت.إذا كان الأمر كبيرًا جدًا بالنسبة لك في عصر البيتابايت، فلا تتردد في استخراج الطريقة وتوابعها من الرابط أعلاه.
  2. إذا كنت تستخدم (أو ترغب في استخدام) Entity Framework (>=v6.0)، فلا داعي للقلق.فقط قم بتثبيت إطار كيان حزمة Nuget وانطلق.

لقد قمت بتحميل لينكباد عينة التعليمات البرمجية هنا.

  1. قم بتنزيل العينة، وافتحها باستخدام LINQPad واضغط على F4.
  2. يجب أن تشاهد EntityFramework.dll باللون الأحمر.قم بتنزيل ملف dll من هذا موقع.تصفح وأضف المرجع.
  3. انقر فوق موافق.ضرب F5.

كما ترون، يمكنك الفرز على جميع الأعمدة الأربعة لأنواع البيانات المختلفة عن طريق النقر فوق رؤوس الأعمدة الخاصة بها في عنصر التحكم DataGridView.

أولئك الذين ليس لديهم LINQPad، لا يزال بإمكانهم تنزيل الاستعلام وفتحه باستخدام المفكرة، لرؤية العينة الكاملة.

وهنا هو بديل نظيف جدا ويعمل على ما يرام في حالتي. كان لي بالفعل وظائف مقارنة محددة إعدادها للاستخدام مع List.Sort (مقارنة) لذلك أنا مجرد تكييف هذا من أجزاء من الأمثلة ستاكوفيرفلوو أخرى.

class SortableBindingList<T> : BindingList<T>
{
 public SortableBindingList(IList<T> list) : base(list) { }

 public void Sort() { sort(null, null); }
 public void Sort(IComparer<T> p_Comparer) { sort(p_Comparer, null); }
 public void Sort(Comparison<T> p_Comparison) { sort(null, p_Comparison); }

 private void sort(IComparer<T> p_Comparer, Comparison<T> p_Comparison)
 {
  if(typeof(T).GetInterface(typeof(IComparable).Name) != null)
  {
   bool originalValue = this.RaiseListChangedEvents;
   this.RaiseListChangedEvents = false;
   try
   {
    List<T> items = (List<T>)this.Items;
    if(p_Comparison != null) items.Sort(p_Comparison);
    else items.Sort(p_Comparer);
   }
   finally
   {
    this.RaiseListChangedEvents = originalValue;
   }
  }
 }
}

وهنا هو implmentation جديد باستخدام بعض الحيل الجديدة.

والنوع الأساسي من IList<T> يجب تنفيذ void Sort(Comparison<T>) أو يجب أن يمر في مندوب لاستدعاء الدالة نوع بالنسبة لك. (IList<T> لايوجد وظيفة void Sort(Comparison<T>))

وخلال المنشئ ثابت الطبقة سوف تذهب من خلال نوع من T العثور على جميع خصائص وضح العامة التي تطبق ICompareable أو ICompareable<T> وتخزين المندوبين أنه يخلق لاستخدامها لاحقا. ويتم ذلك في منشئ ثابتة لأننا بحاجة فقط للقيام بذلك مرة واحدة لكل نوع من T وDictionary<TKey,TValue> هو موضوع آمنة على يقرأ.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ExampleCode
{
    public class SortableBindingList<T> : BindingList<T>
    {
        private static readonly Dictionary<string, Comparison<T>> PropertyLookup;
        private readonly Action<IList<T>, Comparison<T>> _sortDelegate;

        private bool _isSorted;
        private ListSortDirection _sortDirection;
        private PropertyDescriptor _sortProperty;

        //A Dictionary<TKey, TValue> is thread safe on reads so we only need to make the dictionary once per type.
        static SortableBindingList()
        {
            PropertyLookup = new Dictionary<string, Comparison<T>>();
            foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                Type propertyType = property.PropertyType;
                bool usingNonGenericInterface = false;

                //First check to see if it implments the generic interface.
                Type compareableInterface = propertyType.GetInterfaces()
                    .FirstOrDefault(a => a.Name == "IComparable`1" &&
                                         a.GenericTypeArguments[0] == propertyType);

                //If we did not find a generic interface then use the non-generic interface.
                if (compareableInterface == null)
                {
                    compareableInterface = propertyType.GetInterface("IComparable");
                    usingNonGenericInterface = true;
                }

                if (compareableInterface != null)
                {
                    ParameterExpression x = Expression.Parameter(typeof(T), "x");
                    ParameterExpression y = Expression.Parameter(typeof(T), "y");

                    MemberExpression xProp = Expression.Property(x, property.Name);
                    Expression yProp = Expression.Property(y, property.Name);

                    MethodInfo compareToMethodInfo = compareableInterface.GetMethod("CompareTo");

                    //If we are not using the generic version of the interface we need to 
                    // cast to object or we will fail when using structs.
                    if (usingNonGenericInterface)
                    {
                        yProp = Expression.TypeAs(yProp, typeof(object));
                    }

                    MethodCallExpression call = Expression.Call(xProp, compareToMethodInfo, yProp);

                    Expression<Comparison<T>> lambada = Expression.Lambda<Comparison<T>>(call, x, y);
                    PropertyLookup.Add(property.Name, lambada.Compile());
                }
            }
        }

        public SortableBindingList() : base(new List<T>())
        {
            _sortDelegate = (list, comparison) => ((List<T>)list).Sort(comparison);
        }

        public SortableBindingList(IList<T> list) : base(list)
        {
            MethodInfo sortMethod = list.GetType().GetMethod("Sort", new[] {typeof(Comparison<T>)});
            if (sortMethod == null || sortMethod.ReturnType != typeof(void))
            {
                throw new ArgumentException(
                    "The passed in IList<T> must support a \"void Sort(Comparision<T>)\" call or you must provide one using the other constructor.",
                    "list");
            }

            _sortDelegate = CreateSortDelegate(list, sortMethod);
        }

        public SortableBindingList(IList<T> list, Action<IList<T>, Comparison<T>> sortDelegate)
            : base(list)
        {
            _sortDelegate = sortDelegate;
        }

        protected override bool IsSortedCore
        {
            get { return _isSorted; }
        }

        protected override ListSortDirection SortDirectionCore
        {
            get { return _sortDirection; }
        }

        protected override PropertyDescriptor SortPropertyCore
        {
            get { return _sortProperty; }
        }

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

        private static Action<IList<T>, Comparison<T>> CreateSortDelegate(IList<T> list, MethodInfo sortMethod)
        {
            ParameterExpression sourceList = Expression.Parameter(typeof(IList<T>));
            ParameterExpression comparer = Expression.Parameter(typeof(Comparison<T>));
            UnaryExpression castList = Expression.TypeAs(sourceList, list.GetType());
            MethodCallExpression call = Expression.Call(castList, sortMethod, comparer);
            Expression<Action<IList<T>, Comparison<T>>> lambada =
                Expression.Lambda<Action<IList<T>, Comparison<T>>>(call,
                    sourceList, comparer);
            Action<IList<T>, Comparison<T>> sortDelegate = lambada.Compile();
            return sortDelegate;
        }

        protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
        {
            Comparison<T> comparison;

            if (PropertyLookup.TryGetValue(property.Name, out comparison))
            {
                if (direction == ListSortDirection.Descending)
                {
                    _sortDelegate(Items, (x, y) => comparison(y, x));
                }
                else
                {
                    _sortDelegate(Items, comparison);
                }

                _isSorted = true;
                _sortProperty = property;
                _sortDirection = direction;

                OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, property));
            }
        }

        protected override void RemoveSortCore()
        {
            _isSorted = false;
        }
    }
}

وليس للكائنات مخصصة. في صافي 2.0، وكان لي للفة لي على فرز باستخدام BindingList. قد يكون هناك شيء جديد في صافي 3.5 ولكن أنا لم ينظر إلى ذلك حتى الآن. الآن أن هناك LINQ وفرز الخيارات التي تأتي مع إذا كان هذا الآن قد يكون أسهل في التنفيذ.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top