2008-10-30 15 views
11

में मैं अपने Windows फार्म में एक BindingList<T> कि "IComparable<Contact>" संपर्क-वस्तुओं की एक सूची में शामिल है का उपयोग कर रहा हूँ। अब मैं उपयोगकर्ता को ग्रिड में प्रदर्शित किसी भी कॉलम से सॉर्ट करने में सक्षम होना चाहता हूं।डेटाग्रिड व्यू सॉर्ट और उदा। BindingList <T> नेट

एक तरह से MSDN ऑनलाइन पर वर्णित से पता चलता है जो कि कैसे जो छँटाई की अनुमति देता है BindingList<T> के आधार पर एक कस्टम संकलन लागू करने के लिए नहीं है। लेकिन क्या कस्टम कोड का उपयोग करके अंतर्निहित संग्रह को सॉर्ट करने के लिए डेटाग्रिड व्यू (या यहां तक ​​कि बाईंडिंगसोर्स पर भी अच्छा) में सॉर्ट-इवेंट या कुछ ऐसा नहीं किया जा सकता है?

मुझे एमएसडीएन द्वारा वर्णित तरीके से वास्तव में पसंद नहीं है। दूसरी तरफ मैं संग्रह में LINQ क्वेरी को आसानी से लागू कर सकता हूं।

उत्तर

18

मैं higly अपनी सादगी और सुंदरता के लिए Matthias' solution सराहना करते हैं।

हालांकि, यह कम डेटा वॉल्यूम के लिए उत्कृष्ट परिणाम देता है, जबकि बड़े डेटा वॉल्यूम्स के साथ काम करते समय प्रदर्शन प्रतिबिंब के कारण इतना अच्छा नहीं होता है।

मैं सरल डेटा वस्तुओं का संग्रह के साथ एक परीक्षण भाग गया, 100000 तत्वों की गिनती। एक पूर्णांक प्रकार की संपत्ति द्वारा छंटनी लगभग 1 मिनट ले ली। कार्यान्वयन मैं आगे विस्तार करने जा रहा हूं इसे ~ 200ms में बदल दिया।

मूल विचार दृढ़ता से टाइप किया तुलना लाभ के लिए, ApplySortCore विधि सामान्य रखते हुए है। निम्नलिखित एक विशिष्ट comparer के लिए एक कॉल के साथ सामान्य तुलना प्रतिनिधि, एक व्युत्पन्न वर्ग में लागू की जगह:

न्यू SortableBindingList में < टी >:

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; 
} 

:

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

ApplySortCore में परिवर्तन

: अब, व्युत्पन्न वर्ग में एक-एक sortable संपत्ति के लिए comparers को लागू करना 10

इस संस्करण को थोड़ा और कोड चाहिए, लेकिन यदि प्रदर्शन एक मुद्दा है, तो मुझे लगता है कि यह प्रयास के लायक है।

+1

के लिए SupportsSortingCore, IsSortedCord, SortDirectionCore, SortPropertyCore और RemoveSortCore को ओवरराइड करना चाहिए, कुछ पिछले डेवलपर ने इसे विन फॉर्म प्रोजेक्ट में उसी तरह कार्यान्वित किया है, जिस पर मैं काम कर रहा हूं, और मैं समझना चाहता हूं कि क्यों। अब मुझे पता है; यह बेहतर प्रदर्शन है। यह एक अविश्वसनीय रूप से सहायक पोस्ट था, क्योंकि यह न केवल आपको समाधान को कार्यान्वित करने का तरीका दिखाता है, बल्कि यह बताता है कि इस समाधान में कौन से परिदृश्य बेहतर हैं। +1 – Jim

+1

सुझाव: उपरोक्त कोड संशोधित करने वाली फ़ाइल में एक लिंक जोड़ें। –

+1

मैं सूची में उलटा होने पर दो बार सॉर्ट करने की बजाए एक छोटा बदलाव सुझाता हूं, तुलनात्मक रूप से तुलना करें 'तुलना तुलनाकर्ता = GetComparer (prop); अगर (दिशा == ListSortDirection.Descending) {var original = comparr; तुलनाकर्ता = (एक्स, वाई) => मूल (वाई, एक्स); } itemsList.Sort (तुलनात्मक); ' –

0

कस्टम ऑब्जेक्ट्स के लिए नहीं। .NET 2.0 में, मुझे बाइंडिंगलिस्ट का उपयोग करके सॉर्टिंग पर रोल करना पड़ा। .NET 3.5 में कुछ नया हो सकता है लेकिन मैंने अभी तक इसमें नहीं देखा है। अब जब LINQ और सॉर्टिंग विकल्प हैं जो आते हैं तो यह अब लागू करना आसान हो सकता है।

+0

आपके उत्तर के लिए धन्यवाद। छंटाई LINQ के साथ बहुत आसान हो गया है, लेकिन मैं जब उपयोगकर्ता सूची ... –

24

मैं googled और अपने खुद के कुछ और समय पर करने की कोशिश की ...

वहाँ .NET में कोई अंतर्निहित तरीका अब तक है। आपको BindingList<T> पर आधारित कस्टम क्लास को कार्यान्वित करना होगा। Custom Data Binding, Part 2 (MSDN) में एक तरीका वर्णित है। मैं अंततः ApplySortCore -method का कार्यान्वयन प्रदान करने के लिए एक अलग कार्यान्वयन का उत्पादन करता हूं जो परियोजना-निर्भर नहीं है।

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 लागू करता द्वारा सॉर्ट कर सकते हैं।

