CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
SortedBindingList.cs
Go to the documentation of this file.
1#if !NETFX_CORE
2//-----------------------------------------------------------------------
3// <copyright file="SortedBindingList.cs" company="Marimer LLC">
4// Copyright (c) Marimer LLC. All rights reserved.
5// Website: https://cslanet.com
6// </copyright>
7// <summary>Provides a sorted view into an existing IList(Of T).</summary>
8//-----------------------------------------------------------------------
9using System;
10using System.ComponentModel;
11using System.Collections.Generic;
12using System.Collections;
13using Csla.Properties;
14
15namespace Csla
16{
17
25 public class SortedBindingList<T> :
26 IList<T>, IBindingList, IEnumerable<T>, ICancelAddNew
27 {
28
29 #region ListItem class
30
31 private class ListItem : IComparable<ListItem>
32 {
33
34 private object _key;
35 private int _baseIndex;
36
37 public object Key
38 {
39 get { return _key; }
40 }
41
42 public int BaseIndex
43 {
44 get { return _baseIndex; }
45 set { _baseIndex = value; }
46 }
47
48 public ListItem(object key, int baseIndex)
49 {
50 _key = key;
51 _baseIndex = baseIndex;
52 }
53
54 public int CompareTo(ListItem other)
55 {
56 object target = other.Key;
57
58 if (Key is IComparable)
59 return ((IComparable)Key).CompareTo(target);
60
61 else
62 {
63 if (Key == null)
64 {
65 if (target == null)
66 return 0;
67 else
68 return 1;
69 }
70 else if (Key.Equals(target))
71 return 0;
72
73 else if (target == null)
74 return 1;
75
76 return Key.ToString().CompareTo(target.ToString());
77 }
78 }
79
80 public override string ToString()
81 {
82 return Key.ToString();
83 }
84
85 }
86
87 #endregion
88
89 #region Sorted enumerator
90
91 private class SortedEnumerator : IEnumerator<T>
92 {
93 private IList<T> _list;
94 private List<ListItem> _sortIndex;
95 private ListSortDirection _sortOrder;
96 private int _index;
97
98 public SortedEnumerator(
99 IList<T> list,
100 List<ListItem> sortIndex,
101 ListSortDirection direction)
102 {
103 _list = list;
104 _sortIndex = sortIndex;
105 _sortOrder = direction;
106 Reset();
107 }
108
109 public T Current
110 {
111 get { return _list[_sortIndex[_index].BaseIndex]; }
112 }
113
114 Object System.Collections.IEnumerator.Current
115 {
116 get { return _list[_sortIndex[_index].BaseIndex]; }
117 }
118
119 public bool MoveNext()
120 {
121 if (_sortOrder == ListSortDirection.Ascending)
122 {
123 if (_index < _sortIndex.Count - 1)
124 {
125 _index++;
126 return true;
127 }
128 else
129 return false;
130 }
131 else
132 {
133 if (_index > 0)
134 {
135 _index--;
136 return true;
137 }
138 else
139 return false;
140 }
141 }
142
143 public void Reset()
144 {
145 if (_sortOrder == ListSortDirection.Ascending)
146 _index = -1;
147 else
148 _index = _sortIndex.Count;
149 }
150
151 #region IDisposable Support
152
153 private bool _disposedValue = false; // To detect redundant calls.
154
155 // IDisposable
156 protected virtual void Dispose(bool disposing)
157 {
158 if (!_disposedValue)
159 {
160 if (disposing)
161 {
162 // free unmanaged resources when explicitly called
163 }
164 // free shared unmanaged resources
165 }
166 _disposedValue = true;
167 }
168
169 // this code added to correctly implement the disposable pattern.
170 public void Dispose()
171 {
172 // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
173 Dispose(true);
174 GC.SuppressFinalize(this);
175 }
176
180 ~SortedEnumerator()
181 {
182 Dispose(false);
183 }
184
185 #endregion
186
187 }
188
189 #endregion
190
191 #region Sort/Unsort
192
193 private void DoSort()
194 {
195 int index = 0;
196 _sortIndex.Clear();
197
198 if (_sortBy == null)
199 {
200 foreach (T obj in _list)
201 {
202 _sortIndex.Add(new ListItem(obj, index));
203 index++;
204 }
205 }
206 else
207 {
208 foreach (T obj in _list)
209 {
210 _sortIndex.Add(new ListItem(_sortBy.GetValue(obj), index));
211 index++;
212 }
213 }
214
215 _sortIndex.Sort();
216 _sorted = true;
217
218 OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0));
219
220 }
221
222 private void UndoSort()
223 {
224 _sortIndex.Clear();
225 _sortBy = null;
226 _sortOrder = ListSortDirection.Ascending;
227 _sorted = false;
228
229 OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, 0));
230
231 }
232
233 #endregion
234
235 #region IEnumerable<T>
236
241 public IEnumerator<T> GetEnumerator()
242 {
243 if (_sorted)
244 return new SortedEnumerator(_list, _sortIndex, _sortOrder);
245 else
246 return _list.GetEnumerator();
247 }
248
249 #endregion
250
251 #region IBindingList, IList<T>
252
258 public void AddIndex(PropertyDescriptor property)
259 {
260 if (_supportsBinding)
261 _bindingList.AddIndex(property);
262 }
263
267 public object AddNew()
268 {
269 object result;
270 if (_supportsBinding)
271 {
272 _initiatedLocally = true;
273 result = _bindingList.AddNew();
274 _initiatedLocally = false;
276 new ListChangedEventArgs(
277 ListChangedType.ItemAdded, _bindingList.Count - 1));
278 }
279 else
280 result = null;
281
282 return result;
283 }
284
288 public bool AllowEdit
289 {
290 get
291 {
292 if (_supportsBinding)
293 return _bindingList.AllowEdit;
294 else
295 return false;
296 }
297 }
298
302 public bool AllowNew
303 {
304 get
305 {
306 if (_supportsBinding)
307 return _bindingList.AllowNew;
308 else
309 return false;
310 }
311 }
312
316 public bool AllowRemove
317 {
318 get
319 {
320 if (_supportsBinding)
321 return _bindingList.AllowRemove;
322 else
323 return false;
324 }
325 }
326
332 public void ApplySort(string propertyName, ListSortDirection direction)
333 {
334 _sortBy = GetPropertyDescriptor(propertyName);
335
336 ApplySort(_sortBy, direction);
337 }
338
344 public void ApplySort(
345 PropertyDescriptor property, ListSortDirection direction)
346 {
347 _sortBy = property;
348 _sortOrder = direction;
349 DoSort();
350 }
351
357 public int Find(string propertyName, object key)
358 {
359 PropertyDescriptor findProperty = GetPropertyDescriptor(propertyName);
360
361 return Find(findProperty, key);
362
363 }
364
365 private static PropertyDescriptor GetPropertyDescriptor(string propertyName)
366 {
367 PropertyDescriptor property = null;
368
369 if (!String.IsNullOrEmpty(propertyName))
370 {
371 Type itemType = typeof(T);
372 foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(itemType))
373 {
374 if (prop.Name == propertyName)
375 {
376 property = prop;
377 break;
378 }
379 }
380
381 // throw exception if propertyDescriptor could not be found
382 if (property == null)
383 throw new ArgumentException(string.Format(Resources.SortedBindingListPropertyNameNotFound, propertyName), propertyName);
384 }
385
386 return property;
387 }
388
395 public int Find(PropertyDescriptor property, object key)
396 {
397 if (_supportsBinding)
398 {
399 var originalIndex = _bindingList.Find(property, key);
400 return originalIndex > -1 ? SortedIndex(originalIndex) : -1;
401 }
402 else
403 return -1;
404 }
405
409 public bool IsSorted
410 {
411 get { return _sorted; }
412 }
413
424 public event ListChangedEventHandler ListChanged;
425
430 protected void OnListChanged(ListChangedEventArgs e)
431 {
432 if (ListChanged != null)
433 ListChanged(this, e);
434 }
435
441 public void RemoveIndex(PropertyDescriptor property)
442 {
443 if (_supportsBinding)
444 _bindingList.RemoveIndex(property);
445 }
446
450 public void RemoveSort()
451 {
452 UndoSort();
453 }
454
458 public ListSortDirection SortDirection
459 {
460 get { return _sortOrder; }
461 }
462
466 public PropertyDescriptor SortProperty
467 {
468 get { return _sortBy; }
469 }
470
476 {
477 get { return true; }
478 }
479
484 {
485 get
486 {
487 if (_supportsBinding)
488 return _bindingList.SupportsSearching;
489 else
490 return false;
491 }
492 }
493
497 public bool SupportsSorting
498 {
499 get { return true; }
500 }
501
507 public void CopyTo(T[] array, int arrayIndex)
508 {
509 int pos = arrayIndex;
510 foreach (T child in this)
511 {
512 array[pos] = child;
513 pos++;
514 }
515 }
516
517 void System.Collections.ICollection.CopyTo(System.Array array, int index)
518 {
519 T[] tmp = new T[array.Length];
520 CopyTo(tmp, index);
521 Array.Copy(tmp, 0, array, index, array.Length);
522 }
523
527 public int Count
528 {
529 get { return _list.Count; }
530 }
531
532 bool System.Collections.ICollection.IsSynchronized
533 {
534 get { return false; }
535 }
536
537 object System.Collections.ICollection.SyncRoot
538 {
539 get { return _list; }
540 }
541
542 IEnumerator System.Collections.IEnumerable.GetEnumerator()
543 {
544 return GetEnumerator();
545 }
546
551 public void Add(T item)
552 {
553 _list.Add(item);
554 }
555
556 int System.Collections.IList.Add(object value)
557 {
558 Add((T)value);
559 return SortedIndex(_list.Count - 1);
560 }
561
565 public void Clear()
566 {
567 _list.Clear();
568 }
569
574 public bool Contains(T item)
575 {
576 return _list.Contains(item);
577 }
578
579 bool System.Collections.IList.Contains(object value)
580 {
581 return Contains((T)value);
582 }
583
588 public int IndexOf(T item)
589 {
590 return SortedIndex(_list.IndexOf(item));
591 }
592
593 int System.Collections.IList.IndexOf(object value)
594 {
595 return IndexOf((T)value);
596 }
597
604 public void Insert(int index, T item)
605 {
606 _list.Insert(index, item);
607 }
608
609 void System.Collections.IList.Insert(int index, object value)
610 {
611 Insert(index, (T)value);
612 }
613
614 bool System.Collections.IList.IsFixedSize
615 {
616 get { return false; }
617 }
618
622 public bool IsReadOnly
623 {
624 get { return _list.IsReadOnly; }
625 }
626
627 object System.Collections.IList.this[int index]
628 {
629 get
630 {
631 return this[index];
632 }
633 set
634 {
635 this[index] = (T)value;
636 }
637 }
638
643 public bool Remove(T item)
644 {
645 return _list.Remove(item);
646 }
647
648 void System.Collections.IList.Remove(object value)
649 {
650 Remove((T)value);
651 }
652
662 public void RemoveAt(int index)
663 {
664 if (_sorted)
665 {
666 _initiatedLocally = true;
667 int baseIndex = OriginalIndex(index);
668
669 // remove the item from the source list
670 _list.RemoveAt(baseIndex);
671
672 _initiatedLocally = false;
673 }
674 else
675 _list.RemoveAt(index);
676 }
677
683 public T this[int index]
684 {
685 get
686 {
687 if (_sorted)
688 return _list[OriginalIndex(index)];
689 else
690 return _list[index];
691 }
692 set
693 {
694 if (_sorted)
695 _list[OriginalIndex(index)] = value;
696 else
697 _list[index] = value;
698 }
699 }
700
701 #endregion
702
703 #region SourceList
704
709 [EditorBrowsable(EditorBrowsableState.Advanced)]
710 public IList<T> SourceList
711 {
712 get
713 {
714 return _list;
715 }
716 }
717
718 #endregion
719
720 private IList<T> _list;
721 private bool _supportsBinding;
722 private IBindingList _bindingList;
723 private bool _sorted;
724 private bool _initiatedLocally;
725 private PropertyDescriptor _sortBy;
726 private ListSortDirection _sortOrder =
727 ListSortDirection.Ascending;
728 private List<ListItem> _sortIndex =
729 new List<ListItem>();
730
731
736 public SortedBindingList(IList<T> list)
737 {
738 _list = list;
739
740 if (_list is IBindingList)
741 {
742 _supportsBinding = true;
743 _bindingList = (IBindingList)_list;
744 _bindingList.ListChanged +=
745 new ListChangedEventHandler(SourceChanged);
746 }
747 }
748
749 private void SourceChanged(
750 object sender, ListChangedEventArgs e)
751 {
752 if (_sorted)
753 {
754 switch (e.ListChangedType)
755 {
756 case ListChangedType.ItemAdded:
757 T newItem = _list[e.NewIndex];
758 if (e.NewIndex == _list.Count - 1)
759 {
760 object newKey;
761 if (_sortBy != null)
762 newKey = _sortBy.GetValue(newItem);
763 else
764 newKey = newItem;
765
766 if (_sortOrder == ListSortDirection.Ascending)
767 _sortIndex.Add(
768 new ListItem(newKey, e.NewIndex));
769 else
770 _sortIndex.Insert(0,
771 new ListItem(newKey, e.NewIndex));
772 if (!_initiatedLocally)
774 new ListChangedEventArgs(
775 ListChangedType.ItemAdded,
776 SortedIndex(e.NewIndex)));
777 }
778 else
779 DoSort();
780 break;
781
782 case ListChangedType.ItemChanged:
783 // an item changed - just relay the event with
784 // a translated index value
786 new ListChangedEventArgs(
787 ListChangedType.ItemChanged, SortedIndex(e.NewIndex), e.PropertyDescriptor));
788 break;
789
790 case ListChangedType.ItemDeleted:
791 var internalIndex = InternalIndex(e.NewIndex);
792 var sortedIndex = internalIndex;
793 if ((_sorted) && (_sortOrder == ListSortDirection.Descending))
794 sortedIndex = _sortIndex.Count - 1 - internalIndex;
795
796 // remove from internal list
797 _sortIndex.RemoveAt(internalIndex);
798
799 // now fix up all index pointers in the sort index
800 foreach (ListItem item in _sortIndex)
801 if (item.BaseIndex > e.NewIndex)
802 item.BaseIndex -= 1;
803
805 new ListChangedEventArgs(
806 ListChangedType.ItemDeleted, sortedIndex, e.PropertyDescriptor));
807 break;
808
809 default:
810 // for anything other than add, delete or change
811 // just re-sort the list
812 if (!_initiatedLocally)
813 DoSort();
814 break;
815 }
816 }
817 else
818 {
819 if (!_initiatedLocally)
820 OnListChanged(e);
821 }
822 }
823
824 private int OriginalIndex(int sortedIndex)
825 {
826 if (sortedIndex == -1) return -1;
827
828 if (_sorted)
829 {
830 if (_sortOrder == ListSortDirection.Ascending)
831 return _sortIndex[sortedIndex].BaseIndex;
832 else
833 return _sortIndex[_sortIndex.Count - 1 - sortedIndex].BaseIndex;
834 }
835 else
836 return sortedIndex;
837 }
838
839 private int SortedIndex(int originalIndex)
840 {
841 if (originalIndex == -1) return -1;
842
843 int result = 0;
844 if (_sorted)
845 {
846 for (int index = 0; index < _sortIndex.Count; index++)
847 {
848 if (_sortIndex[index].BaseIndex == originalIndex)
849 {
850 result = index;
851 break;
852 }
853 }
854 if (_sortOrder == ListSortDirection.Descending)
855 result = _sortIndex.Count - 1 - result;
856 }
857 else
858 result = originalIndex;
859 return result;
860 }
861
862 private int InternalIndex(int originalIndex)
863 {
864 int result = 0;
865 if (_sorted)
866 {
867 for (int index = 0; index < _sortIndex.Count; index++)
868 {
869 if (_sortIndex[index].BaseIndex == originalIndex)
870 {
871 result = index;
872 break;
873 }
874 }
875 }
876 else
877 result = originalIndex;
878 return result;
879 }
880
881
882 #region ICancelAddNew Members
883
884 void ICancelAddNew.CancelNew(int itemIndex)
885 {
886 if (itemIndex <= -1) return;
887
888 ICancelAddNew can = _list as ICancelAddNew;
889 if (can != null)
890 can.CancelNew(OriginalIndex(itemIndex));
891 else
892 _list.RemoveAt(OriginalIndex(itemIndex));
893 }
894
895 void ICancelAddNew.EndNew(int itemIndex)
896 {
897 ICancelAddNew can = _list as ICancelAddNew;
898 if (can != null)
899 can.EndNew(OriginalIndex(itemIndex));
900 }
901
902 #endregion
903
904 #region ToArray
905
909 public T[] ToArray()
910 {
911 List<T> result = new List<T>();
912 foreach (T item in this)
913 result.Add(item);
914 return result.ToArray();
915 }
916
917 #endregion
918
919 }
920}
921#endif
A strongly-typed resource class, for looking up localized strings, etc.
static string SortedBindingListPropertyNameNotFound
Looks up a localized string similar to PropertyName '{0}' not found in list.
Provides a sorted view into an existing IList(Of T).
object AddNew()
Implemented by IList source object.
int Find(PropertyDescriptor property, object key)
Implemented by IList source object.
void RemoveAt(int index)
Removes the child object at the specified index in the list, resorting the display as needed.
void ApplySort(string propertyName, ListSortDirection direction)
Applies a sort to the view.
void RemoveSort()
Removes any sort currently applied to the view.
bool AllowEdit
Implemented by IList source object.
int Find(string propertyName, object key)
Finds an item in the view
void Insert(int index, T item)
Implemented by IList source object.
bool IsReadOnly
Implemented by IList source object.
bool IsSorted
Gets a value indicating whether the view is currently sorted.
IEnumerator< T > GetEnumerator()
Returns an enumerator for the list, honoring any sort that is active at the time.
ListChangedEventHandler ListChanged
Raised to indicate that the list's data has changed.
SortedBindingList(IList< T > list)
Creates a new view based on the provided IList object.
PropertyDescriptor SortProperty
Returns the PropertyDescriptor of the current sort.
void AddIndex(PropertyDescriptor property)
Implemented by IList source object.
void RemoveIndex(PropertyDescriptor property)
Implemented by IList source object.
ListSortDirection SortDirection
Returns the direction of the current sort.
bool SupportsChangeNotification
Returns true since this object does raise the ListChanged event.
bool SupportsSearching
Implemented by IList source object.
bool AllowNew
Implemented by IList source object.
T[] ToArray()
Get an array containing all items in the list.
bool Remove(T item)
Implemented by IList source object.
IList< T > SourceList
Gets the source list over which this SortedBindingList is a view.
int Count
Implemented by IList source object.
void Clear()
Implemented by IList source object.
bool AllowRemove
Implemented by IList source object.
void OnListChanged(ListChangedEventArgs e)
Raises the ListChanged event.
void Add(T item)
Implemented by IList source object.
bool SupportsSorting
Returns true.
bool Contains(T item)
Implemented by IList source object.
int IndexOf(T item)
Implemented by IList source object.
void ApplySort(PropertyDescriptor property, ListSortDirection direction)
Applies a sort to the view.
void CopyTo(T[] array, int arrayIndex)
Implemented by IList source object.