Skip to content
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

Make bindings which produce UnsetValue revert to default value for property #10189

Merged
merged 8 commits into from
Feb 11, 2023

Conversation

grokys
Copy link
Member

@grokys grokys commented Feb 3, 2023

What does the pull request do?

As mentioned in #10110, #8600 introduced a problem whereby if a binding produces UnsetValue (or a binding error) then the binding is unsubscribed.

This started happening because one of the aims of #8600 was to only have a subscription to one single active binding at a time 1, but historically our behavior when a binding produces UnsetValue was for a binding or value with a lower priority to take effect; requiring multiple bindings to be subscribed.

As discussed in #10039, this historical behavior is different from WPF and I think can be changed. In WPF when a property produces UnsetValue, the property value is simply set to the default value of the property ignoring any lower priority values.

This PR makes that change. I'm leaving it as a draft for the moment to discuss the implications of this if any.

Breaking changes

Changes the behavior of bindings. If anyone's binding was producing UnsetValue expecting the property value to revert to the value from a style etc then this will break.

Fixed issues

Fixes #10110

cc: @tomenscape for review/comments because this is something you've encountered.

Footnotes

  1. Previously all bindings were subscribed, even if their value was not visible which caused performance problems in some cases

This changes the behavior of bindings slightly, in that previously a binding which produced `UnsetValue` (or a binding error) caused the value of any lower priority value of value to take effect. After this change, the binding reverts to the property's default value. This behavior more closely matches WPF and fixes #10110.
Local value bindings with a type of `BindingValue<T>` were not being validated.
/// Checks whether a value is assigned to the property, or that there is a binding to the
/// property that is producing a value other than <see cref="AvaloniaProperty.UnsetValue"/>.
/// Returns true if <paramref name="property"/> is a styled property which has a value
/// assigned to it or a binding targeting it; otherwise false.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the change in semantics, the meaning of IsSet had to change as well.

owner.ClearLocalValue(property);
{
var effectiveValue = value.Value;
if (property.ValidateValue?.Invoke(effectiveValue) == false)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes a bug here where we actually weren't validating the value in this case. Also added a unit test.

@avaloniaui-team
Copy link
Contributor

You can test this PR using the following package version. 11.0.999-cibuild0029508-beta. (feed url: https://pkgs.dev.azure.com/AvaloniaUI/AvaloniaUI/_packaging/avalonia-all/nuget/v3/index.json) [PRBUILDID]

@TomEdwardsEnscape
Copy link
Contributor

I've not run this code but it looks like exactly what I had in mind, so 👍 from me.

This started happening because one of the aims of #8600 was to only have a subscription to one single active binding at a time, but historically our behavior when a binding produces UnsetValue was for a binding or value with a lower priority to take effect; requiring multiple bindings to be subscribed.

Not strictly related to this PR, but if Avalonia doesn't update the value of non-effective bindings, then what happens should they become effective later? Do we see a stale value, or is the current value retrieved from the source?

@grokys
Copy link
Member Author

grokys commented Feb 9, 2023

Not strictly related to this PR, but if Avalonia doesn't update the value of non-effective bindings, then what happens should they become effective later? Do we see a stale value, or is the current value retrieved from the source?

Yes, when a binding becomes effective, it is subscribed, so the current value will be retrieved from the source.

@grokys grokys marked this pull request as ready for review February 9, 2023 09:52
@grokys
Copy link
Member Author

grokys commented Feb 9, 2023

I think this is ready for review now. The only thing I'm not sure about is the behavior of bindings before they produce a value. Currently, there is an exception to the "one active binding subscription per property" rule, in that until a binding produces a value, multiple subscriptions can be active. This shouldn't be encountered in normal circumstances because standard bindings produce a value immediately in all cases except animation bindings.

I started making a change whereby bindings that don't produce a value when subscribed cause the default value to be produced, but some of the animation tests started failing. Problem is that there are so few animation tests that I'm unsure whether this will cause a real problem with animations, or if it's just how the tests were written.

Given the above, I think I'm going to leave this behavior as-is until it causes a problem, or we rewrite the animation stuff and add proper unit tests.

@avaloniaui-team
Copy link
Contributor

You can test this PR using the following package version. 11.0.999-cibuild0029796-beta. (feed url: https://pkgs.dev.azure.com/AvaloniaUI/AvaloniaUI/_packaging/avalonia-all/nuget/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 merged commit f5d28eb into master Feb 11, 2023
@maxkatz6 maxkatz6 deleted the fixes/10110-binding-unset-unsubscribe branch February 11, 2023 06:36
@avaloniaui-team
Copy link
Contributor

You can test this PR using the following package version. 11.0.999-cibuild0029870-beta. (feed url: https://pkgs.dev.azure.com/AvaloniaUI/AvaloniaUI/_packaging/avalonia-all/nuget/v3/index.json) [PRBUILDID]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DynamicResource is unsubscribing from the resource changes after it gets null/unset value once.
4 participants