/// <summary> /// Defines a new RootStateEventListener with the given state being considered /// the "root" state, relative to which all properties will be resolved and which /// will be listened for. /// </summary> /// <param name="root">The root state. All events and properties will be relative /// to the root state.</param> /// <exception cref="ArgumentNullException">The passed state was null.</exception> public RootStateEventListener(ChangeableState root) { this.root = root; if (this.root == null) { throw new ArgumentNullException(nameof(root)); } this.tree = new PropertyNameTree(); this.root.PropertyChanged += OnRootPropertyChanged; }
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); } } }
public SubstateListener(ChangeableState state, PropertyChangeEventHandler handler) { State = state; Handler = handler; }