Skip to content

Conversation

@grokys
Copy link
Member

@grokys grokys commented Feb 20, 2025

What does the pull request do?

As described in #17029 adds support for the null-conditional operator (?.) to reflection and compiled bindings.

What is the current behavior?

If a null is encountered while evaluating in a binding path, then a warning will be logged and the binding will result in an error state. If such a binding is used in a multi-binding then the multi-binding will not produce a value as it will consider its input to be in an error state.

What is the updated/expected behavior with this PR?

One can now use the ?. operator in a binding path to indicate "it is expected that this property may be null". If this is the case then the binding will produce null instead of an error. An example:

<TextBlock Text='{Binding Second.Third?.Final}'/>

Indicates that it is expected that Third may be null.

Note that this PR does not implement the related ?[] operator for array indexing.

Fixed issues

Fixes #17029

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0055010-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Copy link
Member

@MrJul MrJul left a comment

Choose a reason for hiding this comment

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

There's a scenario that isn't handled properly currently: a type cast followed by a null conditional operator.

For example, {Binding ((SomeType)First)?.Second, TargetNullValue='null!'} won't display the TargetNullValue if First is null.

Edit: shouldn't be part of this PR, this is changing how the cast works, see comments below.

Apart from that and two very minor points, this looks good to me!

@maxkatz6
Copy link
Member

maxkatz6 commented Feb 23, 2025

For example, {Binding ((SomeType)First)?.Second, TargetNullValue='null!'} won't display the TargetNullValue if First is null.

Some users might expect invalid cast exception here (and so, FallbackValue to be used instead). We don't have any as operator equvalent.

...but at the same time, "?" might be a good enough indicator in the binding syntax.

@MrJul
Copy link
Member

MrJul commented Feb 23, 2025

Some users might expect invalid cast exception

null can be cast to all reference types without any exception, so I wouldn't expect one here, matching the standard C# cast / .NET castclass.

I now realize that this isn't the current behavior of the binding cast at all: {Binding ((SomeReferenceType)Foo), TargetNullValue='null!'} when Foo is null fails before this PR. In practice, it doesn't really matter today, since all usages of the cast would usually be followed a property path, causing the fallback value to be used.

After this PR, the nuance becomes a bit more important.
Altering the cast behavior is totally out of scope of this PR anyway, and if done, should probably be a v12 change.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0055482-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Copy link
Member

@MrJul MrJul left a comment

Choose a reason for hiding this comment

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

LGTM! A nice feature :)

@MrJul MrJul enabled auto-merge March 12, 2025 13:01
@maxkatz6 maxkatz6 disabled auto-merge March 14, 2025 06:46
@maxkatz6 maxkatz6 merged commit fb5121a into master Mar 14, 2025
9 of 11 checks passed
@maxkatz6 maxkatz6 deleted the feature/17029-null-conditional-bindings branch March 14, 2025 06:47
@Jannik-M91
Copy link

First of all thanks for your work and fixing my issue (#17029)!

I tried the 11.3.0-beta2 release and while I can now fix some of the issues I had, the behavior is not as expected in others. But maybe I am just using this feature wrong.

I am using bindings with array indexing, I have seen that the ?[] operator is not supported and that is fine, but the following does also not work when MyProperty is null:

        <NumericUpDown
                       Increment="{Binding MyProperty?.Increment[0], TargetNullValue=1}"
                       Value="{Binding MyProperty?.Value[0], TargetNullValue=0}"
                       Minimum="{Binding MyProperty?.Min[0], TargetNullValue=0}}"
                       Maximum="{Binding MyProperty?.Max[0], TargetNullValue=0}}" />

which results in the following error messages:

[Binding]An error occurred binding 'Increment' to 'LensIris.Increment.Item' at 'Increment': 'Value is null.' (NumericUpDown #45015971)
[Binding]An error occurred binding 'Value' to 'LensIris.Value.Item' at 'Value': 'Value is null.' (NumericUpDown #45015971)
[Binding]An error occurred binding 'Minimum' to 'LensIris.Min.Item' at 'Min': 'Value is null.' (NumericUpDown #45015971)
[Binding]An error occurred binding 'Maximum' to 'LensIris.Max.Item' at 'Max': 'Value is null.' (NumericUpDown #45015971)

Why are the child properties (Increment, Value, Min, Max) evaluated at all? I would have expected the evaluation to stop at MyProperty and then use the TargetNullValue. It does work that way for "normal" (non-array) child properties.

Note: If this is not the right place to bring this up please let me know, I will then open a separate discussion or bug report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Null-conditional operators for nested Bindings

6 participants