Wrap IList<T> and use it for effective binding in Silverlight and WPF

Print Content | More

If you use some ORM tool like NHibernate and your business entities or DTOs have to support collections of other elements, you are forced to use the interface notation IList<T> or ICollection<T> to hold a reference to the real collection implementation (cause usually those ORM tools tend to inject their own implementation of the collection classes).

Such collections aren’t very well suited to be used in a binding cause they usually doesn’t support any mechanism to inform the binding object that the collection has been changed or altered, so the change is not propagated to the user interface.

You have two way to solve the problem:

1- Convert the collection to a well known ObservableCollection<T>, but in this way you’ll loose the flexibility of having an ORM for your entity cause you’re modifying its normal way to handle object instances.

2- Wrap the ‘ISomething<T>’ collection with another class that exposes all you need to be effective as a binding source for Silverlight or WPF.

I prefer to follow the second approach, cause it eliminates a lot of conversions between objects and preserves objects integrity. Here is a simple implementation of a wrapping class for a generic IList<T> collection:

   1: [Serializable]
   2: [CollectionDataContract]
   3: public class NotifyCollectionWrapper<T> : IList<T>, IList, INotifyCollectionChanged
   4: {
   5:     public NotifyCollectionWrapper()
   6:     {
   7:     }
   8:  
   9:     public NotifyCollectionWrapper(IList<T> innerlist)
  10:     {
  11:         Reset(innerlist);
  12:     }
  13:  
  14:     public NotifyCollectionWrapper<T> Reset(IList<T> innerlist)
  15:     {
  16:         _InnerList = innerlist;
  17:         return this;
  18:     }
  19:  
  20:     private IList<T> _InnerList;
  21:  
  22:     #region INotifyCollectionChanged Members
  23:  
  24:     public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
  25:  
  26:     /// <summary>
  27:     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has been
  28:     /// added to the end of the collection.
  29:     /// </summary>
  30:     /// <param name="item">Item added to the collection.</param>
  31:     protected void OnItemAdded(T item)
  32:     {
  33:         if (this.CollectionChanged != null)
  34:         {
  35:             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
  36:                                                         NotifyCollectionChangedAction.Add, item, this.Count - 1));
  37:         }
  38:     }
  39:  
  40:     /// <summary>
  41:     /// Fires the <see cref="CollectionChanged"/> event to indicate the collection
  42:     /// has been reset.  This is used when the collection has been cleared or
  43:     /// entirely replaced.
  44:     /// </summary>
  45:     protected void OnCollectionReset()
  46:     {
  47:         if (this.CollectionChanged != null)
  48:         {
  49:             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
  50:                                                         NotifyCollectionChangedAction.Reset));
  51:         }
  52:     }
  53:  
  54:     /// <summary>
  55:     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
  56:     /// been inserted into the collection at the specified index.
  57:     /// </summary>
  58:     /// <param name="index">Index the item has been inserted at.</param>
  59:     /// <param name="item">Item inserted into the collection.</param>
  60:     protected void OnItemInserted(int index, T item)
  61:     {
  62:         if (this.CollectionChanged != null)
  63:         {
  64:             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
  65:                                                         NotifyCollectionChangedAction.Add, item, index));
  66:         }
  67:     }
  68:  
  69:     /// <summary>
  70:     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
  71:     /// been replaced into the collection at the specified index.
  72:     /// </summary>
  73:     /// <param name="index">Index the item has been inserted at.</param>
  74:     /// <param name="item">Item inserted into the collection.</param>
  75:     protected void OnItemReplaced(T newItem, T oldItem, int index)
  76:     {
  77:         if (this.CollectionChanged != null)
  78:         {
  79:             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
  80:                                                         NotifyCollectionChangedAction.Replace, newItem, oldItem, index));
  81:         }
  82:     }
  83:  
  84:     /// <summary>
  85:     /// Fires the <see cref="CollectionChanged"/> event to indicate an item has
  86:     /// been removed from the collection at the specified index.
  87:     /// </summary>
  88:     /// <param name="item">Item removed from the collection.</param>
  89:     /// <param name="index">Index the item has been removed from.</param>
  90:     protected void OnItemRemoved(T item, int index)
  91:     {
  92:         if (this.CollectionChanged != null)
  93:         {
  94:             this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(
  95:                                                         NotifyCollectionChangedAction.Remove, item, index));
  96:         }
  97:     }
  98:  
  99:     #endregion
 100:  
 101:     #region IList<T> Members
 102:  
 103:     public int IndexOf(T item)
 104:     {
 105:         return _InnerList.IndexOf(item);
 106:     }
 107:  
 108:     public void Insert(int index, T item)
 109:     {
 110:         _InnerList.Insert(index, item);
 111:         OnItemInserted(index, item);
 112:     }
 113:  
 114:     public T this[int index]
 115:     {
 116:         get
 117:         {
 118:             return _InnerList[index];
 119:         }
 120:  
 121:         set
 122:         {
 123:             T old = _InnerList[index];
 124:             _InnerList[index] = value;
 125:             OnItemReplaced(value, old, index);
 126:         }
 127:     }
 128:  
 129:     public void RemoveAt(int index)
 130:     {
 131:         T item = _InnerList[index];
 132:         _InnerList.RemoveAt(index);
 133:         OnItemRemoved(item, index);
 134:     }
 135:  
 136:     #endregion
 137:  
 138:     #region ICollection<T> Members
 139:  
 140:     public void Add(T item)
 141:     {
 142:         _InnerList.Add(item);
 143:         OnItemAdded(item);
 144:     }
 145:  
 146:     public bool Contains(T item)
 147:     {
 148:         return _InnerList.Contains(item);
 149:     }
 150:  
 151:     public void CopyTo(T[] array, int arrayIndex)
 152:     {
 153:         _InnerList.CopyTo(array, arrayIndex);
 154:     }
 155:  
 156:     public bool Remove(T item)
 157:     {
 158:         int index = _InnerList.IndexOf(item);
 159:         bool result = _InnerList.Remove(item);
 160:         OnItemRemoved(item, index);
 161:         return result;
 162:     }
 163:  
 164:     public void Clear()
 165:     {
 166:         _InnerList.Clear();
 167:         OnCollectionReset();
 168:     }
 169:  
 170:     public int Count
 171:     {
 172:         get
 173:         {
 174:             if (_InnerList != null)
 175:                 return _InnerList.Count;
 176:             return 0;
 177:         }
 178:     }
 179:  
 180:     public bool IsReadOnly
 181:     {
 182:         get { return _InnerList.IsReadOnly; }
 183:     }
 184:  
 185:     #endregion
 186:  
 187:     #region IEnumerable<T> Members
 188:  
 189:     IEnumerator<T> IEnumerable<T>.GetEnumerator()
 190:     {
 191:         if (_InnerList != null)
 192:             return _InnerList.GetEnumerator();
 193:         return null;
 194:     }
 195:  
 196:     #endregion
 197:  
 198:     #region IEnumerable Members
 199:  
 200:     IEnumerator IEnumerable.GetEnumerator()
 201:     {
 202:         return GetEnumerator();
 203:     }
 204:  
 205:     protected IEnumerator GetEnumerator()
 206:     {
 207:         if (_InnerList != null)
 208:             return _InnerList.GetEnumerator();
 209:         return null;
 210:     }
 211:  
 212:     #endregion
 213:  
 214:     #region IList Members
 215:  
 216:     public int Add(object value)
 217:     {
 218:         _InnerList.Add((T)value);
 219:         OnItemAdded((T)value);
 220:         return -1;
 221:     }
 222:  
 223:     public bool Contains(object value)
 224:     {
 225:         return _InnerList.Contains((T)value);
 226:     }
 227:  
 228:     public int IndexOf(object value)
 229:     {
 230:         return _InnerList.IndexOf((T)value);
 231:     }
 232:  
 233:     public void Insert(int index, object value)
 234:     {
 235:         _InnerList.Insert(index, (T)value);
 236:         OnItemInserted(index, (T)value);
 237:     }
 238:  
 239:     public bool IsFixedSize
 240:     {
 241:         get { return false; }
 242:     }
 243:  
 244:     public void Remove(object value)
 245:     {
 246:         int index = _InnerList.IndexOf((T)value);
 247:         bool result = _InnerList.Remove((T)value);
 248:         OnItemRemoved((T)value, index);
 249:     }
 250:  
 251:     object IList.this[int index]
 252:     {
 253:         get
 254:         {
 255:             return _InnerList[index];
 256:         }
 257:         set
 258:         {
 259:             T old = _InnerList[index];
 260:             _InnerList[index] = (T)value;
 261:             OnItemReplaced((T)value, old, index);
 262:         }
 263:     }
 264:  
 265:     #endregion
 266:  
 267:     #region ICollection Members
 268:  
 269:     public void CopyTo(Array array, int index)
 270:     {
 271:         throw new NotImplementedException();
 272:     }
 273:  
 274:     public bool IsSynchronized
 275:     {
 276:         get { return false; }
 277:     }
 278:  
 279:     public object SyncRoot
 280:     {
 281:         get { return this; }
 282:     }
 283:  
 284:     #endregion
 285: }

You can Wrap an IList<T> just creating a new instance of the collection and passing in the reference as a constructor parameter or you can use the Reset() method which will reinitialize the innerList variable to the new collection to be wrapped; in this way you can unleash the full power of binding to a collection that comes directly from an object loaded with an ORM.



Binding, Collections, Nhibernate, Silverlight, Wpf, Ilist, Collection, Nhibernat

3 comments

Related Post

  1. #1 da Guardian - Saturday March 2010 alle 01:15

    CollectionChanged is anv event exposed by the class that others can subscribe to; look at line 24, you'll find the definition: public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; this event must be defined in this way cause it's part of the INotifyCollectionChanged interface.

  2. #2 da Schwartzenberg - Saturday March 2010 alle 01:15

    you check if (this.CollectionChanged != null) But it is null. It isnt implemented in the class ... where should it be implemented and could you give an example? Thanks

  3. #3 da WPF Databinding with NHibernate Collections | //refactor this - Saturday March 2010 alle 01:15

    [...] their own custom collection type by wrapping the IList and implementing INotifyCollectionChanged (see this post At Guardian’s Home).  I quickly decided that the first method was not for me as I really don’t care to spend [...]

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)