DependencyProperty.AddOwner registers multiple callbacks for single type
Posted: 08 Dec 2022, 11:57
Hello. I think, I found a bug in DependencyProperty.AddOwner implementation.
Lets create two UserControls: UserControlA and UserControlB.
UserControlA defines a dependency property Value with a default value and a change callback.
UserControlB reuses this property with AddOwner, redefining its own change callback.
Code:
Lets use these controls somewhere in a view in the following way:
Compiling and runnig this code in WPF we get the following messages in output window:
All is fine, both controls got their change notifications.
But in Noesis this code fails with exception:
Adding a default value to UserControlB property definition:
After that exception does not occure, but there's another problem: UserControlA receives property change callback from UserControlB:
Of course, there's no actual need to use AddOwner in this code (and in my code too, to be fair), but this is a behaviour mismatch between WPF and Noesis, and you guys seem to be willing to fix those things :) Thanks in advance.
Lets create two UserControls: UserControlA and UserControlB.
UserControlA defines a dependency property Value with a default value and a change callback.
UserControlB reuses this property with AddOwner, redefining its own change callback.
Code:
Code: Select all
public partial class UserControlA : UserControl
{
... ctor, InitializeComponent, CLR property definition omitted ...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(UserControlA), new PropertyMetadata(5, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"UserControlA: d is {d.GetType().Name}: Value changed to {e.NewValue}");
}
}
public partial class UserControlB : UserControl
{
... ctor, InitializeComponent, CLR property definition omitted ...
public static readonly DependencyProperty ValueProperty =
UserControlA.ValueProperty.AddOwner(typeof(UserControlB), new PropertyMetadata(OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"UserControlB: d is {d.GetType().Name}, Value changed to {e.NewValue}");
}
}
Code: Select all
<StackPanel Orientation="Vertical">
<vcviews:UserControlA Value="8" />
<vcviews:UserControlB Value="10" />
</StackPanel>
Code: Select all
UserControlA: d is UserControlA: Value changed to 8
UserControlB: d is UserControlB, Value changed to 10
But in Noesis this code fails with exception:
Code: Select all
ArgumentException: Invalid default value type for dependency property 'Brio.MRS.ViewCore.Views.UserControlB.Value'
Noesis.DependencyProperty.ValidateDefaultValue (System.String name, System.Type type, System.Type owner, System.Object value) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Proxies/DependencyPropertyExtend.cs:180)
Noesis.DependencyProperty.AddOwner (System.Type ownerType, Noesis.PropertyMetadata metadata) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Proxies/DependencyPropertyExtend.cs:42)
Brio.MRS.ViewCore.Views.UserControlB..cctor () (at Assets/Modules/ViewCore/Views/UserControlB.xaml.cs:12)
Rethrow as TypeInitializationException: The type initializer for 'Brio.MRS.ViewCore.Views.UserControlB' threw an exception.
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (System.RuntimeTypeHandle type) (at <695d1cc93cca45069c528c15c9fdd749>:0)
Noesis.Extend.RunClassConstructor (System.Type type) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1765)
Noesis.Extend.RegisterDependencyProperties (System.Type type) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1730)
Noesis.Extend.RegisterNativeType (System.Type type, System.Boolean registerDP) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1374)
Noesis.Extend.RegisterNativeType (System.Type type) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1221)
Noesis.Extend.RegisterType (System.String typeName) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1877)
UnityEngine.Debug:LogException(Exception)
NoesisUnity:OnUnhandledException(Exception) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/NoesisUnity.cs:374)
Noesis.Error:UnhandledException(Exception) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Error.cs:18)
Noesis.Extend:RegisterType(String) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/Extend.cs:1882)
Noesis.GUI:Noesis_LoadComponent(HandleRef, String)
Noesis.GUI:LoadComponent(Object, String) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/API/Core/NoesisGUI.cs:322)
NoesisUnity:LoadComponent(Object, String) (at Library/PackageCache/com.noesis.noesisgui@36e5d70d8b/Runtime/NoesisUnity.cs:270)
Brio.MRS.BimViewer.Views.BimViewerView:InitializeComponent() (at Assets/Modules/BimViewer/Views/BimViewerView.xaml.cs:20)
Brio.MRS.BimViewer.Views.BimViewerView:.ctor() (at Assets/Modules/BimViewer/Views/BimViewerView.xaml.cs:14)
...
Code: Select all
public partial class UserControlB : UserControl
{
public static readonly DependencyProperty ValueProperty =
UserControlA.ValueProperty.AddOwner(typeof(UserControlB), new PropertyMetadata(7, OnValueChanged));
//------^
...
}
Code: Select all
UserControlA: d is UserControlA: Value changed to 8
UserControlA: d is UserControlB: Value changed to 10 <== !
UserControlB: d is UserControlB, Value changed to 10