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
24.52k stars 2.12k forks source link

Relative Bindings Fail with CompileBindings Enabled (ReactiveUI/MVVM) #11616

Open SparkyTD opened 1 year ago

SparkyTD commented 1 year ago

Describe the bug I'm experiencing an issue with Avalonia 11.0.0-rc1.1 where relative bindings fail to compile when CompileBindings is enabled. I've tried three different syntaxes for binding to the parent window's ViewModel, all of which yield the same error:

To Reproduce Steps to reproduce the behavior:

  1. Create a new project with the Avalonia MVVM template
  2. Make sure that the MainWindow class derives from ReactiveWindow<MainWindowViewModel>, and not Window
  3. Set CompiledBindings to True on the window
  4. Implement a test command in MainWindowViewModel
  5. Place a button on the window, bind it to the test command via any of the three methods mentioned above
  6. Try to compile the application
  7. The following exception is thrown while compiling the bindings:
    MainWindow.axaml(18, 17): [AVLN:0004] Internal compiler error while emitting node Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.XamlIlBindingPathHelper+XamlIlBindingPathNode:
    System.NullReferenceException: Object reference not set to an instance of an object.
    at Mono.Cecil.ImportGenericContext.TypeParameter(String type, Int32 position)
    at Mono.Cecil.DefaultMetadataImporter.ImportTypeSpecification(TypeReference type, ImportGenericContext context)
    ...

Expected behavior The program should compile successfully.

Desktop:

Additional context The error only occurs when CompileBindings is enabled. If I disable it, the code compiles and the binding works as expected. The error in question is an internal compiler error with a NullReferenceException.

The issue might be related to #10489 and #10486.

Use case The obvious answer would be to simply not use relative bindings and bind to the ViewModel directly (assuming that x:DataType is set on the control/window) which would work as expected regardless of the state of CompiledBindings. However in my specific use case, I was trying to bind to a command in a custom control's ViewModel from within the ItemTemplate of a ListBox (a simple "delete this row" button), where I had to use relative bindings to reach my ViewModel.

This is not a critical bug in my case since I can just disable CompiledBindings for the time being, but I do enjoy the benefits of the function such as additional IntelliSense and syntax highlighting in the axaml editor when it's enabled, so I would consider it an annoying bug.

IanRawley commented 1 year ago

I was having the same issue, and found that you can instead use ReflectionBinding for those specific cases. e.g. Instead of Command="{Binding ElementName=X, Path=DataContext.DoYCommand}" do Command="{ReflectionBinding ElementName=X, Path=DataContext.DoYCommand}"

This turns off compiled bindings for this binding only.

timunie commented 1 year ago

While I think in ReactiveWindow it should work without, one can also cast in the C# like cast style inside a binding. e.g.

{Binding $parent[Window].((vm:MyViewModel).DataContext).MyCommand }

timahrentlov commented 1 year ago

I see this as well, and have the same use case as SparkyTD

ymzl9229 commented 1 year ago

亲:   您发的邮件已收到【自动回复】!   3Q,祝您生活愉快!                          VIC

renrutsirhc commented 10 months ago

I also have this issue in 0.10.19. Same use case as SparkyTD. @timunie 's suggestion of casting is a work around I'm using for now as it allows for the use of compiled bindings.