+0

1+ धन्यवाद यह वास्तव में –

+7

+1 में सहायक है सॉर्ट करने के लिए कामना की गति प्रदान करने के कोई रास्ता नहीं मिल गया है। क्यों एमएस सिर्फ इस दर्जन पहली जगह में वर्ग पुस्तकालय में कोड की लाइनों को लागू नहीं किया? –

+0

सॉर्ट करने योग्य बाइंडिंगलिस्ट के लिए सूची के सॉर्ट() व्यवहार को अनुकरण करने के तरीके के बारे में जानकारी के लिए यह पोस्ट भी देखें।

4

यहां एक विकल्प है जो बहुत साफ है और मेरे मामले में ठीक काम करता है। मेरे पास पहले से ही सूची तुलना (तुलना) के साथ उपयोग के लिए स्थापित विशिष्ट तुलनात्मक कार्य थे, इसलिए मैंने इसे अन्य स्टैक ओवरफ्लो उदाहरणों के हिस्सों से अनुकूलित किया।

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; 
    } 
    } 
} 
} 
3

यहाँ कुछ नए गुर उपयोग कर एक नया implmentation है।

IList<T> के अंतर्निहित प्रकार void Sort(Comparison<T>) को लागू करना चाहिए या आप एक प्रतिनिधि में पारित करना होगा आप के लिए तरह समारोह कॉल करने के लिए।

(IList<T> एक void Sort(Comparison<T>) समारोह नहीं है) स्थिर निर्माता वर्ग प्रकार T के माध्यम से जाना होगा सभी सार्वजनिक instanced गुण है कि 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; 
     } 
    } 
} 
+0

इसे सही उत्तर के रूप में चिह्नित किया जाना चाहिए। –

6

मुझे लगता है कि ये सभी उत्तर लिखे गए समय में अच्छे थे। शायद वे अभी भी हैं। मैं कुछ इसी तरह की तलाश में और वैकल्पिक समाधान कन्वर्ट करने के लिए किसी भी सूची या संग्रह BindingList<T> sortable लिए किया गया था।

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

यह समाधान एक विस्तार विधि Entity Framework पुस्तकालय में उपलब्ध का उपयोग करता है:

यहाँ महत्वपूर्ण स्निपेट (पूर्ण नमूना के लिए लिंक नीचे साझा किया जाता है) है। तो कृपया आगे बढ़ने से पहले निम्नलिखित पर विचार करें:

  1. यदि आप इकाई फ्रेमवर्क का उपयोग नहीं करना चाहते हैं, तो यह ठीक है, यह समाधान इसका उपयोग नहीं कर रहा है। हम सिर्फ एक विस्तार विधि का उपयोग कर रहे हैं जो उन्होंने विकसित किया है। EntityFramework.dll का आकार 5 एमबी है। यदि पेटबाइट्स के युग में आपके लिए यह बहुत बड़ा है, तो उपर्युक्त लिंक से विधि और इसकी निर्भरताओं को निकालने के लिए स्वतंत्र महसूस करें।
  2. आप उपयोग कर रहे हैं (या उपयोग करना चाहते हैं) इकाई की रूपरेखा (> = v6.0), तो आपको चिंता की बात नहीं है। बस इकाई फ्रेमवर्क Nuget पैकेज स्थापित करें और आगे बढ़ें।

मैंने LINQPad कोड नमूना here अपलोड किया है।

  1. नमूना डाउनलोड करें, LINQPad का उपयोग करके इसे खोलें और F4 दबाएं।
  2. आप लाल रंग में EntityFramework.dll देखना चाहिए। इस location से डीएल डाउनलोड करें। ब्राउज़ करें और संदर्भ जोड़ें।
  3. ठीक क्लिक करें। एफ 5 मारा।

जैसा कि आप देख सकते हैं, आप डेटाग्रिड व्यू नियंत्रण पर अपने कॉलम हेडर पर क्लिक करके विभिन्न डेटा प्रकारों के सभी चार कॉलमों को सॉर्ट कर सकते हैं।

जो लोग LINQPad अभी भी क्वेरी डाउनलोड करने और नोटपैड के साथ इसे खोलने, पूर्ण नमूना देखने के लिए कर सकते हैं नहीं है,।

+0

नतीजा एक क्रमबद्ध बाध्यकारी सूची नहीं है, इसलिए यदि मुझे यह सही ढंग से मिल गया तो यह सुंदर समाधान केवल तभी काम करता है जब आपकी ऑब्जेक्ट ('व्यक्ति') सरल हो और इसकी सभी गुण तालिका में दिखने जा रहे हैं। – gneri

+0

आपको ग्रिड में सभी गुणों को प्रदर्शित करने की आवश्यकता नहीं है। किसी कॉलम हेडर पर क्लिक करने का प्रयास करें, यह उस कॉलम पर सॉर्ट करेगा, इसलिए यह वास्तव में एक क्रमबद्ध बाइंडिंग सूची है। और आप सही हैं, यह समाधान केवल साधारण वस्तुओं के लिए है क्योंकि यह डिफ़ॉल्ट तुलनाकर्ताओं का उपयोग करता है, हालांकि, हम आमतौर पर केवल आदिम प्रकारों को प्रदर्शित और क्रमबद्ध करते हैं, है ना? –