// listen for changes to a property internal void RegisterForChanges(object item, PropertyDescriptor pd, DataBindEngine engine) { // lazy creation of the cache if (_table == null) { _table = new HybridDictionary(); } ValueTableKey key = new ValueTableKey(item, pd); object value = _table[key]; if (value == null) { // new entry needed - add a listener INotifyPropertyChanged inpc = item as INotifyPropertyChanged; if (inpc != null) { PropertyChangedEventManager.AddHandler(inpc, OnPropertyChanged, pd.Name); } else { ValueChangedEventManager.AddHandler(item, OnValueChanged, pd); } } }
// invalidate (remove) a cache entry. Called when the source raises a change event. void InvalidateCache(object item, PropertyDescriptor pd) { // ignore changes to special XLinq PD's - leave our interposed object in the cache if (SystemXmlLinqHelper.IsXLinqCollectionProperty(pd)) { return; } ValueTableKey key = new ValueTableKey(item, pd); _table.Remove(key); }
// return all the properties registered for the given item IEnumerable <PropertyDescriptor> GetPropertiesForItem(object item) { List <PropertyDescriptor> result = new List <PropertyDescriptor>(); foreach (DictionaryEntry de in _table) { ValueTableKey key = (ValueTableKey)de.Key; if (Object.Equals(item, key.Item)) { result.Add(key.PropertyDescriptor); } } return(result); }
// remove stale entries from the table internal bool Purge() { if (_table == null) { return(false); } // first see if there are any stale entries. No sense allocating // storage if there's nothing to do. bool isPurgeNeeded = false; ICollection keys = _table.Keys; foreach (ValueTableKey key in keys) { if (key.IsStale) { isPurgeNeeded = true; break; } } // if the purge is needed, copy the keys and purge the // stale entries. The copy avoids deletion out from under the // key collection. if (isPurgeNeeded) { ValueTableKey[] localKeys = new ValueTableKey[keys.Count]; keys.CopyTo(localKeys, 0); for (int i = localKeys.Length - 1; i >= 0; --i) { if (localKeys[i].IsStale) { _table.Remove(localKeys[i]); } } } return(isPurgeNeeded); // return true if something happened }
public override bool Equals(object o) { if (o == this) { return(true); // this allows deletion of stale keys } ValueTableKey that = o as ValueTableKey; if (that != null) { object item = this.Item; PropertyDescriptor descriptor = this.PropertyDescriptor; if (item == null || descriptor == null) { return(false); // a stale key matches nothing (except itself) } return(this._hashCode == that._hashCode && Object.Equals(item, that.Item) && Object.Equals(descriptor, that.PropertyDescriptor)); } return(false); // this doesn't match a non-ValueTableKey }
// retrieve the value, using the cache if necessary internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) { if (!ShouldCache(item, pd)) { // normal case - just get the value the old-fashioned way return(pd.GetValue(item)); } else { // lazy creation of the cache if (_table == null) { _table = new HybridDictionary(); } // look up the value in the cache bool isXLinqCollectionProperty = SystemXmlLinqHelper.IsXLinqCollectionProperty(pd); ValueTableKey key = new ValueTableKey(item, pd); object value = _table[key]; // if there's no entry, fetch the value and cache it if (value == null) { if (isXLinqCollectionProperty) { // interpose our own value for special XLinq properties value = new XDeferredAxisSource(item, pd); } else { value = pd.GetValue(item); } if (value == null) { value = CachedNull; // distinguish a null value from no entry } _table[key] = value; } // decode null, if necessary if (value == CachedNull) { value = null; } else if (isXLinqCollectionProperty && !indexerIsNext) { // The XLinq properties need special help. When the path // contains "Elements[Foo]", we should return the interposed // XDeferredAxisSource; the path worker will then call the XDAS's // indexer with argument "Foo", and obtain the desired // ObservableCollection. But when the path contains "Elements" // with no indexer, we should return an ObservableCollection // corresponding to the full set of children. // [All this applies to "Descendants" as well.] XDeferredAxisSource xdas = (XDeferredAxisSource)value; value = xdas.FullCollection; } return(value); } }
// retrieve the value, using the cache if necessary internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) { if (!ShouldCache(item, pd)) { // normal case - just get the value the old-fashioned way return(pd.GetValue(item)); } else { // lazy creation of the cache if (_table == null) { _table = new HybridDictionary(); } // look up the value in the cache bool isXLinqCollectionProperty = SystemXmlLinqHelper.IsXLinqCollectionProperty(pd); ValueTableKey key = new ValueTableKey(item, pd); object value = _table[key]; Action FetchAndCacheValue = () => { // if there's no entry, fetch the value and cache it if (value == null) { if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { // intercept GetValue calls for certain ADO properties value = SystemDataHelper.GetValue(item, pd, !FrameworkAppContextSwitches.DoNotUseFollowParentWhenBindingToADODataRelation); } else if (isXLinqCollectionProperty) { // interpose our own value for special XLinq properties value = new XDeferredAxisSource(item, pd); } else { value = pd.GetValue(item); } if (value == null) { value = CachedNull; // distinguish a null value from no entry } // for DataSet properties, store a weak reference to avoid // a memory leak if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { value = new WeakReference(value); } _table[key] = value; } if (SystemDataHelper.IsDataSetCollectionProperty(pd)) { // we stored a weak reference - decode it now WeakReference wr = value as WeakReference; if (wr != null) { value = wr.Target; } } }; FetchAndCacheValue(); if (value == null) { // we can only get here if we cached a weak reference // whose target has since been GC'd. Repeating the call // will refetch the value from the item, and is guaranteed // to set value to non-null. FetchAndCacheValue(); } // decode null, if necessary if (value == CachedNull) { value = null; } else if (isXLinqCollectionProperty && !indexerIsNext) { // The XLinq properties need special help. When the path // contains "Elements[Foo]", we should return the interposed // XDeferredAxisSource; the path worker will then call the XDAS's // indexer with argument "Foo", and obtain the desired // ObservableCollection. But when the path contains "Elements" // with no indexer, we should return an ObservableCollection // corresponding to the full set of children. // [All this applies to "Descendants" as well.] XDeferredAxisSource xdas = (XDeferredAxisSource)value; value = xdas.FullCollection; } return(value); } }
// retrieve the value, using the cache if necessary internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) { if (!ShouldCache(item, pd)) { // normal case - just get the value the old-fashioned way return pd.GetValue(item); } else { // lazy creation of the cache if (_table == null) { _table = new HybridDictionary(); } // look up the value in the cache bool isXLinqCollectionProperty = SystemXmlLinqHelper.IsXLinqCollectionProperty(pd); ValueTableKey key = new ValueTableKey(item, pd); object value = _table[key]; // if there's no entry, fetch the value and cache it if (value == null) { if (isXLinqCollectionProperty) { // interpose our own value for special XLinq properties value = new XDeferredAxisSource(item, pd); } else { value = pd.GetValue(item); } if (value == null) { value = CachedNull; // distinguish a null value from no entry } _table[key] = value; } // decode null, if necessary if (value == CachedNull) { value = null; } else if (isXLinqCollectionProperty && !indexerIsNext) { // The XLinq properties need special help. When the path // contains "Elements[Foo]", we should return the interposed // XDeferredAxisSource; the path worker will then call the XDAS's // indexer with argument "Foo", and obtain the desired // ObservableCollection. But when the path contains "Elements" // with no indexer, we should return an ObservableCollection // corresponding to the full set of children. // [All this applies to "Descendants" as well.] XDeferredAxisSource xdas = (XDeferredAxisSource)value; value = xdas.FullCollection; } return value; } }
// remove stale entries from the table internal bool Purge() { if (_table == null) return false; // first see if there are any stale entries. No sense allocating // storage if there's nothing to do. bool isPurgeNeeded = false; ICollection keys = _table.Keys; foreach (ValueTableKey key in keys) { if (key.IsStale) { isPurgeNeeded = true; break; } } // if the purge is needed, copy the keys and purge the // stale entries. The copy avoids deletion out from under the // key collection. if (isPurgeNeeded) { ValueTableKey[] localKeys = new ValueTableKey[keys.Count]; keys.CopyTo(localKeys, 0); for (int i=localKeys.Length-1; i >= 0; --i) { if (localKeys[i].IsStale) { _table.Remove(localKeys[i]); } } } return isPurgeNeeded; // return true if something happened }
// invalidate (remove) a cache entry. Called when the source raises a change event. void InvalidateCache(object item, PropertyDescriptor pd) { // ignore changes to special XLinq PD's - leave our interposed object in the cache if (SystemXmlLinqHelper.IsXLinqCollectionProperty(pd)) return; ValueTableKey key = new ValueTableKey(item, pd); _table.Remove(key); }