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.13k stars 2.26k forks source link

InvalidCastException during debugging in Jetbrains Rider #17647

Closed grzegorz-wolszczak closed 19 hours ago

grzegorz-wolszczak commented 2 days ago

Describe the bug

Not sure if it is related to Avalonia or Jetbrains Raider or somewhere 'in between'

I have simple applicaton whith one listbox and one custom control. When you click on listbox element , the same content should be displayed in custom control

Code

my view models:

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;

namespace CastExceptionAvaloniaIssue;

public partial class MainViewModel : ObservableObject
{
   [ObservableProperty]
   private ObservableCollection<UserDetailsViewModel> _users = new();

   public MainViewModel()
   {
      Users = new ObservableCollection<UserDetailsViewModel>()
      {
         new UserDetailsViewModel()
         {
            FirstName = "John",
            LastName = "Connor"
         },
         new UserDetailsViewModel()
         {
            FirstName = "Sarah",
            LastName = "Connor"
         }
      };
   }
}
public partial class UserDetailsViewModel: ObservableObject
{
   [ObservableProperty]
   private string _firstName = "";

   [ObservableProperty]
   private string _lastName = "";

}

main window code

public partial class MainWindow : Window
{
   public MainWindow()
   {
      InitializeComponent();
      DataContext = new MainViewModel();
   }

   private void UserList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
   {
      var details = userList.SelectedItem as UserDetailsViewModel;
      if (details is null)
      {
         return;
      }

      CustomControl.DataContext = details;

   }
}

main window axaml

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignWidth="300"
        d:DesignHeight="250"
        Width="300"
        Height="250"
        x:Class="CastExceptionAvaloniaIssue.MainWindow"
        xmlns:vm="clr-namespace:CastExceptionAvaloniaIssue"
        x:DataType="vm:MainViewModel"
        Title="CastExceptionAvaloniaIssue">
    <StackPanel HorizontalAlignment="Left"
                VerticalAlignment="Center">
        <ListBox Name="userList" ItemsSource="{Binding Users}" SelectionChanged="UserList_OnSelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid RowDefinitions="Auto,Auto">
                        <TextBlock Grid.Row="0" Text="{Binding FirstName}" TextWrapping="Wrap" />
                        <TextBlock Grid.Row="1" Text="{Binding LastName}" TextWrapping="Wrap" />
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <vm:UserDetailsControl Name="CustomControl" Parent="{Binding #userList.SelectedItem}" />
    </StackPanel>
</Window>

user control code

public partial class UserDetailsControl : UserControl
{
   public UserDetailsControl()
   {
      InitializeComponent();
   }
}

user control axaml

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200"
             xmlns:app="clr-namespace:CastExceptionAvaloniaIssue"
             x:DataType="app:UserDetailsViewModel"
             x:Class="CastExceptionAvaloniaIssue.UserDetailsControl">
    <StackPanel Background="Black" Orientation="Vertical">
        <Grid RowDefinitions="Auto,Auto">
            <Label Grid.Row="0" Content="{Binding FirstName}" Foreground="Azure"/>
            <Label Grid.Row="1" Content="{Binding LastName}" Foreground="Azure"/>
        </Grid>
    </StackPanel>
</UserControl>

When I try to run this application in debug mode in Jebrains rider

System.InvalidCastException: Unable to cast object of type 'CastExceptionAvaloniaIssue.MainViewModel' to type 'CastExceptionAvaloniaIssue.UserDetailsViewModel'.
   at CompiledAvaloniaXaml.XamlIlHelpers.CastExceptionAvaloniaIssue.UserDetailsViewModel,CastExceptionAvaloniaIssue.FirstName!Getter(Object)
   at Avalonia.Data.Core.ClrPropertyInfo.Get(Object target)
   at Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.InpcPropertyAccessor.get_Value()
   at Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.InpcPropertyAccessor.SendCurrentValue()

I've provided example code to reproduce.

Verified that: this issue is not present when debugging in VisualStudio

