-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Release Type: Latest built in debug
Describe the bug
Let's say you have a
public class MyComp : StartupScript
{
public List<Test2> MyField = new List<Test2>();
[DataContract] public struct Test2{ public bool b; }
}Adding a new item and setting its b field to true in the property grid crashes the gamestudio.
Why ? An AssetMemberNode instance runs its Update function, note the NotifyContentChanging and NotifyContentChanged below:
stride/sources/presentation/Stride.Core.Quantum/MemberNode.cs
Lines 102 to 124 in ba36a7e
| private void Update(object newValue, bool sendNotification) | |
| { | |
| var oldValue = Retrieve(); | |
| MemberNodeChangeEventArgs args = null; | |
| if (sendNotification) | |
| { | |
| args = new MemberNodeChangeEventArgs(this, oldValue, newValue); | |
| NotifyContentChanging(args); | |
| } | |
| var containerValue = Parent.Retrieve(); | |
| if (containerValue == null) | |
| throw new InvalidOperationException("Container's value is null"); | |
| MemberDescriptor.Set(containerValue, ConvertValue(newValue, MemberDescriptor.Type)); | |
| if (containerValue.GetType().GetTypeInfo().IsValueType) | |
| ((GraphNodeBase)Parent).UpdateFromMember(containerValue, NodeIndex.Empty); | |
| UpdateReferences(); | |
| if (sendNotification) | |
| { | |
| NotifyContentChanged(args); | |
| } | |
| } |
NotifyContentChanging calls OnPropertyChanged on an AssetNodeViewModel instance with name b and value false further down the stack:
stride/sources/presentation/Stride.Core.Presentation/ViewModels/ViewModelBase.cs
Lines 240 to 260 in ba36a7e
| protected virtual void OnPropertyChanging(params string[] propertyNames) | |
| { | |
| var propertyChanging = PropertyChanging; | |
| foreach (var propertyName in propertyNames) | |
| { | |
| #if DEBUG | |
| if (changingProperties.Contains(propertyName)) | |
| throw new InvalidOperationException($"OnPropertyChanging called twice for property '{propertyName}' without invoking OnPropertyChanged between calls."); | |
| changingProperties.Add(propertyName); | |
| #endif | |
| propertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); | |
| if (DependentProperties.TryGetValue(propertyName, out var dependentProperties)) | |
| { | |
| OnPropertyChanging(dependentProperties); | |
| } | |
| } | |
| } |
Notice the hashset insertion wrapped inside a
DEBUG scope.
After this, NotifyContentChanged from the first snippet runs, creating a new AssetNodeViewModel with name b and value true, then immediately calls
stride/sources/presentation/Stride.Core.Presentation/ViewModels/ViewModelBase.cs
Lines 266 to 289 in ba36a7e
| protected virtual void OnPropertyChanged(params string[] propertyNames) | |
| { | |
| var propertyChanged = PropertyChanged; | |
| for (var i = 0; i < propertyNames.Length; ++i) | |
| { | |
| var propertyName = propertyNames[propertyNames.Length - 1 - i]; | |
| if (DependentProperties.TryGetValue(propertyName, out var dependentProperties)) | |
| { | |
| var reverseList = new string[dependentProperties.Length]; | |
| for (var j = 0; j < dependentProperties.Length; ++j) | |
| reverseList[j] = dependentProperties[dependentProperties.Length - 1 - j]; | |
| OnPropertyChanged(reverseList); | |
| } | |
| propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |
| #if DEBUG | |
| if (!changingProperties.Contains(propertyName)) | |
| throw new InvalidOperationException($"OnPropertyChanged called for property '{propertyName}' but OnPropertyChanging was not invoked before."); | |
| changingProperties.Remove(propertyName); | |
| #endif | |
| } | |
| } |