-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Correctly propagate BindingContext to Border StrokeShape #13793
Changes from 8 commits
104f170
c88665e
2e8587e
ab75305
d5d74ec
3aeec66
ac8e299
aad92c9
18b2c20
68a1f65
8509689
532babc
a6d5419
63ed402
769e71a
86e19f0
6da1ed1
121eb9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
using System.Collections.Generic; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Collections.Specialized; | ||
using System.Runtime.CompilerServices; | ||
|
@@ -14,6 +15,10 @@ public class Border : View, IContentView, IBorderView, IPaddingElement | |
float[]? _strokeDashPattern; | ||
ReadOnlyCollection<Element>? _logicalChildren; | ||
|
||
WeakNotifyPropertyChangedProxy? _strokeShapeProxy = null; | ||
|
||
~Border() => _strokeShapeProxy?.Unsubscribe(); | ||
|
||
internal ObservableCollection<Element> InternalChildren { get; } = new(); | ||
|
||
internal override IReadOnlyList<Element> LogicalChildrenInternal => | ||
|
@@ -37,7 +42,42 @@ public Thickness Padding | |
} | ||
|
||
public static readonly BindableProperty StrokeShapeProperty = | ||
BindableProperty.Create(nameof(StrokeShape), typeof(IShape), typeof(Border), new Rectangle()); | ||
BindableProperty.Create(nameof(StrokeShape), typeof(IShape), typeof(Border), new Rectangle(), | ||
propertyChanging: (bindable, oldvalue, newvalue) => | ||
{ | ||
if (newvalue is not null) | ||
(bindable as Border)?.StopNotifyingStrokeShapeChanges(); | ||
}, | ||
propertyChanged: (bindable, oldvalue, newvalue) => | ||
{ | ||
if (newvalue is not null) | ||
(bindable as Border)?.NotifyStrokeShapeChanges(); | ||
}); | ||
|
||
void NotifyStrokeShapeChanges() | ||
{ | ||
var strokeShape = StrokeShape; | ||
|
||
if (strokeShape is VisualElement visualElement) | ||
{ | ||
SetInheritedBindingContext(visualElement, BindingContext); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were not propagating the BindingContext to the Shape used in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need Parent AND SetInheritedBindingContext? Does not setting Parent automatically set the binding context? I am not sure, so just checking... Does the order matter? |
||
visualElement.Parent = this; | ||
var proxy = _strokeShapeProxy ??= new(); | ||
proxy.Subscribe(visualElement, (sender, e) => OnPropertyChanged(nameof(StrokeShape))); | ||
} | ||
} | ||
|
||
void StopNotifyingStrokeShapeChanges() | ||
{ | ||
var strokeShape = StrokeShape; | ||
|
||
if (strokeShape is VisualElement visualElement) | ||
{ | ||
SetInheritedBindingContext(visualElement, null); | ||
visualElement.Parent = null; | ||
_strokeShapeProxy?.Unsubscribe(); | ||
} | ||
} | ||
|
||
public static readonly BindableProperty StrokeProperty = | ||
BindableProperty.Create(nameof(Stroke), typeof(Brush), typeof(Border), null); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
using System; | ||
using System.Collections.Specialized; | ||
using System.ComponentModel; | ||
|
||
// NOTE: warning disabled for netstandard projects | ||
#pragma warning disable 0436 | ||
|
@@ -102,4 +103,53 @@ public override void Unsubscribe() | |
base.Unsubscribe(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// A "proxy" class for subscribing INotifyPropertyChanged via WeakReference. | ||
/// General usage is to store this in a member variable and call Subscribe()/Unsubscribe() appropriately. | ||
/// Your class should have a finalizer that calls Unsubscribe() to prevent WeakNotifyCollectionChangedProxy objects from leaking. | ||
/// </summary> | ||
class WeakNotifyPropertyChangedProxy : WeakEventProxy<INotifyPropertyChanged, PropertyChangedEventHandler> | ||
{ | ||
public WeakNotifyPropertyChangedProxy() { } | ||
|
||
public WeakNotifyPropertyChangedProxy(INotifyPropertyChanged source, PropertyChangedEventHandler handler) | ||
{ | ||
Subscribe(source, handler); | ||
} | ||
|
||
void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) | ||
{ | ||
if (TryGetHandler(out var handler)) | ||
{ | ||
handler(sender, e); | ||
} | ||
else | ||
{ | ||
Unsubscribe(); | ||
} | ||
} | ||
|
||
public override void Subscribe(INotifyPropertyChanged source, PropertyChangedEventHandler handler) | ||
{ | ||
if (TryGetSource(out var s)) | ||
{ | ||
s.PropertyChanged -= OnPropertyChanged; | ||
} | ||
|
||
source.PropertyChanged += OnPropertyChanged; | ||
|
||
base.Subscribe(source, handler); | ||
} | ||
|
||
public override void Unsubscribe() | ||
{ | ||
if (TryGetSource(out var s)) | ||
{ | ||
s.PropertyChanged -= OnPropertyChanged; | ||
} | ||
|
||
base.Unsubscribe(); | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we revert this whitespace change? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this always fire? If I set a value and then null, this will not unsub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is wrong, can we add a test for this?