To Reproduce

example_issue_code.zip

Expected behavior

No exceptions should be visible

Avalonia version

11.2.2

OS

Windows

Additional context

My setup: I'm using CommunityToolkit.Mvvm 8.3.2 Jetbrains Rider 2024.3 (Build #RD-243.21565.191) application target framework is <TargetFramework>net8.0</TargetFramework> my dotnet --info output

.NET SDK:
 Version:           9.0.100
 Commit:            59db016f11
 Workload version:  9.0.100-manifests.4a280210
 MSBuild version:   17.12.7+5b8665660

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19045
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\9.0.100\

.NET workloads installed:
 [aspire]
   Installation Source: VS 17.12.35521.163
   Manifest Version:    8.2.2/8.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.2\WorkloadManifest.json
   Install Type:              Msi

Configured to use loose manifests when installing new manifests.

Host:
  Version:      9.0.0
  Architecture: x64
  Commit:       9d5a6a9aa4

.NET SDKs installed:
  7.0.410 [C:\Program Files\dotnet\sdk]
  8.0.307 [C:\Program Files\dotnet\sdk]
  9.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found
grzegorz-wolszczak commented 2 days ago

for reference I've also created this in Jetbrains Rider bug tracker: https://youtrack.jetbrains.com/issue/RIDER-120607/InvalidCastException-in-when-debugging-Avalonia-application-.NET-8.0

halfninja commented 2 days ago

I think you need to set the DataType on your DataTemplate to {x:Type vm:UserDetailsControl} so that the compiled bindings know what the properties in the template relate to.

grzegorz-wolszczak commented 2 days ago

@halfninja Would this explain why Rider in debug mode throws exception ? Why then Visual Studio in debug mode does not throw , and Rider in release mode does not throw... ?

halfninja commented 2 days ago

I'm not sure, that is unusual. One thing I did note when debugging in Rider is that if you click continue past these exception breaks, the app displays and appears to work okay.

I tried running your project and setting the data type didn't help (I think it might already infer it from the type of ItemsSource)- but I hadn't seen the use of the Parent property like this before. It seems happier instead binding the DataContext like this

<vm:UserDetailsControl DataContext="{Binding #userList.SelectedItem, Mode=OneWay}" />

It looks like there's no need for UserList_OnSelectionChanged, as this binding keeps it up to date on its own.

rabbitism commented 2 days ago

This is happening when a collection element attached to visual tree and inherit parent DataContext by default. Not a big problem after children DataContext is assigned.

timunie commented 22 hours ago

What happens if you set DataContext before I calling Initialize? I believe that this may be the related since the compoler don't know about the data context during construction of the UI

Edit:

- <vm:UserDetailsControl Name="CustomControl" Parent="{Binding #userList.SelectedItem}" />
+ <vm:UserDetailsControl Name="CustomControl" DataContext="{Binding #userList.((vm:DetailsViewModel)SelectedItem)}" />
grzegorz-wolszczak commented 20 hours ago

@timunie

  1. Setting DataContext before calling Initialize did not help, exception still happens but..
  2. Setting DataContext explicitly as you've suggested with
    <!-- <vm:UserDetailsControl Name="CustomControl" Parent="{Binding #userList.SelectedItem}" /> -->
        <vm:UserDetailsControl Name="CustomControl" DataContext="{Binding #userList.((vm:UserDetailsViewModel)SelectedItem)}" />

    has helped (!!) and the issue does not appear.

Knowing what we know now, can we answer the question: where is the problem ? Is in in Avalonia code or in Rider ? If in Rider than I'd like to close this issue here. The strange thing is that this issue does not happen e.g. in VisualStudio during debugging. (WHY ?! )

timunie commented 19 hours ago

Thinking further I think the issue is a missuse of Parent and it's not supposed to work at all. So imo both issues can be closed. Even if it doesn't crash, you shouldn't change parent of a control. Better to bind to DataContext. Bonus, you can remove the event handler for SelectionChanged.