示例#1
0
        /// <summary>
        /// Sets a property value and raises the <see cref="PropertyChanged"/> event where applicable.
        /// </summary>
        /// <typeparam name="T">The property <see cref="Type"/>.</typeparam>
        /// <param name="propertyValue">The property value to set.</param>
        /// <param name="setValue">The value to set.</param>
        /// <param name="immutable">Indicates whether the value is immutable; can not be changed once set.</param>
        /// <param name="bubblePropertyChanged">Indicates whether the value should bubble up property changes versus only recording within the sub-entity itself.</param>
        /// <param name="beforeChange">Function to invoke before changing the value; a result of <c>true</c> indicates that the property change is to be cancelled; otherwise, <c>false</c>.</param>
        /// <param name="propertyNames">The names of the properties that changed.</param>
        /// <returns><c>true</c> indicates that the property value changed; otherwise, <c>false</c>.</returns>
        /// <remarks>The first property name specified (see <paramref name="propertyNames"/>) is the primary property; therefore, the only property
        /// where the <see cref="BeforePropertyChanged"/> event is raised. The additional property names allow for the <see cref="PropertyChanged"/>
        /// event to be raised for other properties where related versus having to raise seperately.</remarks>
        protected bool SetValue <T>(ref T propertyValue, T setValue, bool immutable = false, bool bubblePropertyChanged = false, Func <T, bool>?beforeChange = null, params string[] propertyNames)
        {
            ValidateSetValuePropertyNames(propertyNames);

            lock (_lock)
            {
                // Check and see if the value has changed or not; exit if being set to same value.
                var isChanged = true;
                T   val       = Cleaner.Clean(setValue);
                if (propertyValue is IComparable <T> )
                {
                    if (Comparer <T> .Default.Compare(val, propertyValue) == 0)
                    {
                        isChanged = false;
                    }
                }
                else if (Equals(propertyValue, val))
                {
                    isChanged = false;
                }

                if (!isChanged && !RaisePropertyChangedWhenSame)
                {
                    return(false);
                }

                // Test is read only.
                if (IsReadOnly)
                {
                    return(!isChanged ? false : throw new InvalidOperationException(EntityIsReadOnlyMessage));
                }

                // Test immutability.
                if (immutable && isChanged && Comparer <T> .Default.Compare(propertyValue, default !) != 0)
                {
                    throw new InvalidOperationException(ValueIsImmutableMessage);
                }

                // Handle on before property changed.
                if (beforeChange != null)
                {
                    if (beforeChange.Invoke(val))
                    {
                        return(false);
                    }
                }

                if (OnBeforePropertyChanged(propertyNames[0], val))
                {
                    return(false);
                }

                // Determine bubbling and unwire old value.
                INotifyPropertyChanged?npc;
                if (bubblePropertyChanged && propertyValue != null)
                {
                    npc = propertyValue as INotifyPropertyChanged;
                    if (npc != null)
                    {
                        npc.PropertyChanged -= GetValue_PropertyChanged(propertyNames);
                    }
                }

                // Track changes for the new value where parent (this) is tracking.
                if (this is IChangeTrackingLogging ct && ct.IsChangeTracking && val is IChangeTrackingLogging sct && !sct.IsChangeTracking)
                {
                    sct.TrackChanges();
                }

                // Update the property and trigger the property changed.
                propertyValue = val;
                TriggerPropertyChanged(propertyNames);

                // Determine bubbling and wire up new value.
                if (bubblePropertyChanged && val != null)
                {
                    npc = val as INotifyPropertyChanged;
                    if (npc != null)
                    {
                        npc.PropertyChanged += GetValue_PropertyChanged(propertyNames);
                    }
                }

                return(true);
            }
        }