AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
26.04k stars 2.25k forks source link

`IsVisible` binding without a path is not applied #17587

Open TomEdwardsEnscape opened 11 hours ago

TomEdwardsEnscape commented 11 hours ago

Describe the bug

A binding without a property path applied to IsVisible will not be evaluated. The control will remain visible when it should be hidden.

Adding a path of $self.DataContext works around the issue.

To Reproduce

Start Sandbox with this window XAML:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
        x:Class="Sandbox.MainWindow" x:CompileBindings="False">
  <StackPanel>
    <Border IsVisible="{Binding, Converter={x:Static ObjectConverters.IsNotNull}}" Height="19" Background="Red" />
    <Border IsVisible="{Binding $self.DataContext, Converter={x:Static ObjectConverters.IsNotNull}}" Height="19" Background="Green" />
  </StackPanel>
</Window>

You will see this:

image

Expected behavior

The Border with the red background should not be visible.

Avalonia version

11.2.1

OS

Windows

Additional context

Not reproducible in 11.1.x

stevemonaco commented 10 hours ago

AFAIK, objects can't notify for themselves, only their properties. Hence INotifyPropertyChanged. When you use {Binding} which has no path to a property, then you should think of this as a get-only property fetched only when the View itself demands it (ie. on object initialization for a Control or on command execution for CommandParameter). The binding system doesn't have access to a parent object and therefore won't be aware of when it changes.

You could confirm by putting the converter into your project so you can breakpoint on when it's actually fetched/evaluated: https://github.com/AvaloniaUI/Avalonia/blob/ed0cc758e4c1bae1938fbfed738b254627977a59/src/Avalonia.Base/Data/Converters/ObjectConverters.cs

MrJul commented 5 hours ago

For context, 11.1.x was the exception here. In 0.10.x, 11.0.x and 11.2.0 it doesn't work either.

In 11.1.x, a null data context was considered set when there wasn't a path, effectively breaking any OneTime binding without path (they were stuck with a null value). See https://github.com/AvaloniaUI/Avalonia/pull/16729 for more information.

After cross checking with WPF, it turns out that Avalonia's OneTime bindings have a different behavior from WPF. In WPF, OneTime isn't "only once", it's one time per data context, as documented.

The same behavior is documented for Avalonia, so we should align the implementation. Once that's done, we can consider a null data context in a pathless binding as really null, since that won't break one-time bindings anymore.