1- using System . Collections ;
1+ using System . Collections ;
22using System . Collections . ObjectModel ;
33using System . Collections . Specialized ;
44using System . ComponentModel ;
77
88namespace ModelingEvolution . Observable
99{
10+ /// <summary>
11+ /// A filtered, observable view over an <see cref="IList{T}"/> that implements <see cref="INotifyCollectionChanged"/>.
12+ /// Setting the <see cref="Filter"/> predicate automatically re-evaluates which items are visible.
13+ /// </summary>
14+ /// <typeparam name="T">The type of elements in the collection.</typeparam>
1015 public class ObservableCollectionView < T > :
1116 INotifyCollectionChanged ,
1217 INotifyPropertyChanged ,
@@ -24,6 +29,10 @@ public class ObservableCollectionView<T> :
2429 private readonly ObservableCollection < T > _filtered ;
2530 private Predicate < T > _filter ;
2631
32+ /// <summary>
33+ /// Gets or sets the filter predicate. Setting a new value automatically calls <see cref="Merge"/> to update the view.
34+ /// A <c>null</c> value resets the filter to accept all items.
35+ /// </summary>
2736 public Predicate < T > Filter
2837 {
2938 get => this . _filter ;
@@ -60,8 +69,16 @@ private void Merge()
6069 this . _filtered . RemoveAt ( index ) ;
6170 }
6271
72+ /// <summary>
73+ /// Gets the underlying source collection.
74+ /// </summary>
6375 public IList < T > Source => this . _internal ;
6476
77+ /// <summary>
78+ /// Initializes a new instance of <see cref="ObservableCollectionView{T}"/> with the specified source collection.
79+ /// </summary>
80+ /// <param name="src">The source list to wrap. Must implement <see cref="INotifyCollectionChanged"/>. If <c>null</c>, a new <see cref="ObservableCollection{T}"/> is created.</param>
81+ /// <exception cref="ArgumentException">Thrown when <paramref name="src"/> does not implement <see cref="INotifyCollectionChanged"/>.</exception>
6582 public ObservableCollectionView ( IList < T > src = null )
6683 {
6784 this . _internal = src ?? ( IList < T > ) new ObservableCollection < T > ( ) ;
@@ -119,22 +136,60 @@ private void SourceCollectionChanged(object s, NotifyCollectionChangedEventArgs
119136 }
120137 }
121138
139+ /// <summary>
140+ /// Copies the elements of the collection to an <see cref="Array"/>, starting at a particular index.
141+ /// </summary>
142+ /// <param name="array">The destination array.</param>
143+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying begins.</param>
122144 public void CopyTo ( Array array , int index ) => ( ( ICollection ) this . _filtered ) . CopyTo ( array , index ) ;
123145
146+ /// <summary>
147+ /// Gets a value indicating whether access to the collection is synchronized (thread safe).
148+ /// </summary>
124149 public bool IsSynchronized => ( ( ICollection ) this . _filtered ) . IsSynchronized ;
125150
151+ /// <summary>
152+ /// Gets an object that can be used to synchronize access to the collection.
153+ /// </summary>
126154 public object SyncRoot => ( ( ICollection ) this . _filtered ) . SyncRoot ;
127155
156+ /// <summary>
157+ /// Adds an item to the collection.
158+ /// </summary>
159+ /// <param name="value">The object to add.</param>
160+ /// <returns>The position into which the new element was inserted.</returns>
128161 public int Add ( object value ) => ( ( IList ) this . _filtered ) . Add ( value ) ;
129162
163+ /// <summary>
164+ /// Determines whether the collection contains a specific value.
165+ /// </summary>
166+ /// <param name="value">The object to locate.</param>
167+ /// <returns><c>true</c> if <paramref name="value"/> is found; otherwise, <c>false</c>.</returns>
130168 public bool Contains ( object value ) => ( ( IList ) this . _filtered ) . Contains ( value ) ;
131169
170+ /// <summary>
171+ /// Determines the index of a specific value in the collection.
172+ /// </summary>
173+ /// <param name="value">The object to locate.</param>
174+ /// <returns>The index of <paramref name="value"/> if found; otherwise, -1.</returns>
132175 public int IndexOf ( object value ) => ( ( IList ) this . _filtered ) . IndexOf ( value ) ;
133176
177+ /// <summary>
178+ /// Inserts an item at the specified index.
179+ /// </summary>
180+ /// <param name="index">The zero-based index at which <paramref name="value"/> should be inserted.</param>
181+ /// <param name="value">The object to insert.</param>
134182 public void Insert ( int index , object value ) => ( ( IList ) this . _filtered ) . Insert ( index , value ) ;
135183
184+ /// <summary>
185+ /// Removes the first occurrence of a specific object from the collection.
186+ /// </summary>
187+ /// <param name="value">The object to remove.</param>
136188 public void Remove ( object value ) => ( ( IList ) this . _filtered ) . Remove ( value ) ;
137189
190+ /// <summary>
191+ /// Gets a value indicating whether the collection has a fixed size.
192+ /// </summary>
138193 public bool IsFixedSize => ( ( IList ) this . _filtered ) . IsFixedSize ;
139194
140195 bool IList . IsReadOnly => false ;
@@ -145,55 +200,127 @@ object IList.this[int index]
145200 set => this [ index ] = ( T ) value ;
146201 }
147202
203+ /// <summary>
204+ /// Adds an item to the filtered collection.
205+ /// </summary>
206+ /// <param name="item">The item to add.</param>
148207 public void Add ( T item ) => this . _filtered . Add ( item ) ;
149208
209+ /// <summary>
210+ /// Removes all items from the filtered collection.
211+ /// </summary>
150212 public void Clear ( ) => this . _filtered . Clear ( ) ;
151213
214+ /// <summary>
215+ /// Determines whether the filtered collection contains a specific item.
216+ /// </summary>
217+ /// <param name="item">The item to locate.</param>
218+ /// <returns><c>true</c> if <paramref name="item"/> is found; otherwise, <c>false</c>.</returns>
152219 public bool Contains ( T item ) => this . _filtered . Contains ( item ) ;
153220
221+ /// <summary>
222+ /// Copies the elements of the filtered collection to an array, starting at a particular index.
223+ /// </summary>
224+ /// <param name="array">The destination array.</param>
225+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying begins.</param>
154226 public void CopyTo ( T [ ] array , int index ) => this . _filtered . CopyTo ( array , index ) ;
155227
228+ /// <summary>
229+ /// Returns an enumerator that iterates through the filtered collection.
230+ /// </summary>
231+ /// <returns>An enumerator for the filtered collection.</returns>
156232 public IEnumerator < T > GetEnumerator ( )
157233 {
158234 foreach ( T obj in ( Collection < T > ) this . _filtered )
159235 yield return obj ;
160236 }
161237
238+ /// <summary>
239+ /// Determines the index of a specific item in the filtered collection.
240+ /// </summary>
241+ /// <param name="item">The item to locate.</param>
242+ /// <returns>The index of <paramref name="item"/> if found; otherwise, -1.</returns>
162243 public int IndexOf ( T item ) => this . _filtered . IndexOf ( item ) ;
163244
245+ /// <summary>
246+ /// Inserts an item at the specified index in the filtered collection.
247+ /// </summary>
248+ /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
249+ /// <param name="item">The item to insert.</param>
164250 public void Insert ( int index , T item ) => this . _filtered . Insert ( index , item ) ;
165251
252+ /// <summary>
253+ /// Removes the first occurrence of a specific item from the filtered collection.
254+ /// </summary>
255+ /// <param name="item">The item to remove.</param>
256+ /// <returns><c>true</c> if the item was successfully removed; otherwise, <c>false</c>.</returns>
166257 public bool Remove ( T item ) => this . _filtered . Remove ( item ) ;
167258
259+ /// <summary>
260+ /// Removes the item at the specified index from the filtered collection.
261+ /// </summary>
262+ /// <param name="index">The zero-based index of the item to remove.</param>
168263 public void RemoveAt ( int index ) => this . _filtered . RemoveAt ( index ) ;
169264
265+ /// <summary>
266+ /// Gets the number of elements in the filtered collection.
267+ /// </summary>
170268 public int Count => this . _filtered . Count ;
171269
172270 bool ICollection < T > . IsReadOnly => false ;
173271
272+ /// <summary>
273+ /// Gets or sets the element at the specified index in the filtered collection.
274+ /// </summary>
275+ /// <param name="index">The zero-based index of the element to get or set.</param>
276+ /// <returns>The element at the specified index.</returns>
174277 public T this [ int index ]
175278 {
176279 get => this . _filtered [ index ] ;
177280 set => this . _filtered [ index ] = value ;
178281 }
179282
283+ /// <summary>
284+ /// Occurs when a property value changes.
285+ /// </summary>
180286 public event PropertyChangedEventHandler PropertyChanged ;
181287
288+ /// <summary>
289+ /// Moves the item at the specified old index to the specified new index.
290+ /// </summary>
291+ /// <param name="oldIndex">The zero-based index of the item to move.</param>
292+ /// <param name="newIndex">The zero-based index to move the item to.</param>
182293 public void Move ( int oldIndex , int newIndex ) => this . _filtered . Move ( oldIndex , newIndex ) ;
183294
295+ /// <summary>
296+ /// Occurs when the collection changes.
297+ /// </summary>
184298 public event NotifyCollectionChangedEventHandler CollectionChanged ;
185299
186300 IEnumerator IEnumerable . GetEnumerator ( ) => ( IEnumerator ) this . GetEnumerator ( ) ;
187301
302+ /// <summary>
303+ /// Unsubscribes from the source collection's change notifications.
304+ /// </summary>
188305 public void Dispose ( )
189306 {
190307 if ( ! ( this . _internal is INotifyCollectionChanged collectionChanged ) )
191308 return ;
192309 collectionChanged . CollectionChanged -= new NotifyCollectionChangedEventHandler ( this . SourceCollectionChanged ) ;
193310 }
194311 }
312+
313+ /// <summary>
314+ /// Provides extension methods for collections and string formatting utilities.
315+ /// </summary>
195316 public static class Extensions
196317 {
318+ /// <summary>
319+ /// Adds all elements from <paramref name="other"/> to the end of the list.
320+ /// </summary>
321+ /// <typeparam name="T">The type of elements in the list.</typeparam>
322+ /// <param name="list">The target list to add elements to.</param>
323+ /// <param name="other">The elements to add.</param>
197324 public static void AddRange < T > ( this IList < T > list , IEnumerable < T > other )
198325 {
199326 if ( list is List < T > objList )
@@ -207,6 +334,12 @@ public static void AddRange<T>(this IList<T> list, IEnumerable<T> other)
207334 }
208335 }
209336
337+ /// <summary>
338+ /// Enumerates the list by index, gracefully stopping on index-out-of-range or concurrent modification errors.
339+ /// </summary>
340+ /// <typeparam name="T">The type of elements in the list.</typeparam>
341+ /// <param name="list">The list to enumerate.</param>
342+ /// <returns>An enumerable sequence of elements from the list.</returns>
210343 public static IEnumerable < T > For < T > ( this IReadOnlyList < T > list )
211344 {
212345 for ( int i = 0 ; i < list . Count ; i ++ )
@@ -228,6 +361,13 @@ public static IEnumerable<T> For<T>(this IReadOnlyList<T> list)
228361 }
229362 }
230363
364+ /// <summary>
365+ /// Enumerates the list by index with an optional filter, gracefully stopping on index-out-of-range or concurrent modification errors.
366+ /// </summary>
367+ /// <typeparam name="T">The type of elements in the list.</typeparam>
368+ /// <param name="list">The list to enumerate.</param>
369+ /// <param name="filter">An optional predicate to filter elements. If <c>null</c>, all elements are returned.</param>
370+ /// <returns>An enumerable sequence of elements from the list that match the filter.</returns>
231371 public static IEnumerable < T > For < T > ( this IReadOnlyList < T > list , Predicate < T > ? filter )
232372 {
233373 for ( int i = 0 ; i < list . Count ; i ++ )
@@ -251,26 +391,55 @@ public static IEnumerable<T> For<T>(this IReadOnlyList<T> list, Predicate<T>? fi
251391 }
252392
253393 private static readonly CultureInfo EN_US = new CultureInfo ( "en-US" ) ;
254-
394+
395+ /// <summary>
396+ /// Returns a debug-friendly representation of the string with escaped newlines and tabs, or "null" if the string is <c>null</c>.
397+ /// </summary>
398+ /// <param name="s">The string to format.</param>
399+ /// <returns>A debug-friendly string representation.</returns>
255400 public static string ToDebugString ( this string s )
256401 {
257402 return s == null ? "null" : s . Replace ( "\n " , "\\ n" ) . Replace ( "\t " , "\\ t" ) ;
258403 }
404+
405+ /// <summary>
406+ /// Formats the double value as a JavaScript-compatible string using en-US culture.
407+ /// </summary>
408+ /// <param name="value">The value to format.</param>
409+ /// <returns>The formatted string.</returns>
259410 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
260411 public static string AsJs ( this double value )
261412 {
262413 return $ "{ value . ToString ( EN_US ) } ";
263414 }
415+
416+ /// <summary>
417+ /// Formats the integer value as a CSS pixel string (e.g., "42px").
418+ /// </summary>
419+ /// <param name="value">The value to format.</param>
420+ /// <returns>The formatted pixel string.</returns>
264421 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
265422 public static string AsPx ( this int value )
266423 {
267424 return $ "{ value } px";
268425 }
426+
427+ /// <summary>
428+ /// Formats the double value as a CSS pixel string using en-US culture (e.g., "3.14px").
429+ /// </summary>
430+ /// <param name="value">The value to format.</param>
431+ /// <returns>The formatted pixel string.</returns>
269432 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
270433 public static string AsPx ( this double value )
271434 {
272435 return $ "{ value . ToString ( EN_US ) } px";
273436 }
437+
438+ /// <summary>
439+ /// Formats the nullable double value as a CSS pixel string using en-US culture, defaulting to 0 if <c>null</c>.
440+ /// </summary>
441+ /// <param name="value">The value to format.</param>
442+ /// <returns>The formatted pixel string.</returns>
274443 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
275444 public static string AsPx ( this double ? value )
276445 {
0 commit comments