/// <summary> /// Returns the value unchanged, but subscribes to its events if it is itself /// a ChangeableState. Designed to be used with read-only properties with /// pre-initialized values. /// </summary> /// <typeparam name="T">The type of the value.</typeparam> /// <param name="property">The name of the property the value will be assigned to.</param> /// <param name="value">The value to subscribe to, if it is a ChangeableState.</param> /// <returns></returns> protected T Subscribe <T>(string property, T value) { if (value is ChangeableState newSubstate) { PropertyChangeEventHandler newHandler = (object sender, PropertyChangeEventArgs e) => { this.OnPropertyChanged(property + "." + e.PropertyName, e.NewValue); }; _substates[property] = new SubstateListener(newSubstate, newHandler); newSubstate.PropertyChanged += newHandler; } return(value); }
private void OnPropertyChanged(string propertyName, object newValue) { bool lockTaken = false; bool iAmEntrant = false; try { Monitor.Enter(_stateLock, ref lockTaken); if (reentrant) { return; // prevent re-entry from substate update => prevents infinite loops } reentrant = iAmEntrant = true; bool shouldUpdateSubstate = newValue is ChangeableState && ShouldUpdatePropertyAsSubstate(propertyName); if (_substates.ContainsKey(propertyName)) // discard old substate regardless { SubstateListener substate = _substates[propertyName]; substate.State.PropertyChanged -= substate.Handler; _substates.Remove(propertyName); } if (_pausedStateDirtySet != null) // paused? { _pausedStateDirtySet[propertyName] = newValue; return; } if (shouldUpdateSubstate) { ChangeableState newSubstate = (ChangeableState)newValue; PropertyChangeEventHandler newHandler = (object sender, PropertyChangeEventArgs e) => { this.OnPropertyChanged(propertyName + "." + e.PropertyName, e.NewValue); }; _substates[propertyName] = new SubstateListener(newSubstate, newHandler); newSubstate.PropertyChanged += newHandler; } PropertyChanged?.Invoke(this, new PropertyChangeEventArgs(propertyName, newValue)); } finally { reentrant &= !iAmEntrant; if (lockTaken) { Monitor.Exit(_stateLock); } } }