/// <summary> /// Initializes a new instance of the <see cref="T:UsedProperty"/> class. /// </summary> internal UsedPropertyChain(INotifyPropertyChanged parent, string name, UsedSubproperty child, TraversalOptions traversalOptions) : base(name, child, traversalOptions) { if (parent == null) { throw new ArgumentNullException("target"); } this.Parent = parent; this.TopLevelParent = this; }
/// <summary> /// Initializes a new instance of the <see cref="T:UsePropertyBase"/> class. /// </summary> internal UsedPropertyBase(string name, UsedSubproperty child, TraversalOptions traversalOptions) { if (name == null) { throw new ArgumentNullException("name"); } this.Child = child; this.Name = name; this.tracedItems = new List <INotifyPropertyChanged>(); this.TraversalOptions = traversalOptions; this.cyclicAccessGuard = new CyclicAccessGuard <AccessTrace>(1, cyclicAccessRecord); }
private static UsedSubproperty GetContinuationChainLink( MemberExpression memberExpression, ChildInformation child, TraversalOptions options) { Func <INotifyPropertyChanged> parentRetriever = CompileObjectFactoryExpressionINPC(memberExpression.Expression); if (parentRetriever == null) { //This happens when parentMember refers to a non-observable collection return(null); } var usedSub = new UsedSubproperty(memberExpression.Member.Name, parentRetriever, child.Property, options); usedSub.TargetRetriever = CompileMemberExpression(memberExpression); return(usedSub); }
private static void GetUsedProperties(Set <UsedPropertyChain> container, MemberExpression memberExpression, ChildInformation child, TraversalOptions options) { if (memberExpression == null) { return; } MemberTypes memberType = memberExpression.Member.MemberType; if (memberType != MemberTypes.Property) { if (memberType == MemberTypes.Field) { if (options.TrackFields) { var constant = memberExpression.Expression as ConstantExpression; if (constant != null) { HandleFieldConstant(container, memberExpression, child, options); return; } if (IsFieldChain(memberExpression) && child.IsTrackable) { //Occurs with a.b.c.A kind of expression, where //the field chain is the leading part of the //expression and the constant can be extracted from //it. The constant is taken from evaluation of a.b.c HandleFieldConstant(container, memberExpression, child, options); return; } else { //Dead end. It happens, when a field occurs //after a non-constant element, e.g. after //a property reference as in 'A.B.field.C' //'A.B' is not ConstantExpression and therefore, //even with TrackFields on, the field cannot be //tracked any further. However, the preceding //part of the expression can and should be //tracked. //Break the chain (without continuation) GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options); return; } } else { //Break the chain (without continuation) GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options); return; } } throw new InvalidOperationException("Unhandled case. Revise the code"); } var parentMember = memberExpression.Expression as MemberExpression; if (parentMember != null) { UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options); var usedChild = new ChildInformation(usedSubProperty); //Continue the chain GetUsedProperties(container, parentMember, usedChild, options); } else { if (memberExpression.Expression is ParameterExpression) { //Presumably, we are within a nested lambda parameter block. //Skip, just for now. If there's a nested lambda like "c=>c.property" it //is pretty complicated to know when to update. //TODO: return; } var constant = memberExpression.Expression as ConstantExpression; if (constant != null) { var notifiable = constant.Value as INotifyPropertyChanged; if (notifiable != null) { string propertyName = memberExpression.Member.Name; var chain = new UsedPropertyChain(notifiable, propertyName, child.Property, options); chain.TargetRetriever = CompileMemberExpression(memberExpression); container.Add(chain); } else { //Even now add a chain to the container. Fields are not traced, but their //properties can be. This applies only to cases where a the left-hand part //of an expression is a field or a field/constant chain. HandleFieldConstant(container, memberExpression, child, options); } return; } else { UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options); var usedChild = new ChildInformation(usedSubProperty); //This should break the notification chain or continue, if it's marker method. GetUsedProperties(container, memberExpression.Expression, usedChild, options); } } }
/// <summary> /// Initializes a new instance of the <see cref="T:UsedSubproperty"/> class. /// </summary> internal UsedSubproperty(string name, Func <INotifyPropertyChanged> parentRetriever, UsedSubproperty child, TraversalOptions traversalOptions) : base(name, child, traversalOptions) { if (parentRetriever == null) { throw new ArgumentNullException("parentRetriever"); } this.parentRetriever = parentRetriever; }
/// <summary> /// Initializes a new instance of the <see cref="T:ChildInformation"/> class. /// </summary> public ChildInformation(MemberTypes childMemberType) { this.MemberType = childMemberType; this.Property = null; }
/// <summary> /// Initializes a new instance of the <see cref="T:ChildInformation"/> class. /// </summary> public ChildInformation(UsedSubproperty subProperty) { this.Property = subProperty; this.MemberType = subProperty == null ? MemberTypeUnknown : MemberTypes.Property; }