internal PropertyData(object defaultValue, Type type, PropertyChangedCallbackHandler propertyChangedCallback) { Value = defaultValue; Type = type; PropertyChangedCallback += propertyChangedCallback; }
/// <summary> /// Registers a new property in the actual instance of <see cref="NotifyPropertyChanged"/>. /// </summary> /// <param name="name">Name of the registered property.</param> /// <param name="type">Type of the registered property.</param> /// <param name="defaultValue">Default value of the registered property.</param> /// <param name="propertyChangedCallback">Callback invoked right after the registered property changes.</param> /// <exception cref="ArgumentException"> /// <para> /// Parameter <paramref name="name"/> is <c>null</c> or white space. /// </para> /// <para> /// Value of <paramref name="defaultValue"/> cannot be assigned to a property of type specified in the <paramref name="type"/> parameter. /// </para> /// <para> /// Instance already contains a registered property named the same as specified in parameter <paramref name="name"/>. /// </para> /// </exception> /// <exception cref="ArgumentNullException">Parameter <paramref name="type"/> is <c>null</c>.</exception> protected void RegisterProperty(string name, Type type, object defaultValue, PropertyChangedCallbackHandler propertyChangedCallback) { Helpers.ValidateStringNotNullOrWhiteSpace(name, nameof(name)); Helpers.ValidateObjectNotNull(type, nameof(type)); ValidateValueForType(defaultValue, type); if (backingStore.ContainsKey(name)) { throw new ArgumentException($"This class already contains registered property named '{name}'."); } backingStore.Add(name, new PropertyData(defaultValue, type, propertyChangedCallback)); }
public void WorkingWithPropertiesTest() { Wrapper w = new Wrapper(); // Invalid property name AllThrows <string, ArgumentException>(invalidPropertyNames, invalidPropertyName => w.GetValue(invalidPropertyName)); AllThrows <string, ArgumentException>(invalidPropertyNames, invalidPropertyName => w.SetValue(null, invalidPropertyName)); AllThrows <string, ArgumentException>(invalidPropertyNames, invalidPropertyName => w.ForceSetValue(null, invalidPropertyName)); // Not registered property Assert.ThrowsException <ArgumentException>(() => w.GetValue("NotRegisteredProperty")); Assert.ThrowsException <ArgumentException>(() => w.SetValue(null, "NotRegisteredProperty")); Assert.ThrowsException <ArgumentException>(() => w.ForceSetValue(null, "NotRegisteredProperty")); int propertyChangedEventInvokeCount = 0; int propertyChangedCallbackInvokeCount = 0; bool propertyChangedCallbackRegistered = true; string propertyName = null; object oldValue = null; object value = null; w.PropertyChanged += (sender, e) => { Assert.AreEqual(w, sender); Assert.AreEqual(propertyName, e.PropertyName); propertyChangedEventInvokeCount++; }; PropertyChangedCallbackHandler propertyChangedCallback = (sender, e) => { Assert.AreEqual(w, sender); Assert.IsFalse(e.Handled); Assert.AreEqual(oldValue, e.OldValue); Assert.AreEqual(value, e.NewValue); propertyChangedCallbackInvokeCount++; }; foreach (TypeData typeData in typeDataCollection) { for (int i = 0; i < typeData.DefaultValues.Length; i++) { object defaultValue = typeData.DefaultValues[i]; propertyName = typeData.Type.Name + i; Test(false, true, defaultValue); propertyName += "_Forced"; Test(true, true, defaultValue); propertyName = typeData.Type.Name + i + "_NoPropertyChangedEvent"; Test(false, false, defaultValue); propertyName += "_Forced"; Test(true, false, defaultValue); } // Invalid value AllThrows <object, ArgumentException>(typeData.InvalidValues, invalidValue => w.SetValue(invalidValue, propertyName)); AllThrows <object, ArgumentException>(typeData.InvalidValues, invalidValue => w.ForceSetValue(invalidValue, propertyName)); void Test(bool force, bool eventsEnabled, object defaultValue) { w.IsPropertyChangedEventInvokingEnabled = eventsEnabled; w.IsPropertyChangedCallbackInvokingEnabled = eventsEnabled; propertyChangedCallbackRegistered = true; oldValue = null; value = defaultValue; // Default value w.RegisterProperty(propertyName, typeData.Type, value, propertyChangedCallback); Assert.AreEqual(value, w.GetValue(propertyName)); CheckEventsInvoked(false); CheckNoChangeSet(); propertyChangedCallbackRegistered = false; w.UnregisterPropertyChangedCallback(propertyName, propertyChangedCallback); // Changing value but not assigning it to property SetValue(typeData.GetNewValue(value)); // Probably not needed but I like this ( ͡° ͜ʖ ͡°) Assert.AreNotEqual(value, w.GetValue(propertyName)); CheckEventsInvoked(false); // Assigning changed value SetAndTest(eventsEnabled); CheckNoChangeSet(); w.RegisterPropertyChangedCallback(propertyName, propertyChangedCallback); propertyChangedCallbackRegistered = true; // Assigning default value e.g. null etc. SetValue(defaultValue); SetAndTest(eventsEnabled); CheckNoChangeSet(); void SetValue(object newValue) { oldValue = value; value = newValue; } void SetAndTest(bool shouldInvokeEvents) { if (force) { w.ForceSetValue(value, propertyName); } else { w.SetValue(value, propertyName); } Assert.AreEqual(value, w.GetValue(propertyName)); CheckEventsInvoked(shouldInvokeEvents); } void CheckNoChangeSet() { if (force) { // Has to be set to the same // When force == true, it will call callback oldValue = value; } // No change in value - invoking events only when forced SetAndTest(force && eventsEnabled); } void CheckEventsInvoked(bool shouldInvokeEvents) { if (shouldInvokeEvents) { Assert.AreEqual(1, propertyChangedEventInvokeCount); propertyChangedEventInvokeCount = 0; if (propertyChangedCallbackRegistered) { Assert.AreEqual(1, propertyChangedCallbackInvokeCount); propertyChangedCallbackInvokeCount = 0; } } } } } }
/// <summary> /// Unregisters the <paramref name="propertyChangedCallback"/> so it will NOT be invoked when the property with the specified name changes. /// </summary> /// <param name="propertyName">Name of the property.</param> /// <param name="propertyChangedCallback"><see cref="PropertyChangedCallbackHandler"/> to be unregistered.</param> /// <exception cref="ArgumentNullException"> /// <para> /// Parameter <paramref name="propertyName"/> is <c>null</c> or white space. /// </para> /// <para> /// Actual instance does not contain registered property with the specified name. /// </para> /// <para> /// Parameter <paramref name="propertyChangedCallback"/> is <c>null</c>. /// </para> /// </exception> protected void UnregisterPropertyChangedCallback(string propertyName, PropertyChangedCallbackHandler propertyChangedCallback) { Helpers.ValidateObjectNotNull(propertyChangedCallback, nameof(propertyChangedCallback)); GetPropertyData(propertyName, nameof(propertyName)).PropertyChangedCallback -= propertyChangedCallback; }
internal new void UnregisterPropertyChangedCallback(string propertyName, PropertyChangedCallbackHandler propertyChangedCallback) { base.UnregisterPropertyChangedCallback(propertyName, propertyChangedCallback); }
internal new void RegisterProperty(string name, Type type, object defaultValue, PropertyChangedCallbackHandler propertyChangedCallback) { base.RegisterProperty(name, type, defaultValue, propertyChangedCallback); }