< Summary

Information
Class: MyNet.Observable.Behaviors.PropertyChangedForwardingBehavior
Assembly: MyNet.Observable
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Behaviors/PropertyChangedForwardingBehavior.cs
Tag: 323_28699572109
Line coverage
85%
Covered lines: 42
Uncovered lines: 7
Coverable lines: 49
Total lines: 144
Line coverage: 85.7%
Branch coverage
60%
Covered branches: 23
Total branches: 38
Branch coverage: 60.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
OnPropertyChanged(...)66.66%6683.33%
Attach(...)60%101090.9%
RaiseInitialSnapshot(...)66.66%6680%
OnSourcePropertyChanged(...)50%10866.66%
RelayPropertyChanged(...)66.66%6680%
DisposeManagedResources()50%22100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Behaviors/PropertyChangedForwardingBehavior.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="PropertyChangedForwardingBehavior.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.ComponentModel;
 9using System.Threading;
 10
 11namespace MyNet.Observable.Behaviors;
 12
 13/// <summary>
 14/// Relays PropertyChanged notifications from a wrapper object (a property of the owner)
 15/// to the owner. The behavior watches a named property on the owner; when that
 16/// property's value implements <see cref="INotifyPropertyChanged"/>, the behavior
 17/// subscribes to its PropertyChanged and forwards notifications to the owner by
 18/// calling <see cref="ObservableObject.NotifyPropertyChanged(string, object?, object?)"/> with either
 19/// the child property name or a qualified name (<c>Wrapper.Property</c>) depending
 20/// on configuration.
 21/// </summary>
 22public sealed class PropertyChangedForwardingBehavior : SuspendableBehavior<ObservableObject>, IPropertyChangedBehavior
 23{
 24    private readonly string _propertyName;
 25    private readonly bool _concatenatePropertyName;
 26    private readonly bool _raiseInitialSnapshot;
 4227    private readonly Lock _gate = new();
 28    private INotifyPropertyChanged? _source;
 29
 30    /// <summary>
 31    /// Initializes a new instance of the <see cref="PropertyChangedForwardingBehavior"/> class.
 32    /// </summary>
 33    /// <param name="owner">Owner observable object.</param>
 34    /// <param name="propertyName">The owner property that contains the wrapper source.</param>
 35    /// <param name="concatenatePropertyName">True to emit notifications like Wrapper.Name; false to emit only Name.</pa
 36    /// <param name="raiseInitialSnapshot">When <c>true</c>, relays a snapshot of the source's public properties after e
 37    public PropertyChangedForwardingBehavior(
 38        ObservableObject owner,
 39        string propertyName,
 40        bool concatenatePropertyName = true,
 41        bool raiseInitialSnapshot = true)
 4242        : base(owner)
 43    {
 4244        _propertyName = propertyName;
 4245        _concatenatePropertyName = concatenatePropertyName;
 4246        _raiseInitialSnapshot = raiseInitialSnapshot;
 47
 4248        Attach(ObservableObjectPropertyAccess.GetPropertyValue(Owner, _propertyName) as INotifyPropertyChanged);
 4249    }
 50
 51    /// <inheritdoc />
 52    public void OnPropertyChanged(PropertyMutationContext context)
 53    {
 6054        if (IsDisposed || IsSuspended)
 055            return;
 56
 6057        if (!string.Equals(context.PropertyName, _propertyName, StringComparison.Ordinal))
 5158            return;
 59
 960        Attach(context.NewValue as INotifyPropertyChanged);
 961    }
 62
 63    /// <summary>
 64    /// Attaches to the specified source object by subscribing to its PropertyChanged event.
 65    /// </summary>
 66    /// <param name="value">The source object to attach to.</param>
 67    private void Attach(INotifyPropertyChanged? value)
 5168    {
 69        INotifyPropertyChanged? attached;
 70
 71        lock (_gate)
 72        {
 5173            if (ReferenceEquals(_source, value))
 074                return;
 75
 5176            _source?.PropertyChanged -= OnSourcePropertyChanged;
 77
 5178            _source = value;
 79
 5180            attached = _source;
 81
 5182            attached?.PropertyChanged += OnSourcePropertyChanged;
 5183        }
 84
 5185        if (attached is not null && _raiseInitialSnapshot)
 5186            RaiseInitialSnapshot(attached);
 5187    }
 88
 89    /// <summary>
 90    /// Relays current values of the source's public properties (attach-time snapshot).
 91    /// </summary>
 92    private void RaiseInitialSnapshot(INotifyPropertyChanged source)
 93    {
 5194        if (IsDisposed || IsSuspended)
 095            return;
 96
 20497        foreach (var (childPropertyName, _) in NotifyPropertyChangedSourceSnapshot.EnumerateValues(source))
 5198            RelayPropertyChanged(childPropertyName);
 5199    }
 100
 101    /// <summary>
 102    /// Handles PropertyChanged events from the source object and relays them to the owner.
 103    /// </summary>
 104    /// <param name="sender">The source object that raised the event.</param>
 105    /// <param name="e">The event arguments containing the name of the property that changed.</param>
 106    private void OnSourcePropertyChanged(object? sender, PropertyChangedEventArgs e)
 107    {
 27108        if (IsDisposed || IsSuspended)
 0109            return;
 110
 27111        if (string.IsNullOrWhiteSpace(e.PropertyName))
 0112            return;
 113
 114        lock (_gate)
 115        {
 27116            if (!ReferenceEquals(sender, _source))
 0117                return;
 27118        }
 119
 27120        RelayPropertyChanged(e.PropertyName);
 27121    }
 122
 123    private void RelayPropertyChanged(string childPropertyName)
 124    {
 78125        if (IsDisposed || IsSuspended)
 0126            return;
 127
 78128        var relayedName = _concatenatePropertyName ? $"{_propertyName}.{childPropertyName}" : childPropertyName;
 78129        Owner.NotifyPropertyChanged(relayedName, UnknownValue.Instance, UnknownValue.Instance);
 78130    }
 131
 132    /// <inheritdoc/>
 133    protected override void DisposeManagedResources()
 18134    {
 135        lock (_gate)
 136        {
 18137            _source?.PropertyChanged -= OnSourcePropertyChanged;
 18138            _source = null;
 18139        }
 140
 18141        base.DisposeManagedResources();
 18142    }
 143}
 144