diff --git a/src/Uno.UI.Tests/DependencyProperty/Given_DependencyProperty.DataContext.cs b/src/Uno.UI.Tests/DependencyProperty/Given_DependencyProperty.DataContext.cs
index 286248907404..dc83a565b6a4 100644
--- a/src/Uno.UI.Tests/DependencyProperty/Given_DependencyProperty.DataContext.cs
+++ b/src/Uno.UI.Tests/DependencyProperty/Given_DependencyProperty.DataContext.cs
@@ -199,6 +199,38 @@ public void When_DataContext_Inherited_And_Parent_Changed()
Assert.AreEqual(42, SUT.DataContext);
}
+
+ [TestMethod]
+ public void When_DataContext_Inherited_And_Child_Removed()
+ {
+ var SUT = new MyControl();
+
+ var independentChild = new MyControl();
+ var parent = new Grid
+ {
+ Children = { independentChild, SUT },
+ DataContext = 42
+ };
+
+ // And here is the trick: the SUT does have a reference to the child
+ SUT.InnerControl = independentChild;
+
+ int parentCtxChanged = 0, childCtxChanged = 0, SUTCtxChanged = 0;
+ parent.RegisterPropertyChangedCallback(Control.DataContextProperty, (snd, dp) => parentCtxChanged++);
+ independentChild.RegisterPropertyChangedCallback(Control.DataContextProperty, (snd, dp) => childCtxChanged++);
+ SUT.RegisterPropertyChangedCallback(Control.DataContextProperty, (snd, dp) => SUTCtxChanged++);
+
+ Assert.AreEqual(42, SUT.DataContext);
+
+ // And here the issue: when we remove the SUT from the parent,
+ // it will propagate to the independentChild the DataContext change ... while it should not!
+ parent.Children.Remove(SUT);
+
+ Assert.AreEqual(null, SUT.DataContext);
+ Assert.AreEqual(0, parentCtxChanged);
+ Assert.AreEqual(1, SUTCtxChanged);
+ Assert.AreEqual(0, childCtxChanged);
+ }
}
public partial class MyBasicListType : DependencyObject
@@ -264,6 +296,18 @@ private void OnInnerObjectChanged(DependencyPropertyChangedEventArgs e)
}
#endregion
+ }
+ public partial class MyControl : Control
+ {
+ // Just a standard DP defined by a project / third party component
+ public static readonly DependencyProperty InnerControlProperty = DependencyProperty.Register(
+ "InnerControl", typeof(MyControl), typeof(MyControl), new PropertyMetadata(default(MyControl)));
+
+ public MyControl InnerControl
+ {
+ get { return (MyControl)GetValue(InnerControlProperty); }
+ set { SetValue(InnerControlProperty, value); }
+ }
}
}
diff --git a/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs b/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs
index 022ccdcc4f08..1a760ee7c73b 100644
--- a/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs
+++ b/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs
@@ -494,7 +494,7 @@ private void OnDependencyPropertyChanged(DependencyPropertyDetails propertyDetai
var (hasValueInherits, hasValueDoesNotInherit) = GetPropertyInheritanceConfiguration(propertyDetails);
- if (!hasValueDoesNotInherit && (hasValueInherits || propertyDetails.Property.HasAutoDataContextInherit))
+ if (!hasValueDoesNotInherit && hasValueInherits)
{
if(args.NewValue is IDependencyObjectStoreProvider provider)
{
diff --git a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadata.cs b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadata.cs
index ecad3a614f6b..a362ee5b2194 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadata.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadata.cs
@@ -31,7 +31,7 @@ object defaultValue
FrameworkPropertyMetadataOptions options
) : base(defaultValue)
{
- Options = options;
+ Options = options.WithDefault();
}
public FrameworkPropertyMetadata(
@@ -40,7 +40,7 @@ FrameworkPropertyMetadataOptions options
PropertyChangedCallback propertyChangedCallback
) : base(defaultValue, propertyChangedCallback)
{
- Options = options;
+ Options = options.WithDefault();
}
internal FrameworkPropertyMetadata(
@@ -50,7 +50,7 @@ PropertyChangedCallback propertyChangedCallback
BackingFieldUpdateCallback backingFieldUpdateCallback
) : base(defaultValue, propertyChangedCallback, backingFieldUpdateCallback)
{
- Options = options;
+ Options = options.WithDefault();
}
internal FrameworkPropertyMetadata(
@@ -66,7 +66,7 @@ BackingFieldUpdateCallback backingFieldUpdateCallback
BackingFieldUpdateCallback backingFieldUpdateCallback
) : base(defaultValue, null, backingFieldUpdateCallback)
{
- Options = options;
+ Options = options.WithDefault();
}
internal FrameworkPropertyMetadata(
@@ -76,7 +76,7 @@ BackingFieldUpdateCallback backingFieldUpdateCallback
CoerceValueCallback coerceValueCallback
) : base(defaultValue, propertyChangedCallback, coerceValueCallback, null)
{
- Options = options;
+ Options = options.WithDefault();
}
internal FrameworkPropertyMetadata(
@@ -87,7 +87,7 @@ CoerceValueCallback coerceValueCallback
BackingFieldUpdateCallback backingFieldUpdateCallback
) : base(defaultValue, propertyChangedCallback, coerceValueCallback, backingFieldUpdateCallback)
{
- Options = options;
+ Options = options.WithDefault();
}
internal FrameworkPropertyMetadata(
@@ -128,11 +128,11 @@ CoerceValueCallback coerceValueCallback
UpdateSourceTrigger defaultUpdateSourceTrigger
) : base(defaultValue, propertyChangedCallback, coerceValueCallback, null)
{
- Options = options;
+ Options = options.WithDefault();
DefaultUpdateSourceTrigger = defaultUpdateSourceTrigger;
}
-
- public FrameworkPropertyMetadataOptions Options { get; set; }
+
+ public FrameworkPropertyMetadataOptions Options { get; set; } = FrameworkPropertyMetadataOptions.Default;
public UpdateSourceTrigger DefaultUpdateSourceTrigger
{
diff --git a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptions.cs b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptions.cs
index 00809da37979..27178fcc4d3b 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptions.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptions.cs
@@ -18,10 +18,6 @@ public enum FrameworkPropertyMetadataOptions
///
/// Specifies that this property's value or values will inherit the DataContext of its or their parent.
///
- ///
- /// This property is ignored and ValueInheritsDataContext is interpreted as true for properties of type .
- /// Use ValueDoesNotInheritDataContext to prevent this default behavior.
- ///
ValueInheritsDataContext = 1 << 1,
///
@@ -33,7 +29,8 @@ public enum FrameworkPropertyMetadataOptions
/// Prevents this property's value or values from inheriting the DataContext of its or their parent.
///
///
- /// This is useful for properties of type for which ValueInheritsDataContext is always interpreted as true.
+ /// This is useful for framework properties of type for which ValueInheritsDataContext is enabled by default
+ /// (cf. ).
///
ValueDoesNotInheritDataContext = 1 << 3,
@@ -62,5 +59,12 @@ public enum FrameworkPropertyMetadataOptions
///
AffectsRender = 1 << 8,
+ ///
+ /// The default options set when creating a .
+ ///
+ ///
+ /// By default all DP declared by the framework will inherit the DataContext while DP declared by application won't.
+ ///
+ Default = ValueInheritsDataContext,
}
}
diff --git a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptionsExtensions.cs b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptionsExtensions.cs
index 6ee7d289d0f1..611f81353752 100644
--- a/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptionsExtensions.cs
+++ b/src/Uno.UI/UI/Xaml/FrameworkPropertyMetadataOptionsExtensions.cs
@@ -67,5 +67,12 @@ public static bool HasLogicalChild(this FrameworkPropertyMetadataOptions options
public static bool HasWeakStorage(this FrameworkPropertyMetadataOptions options)
=> (options & FrameworkPropertyMetadataOptions.WeakStorage) != 0;
+ ///
+ /// Defines the default flags for a FrameworkPropertyMetadata if not explicitly opt-out (cf. ).
+ ///
+ internal static FrameworkPropertyMetadataOptions WithDefault(this FrameworkPropertyMetadataOptions options)
+ => (options & (FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext | FrameworkPropertyMetadataOptions.ValueInheritsDataContext)) == default
+ ? options | FrameworkPropertyMetadataOptions.ValueInheritsDataContext // == FrameworkPropertyMetadataOptions.Default
+ : options;
}
}