SwitchPresenter sample in gallery crashes under Windows App SDK
Describe the bug
When running the gallery with the Wasdk head on the latest main commit, the 'SwitchPresenter' sample will crash the app:
Exception message:
The text associated with this error code could not be found.
Failed to assign to property 'Microsoft.UI.Xaml.Controls.ItemsControl.ItemsSource'. [Line: 21 Position: 19]
Stack trace:
at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
at ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.LoadComponent(IObjectReference _obj, Object component, Uri resourceLocator, ComponentResourceLocation componentResourceLocation)
at Microsoft.UI.Xaml.Application.LoadComponent(Object component, Uri resourceLocator, ComponentResourceLocation componentResourceLocation)
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.InitializeComponent() in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.22621.0/SwitchPresenter/SwitchPresenterValueSample.g.i.cs:line 39
This behavior is not present when running the gallery under UWP.
Steps to reproduce
1. Clone the repo
2. Generate the gallery solution, include the Primitives component and the wasdk multitarget/head.
3. Build and deploy the wasdk gallery
4. Navigate to the 'SwitchPresenter' sample page and observe crash.
Expected behavior
No crash
Screenshots
No response
Code Platform
- [ ] UWP
- [X] WinAppSDK / WinUI 3
- [ ] Web Assembly (WASM)
- [ ] Android
- [ ] iOS
- [ ] MacOS
- [ ] Linux / GTK
Windows Build Number
- [ ] Windows 10 1809 (Build 17763)
- [ ] Windows 10 1903 (Build 18362)
- [ ] Windows 10 1909 (Build 18363)
- [ ] Windows 10 2004 (Build 19041)
- [ ] Windows 10 20H2 (Build 19042)
- [ ] Windows 10 21H1 (Build 19043)
- [ ] Windows 10 21H2 (Build 19044)
- [ ] Windows 10 22H2 (Build 19045)
- [ ] Windows 11 21H2 (Build 22000)
- [ ] Other (specify)
Other Windows Build number
No response
App minimum and target SDK version
- [ ] Windows 10, version 1809 (Build 17763)
- [ ] Windows 10, version 1903 (Build 18362)
- [ ] Windows 10, version 1909 (Build 18363)
- [ ] Windows 10, version 2004 (Build 19041)
- [ ] Windows 10, version 2104 (Build 20348)
- [ ] Windows 11, version 22H2 (Build 22000)
- [ ] Other (specify)
Other SDK version
No response
Visual Studio Version
No response
Visual Studio Build Number
No response
Device form factor
No response
Additional context
No response
Help us help you
Yes, I'd like to be assigned to work on this item.
This seems not to be a SwitchPresenter issue. The ComboBox in the sample code is crashing the app. I'm not sure why but the crash can be avoided by not passing an Array of enum to the ComboBox.
These are not fixes:
EnumValuesExtension.cs
protected override object ProvideValue()
{
// This crashes the app.
// return Enum.GetValues(Type!);
// This does not crash the app.
return Enum.GetNames(Type!);
}
or just:
SwitchPresenterValueSample.xaml.cs
public SwitchPresenterValueSample()
{
this.InitializeComponent();
// This crashes the app.
// this.AnimalPicker.ItemsSource = Enum.GetValues(typeof(Animal));
// This does not crash the app.
Animal[] animals = [Animal.Cat, Animal.Dog, Animal.Bunny, Animal.Llama, Animal.Parrot, Animal.Squirrel];
this.AnimalPicker.ItemsSource = animals;
}
Looks like it's an issue with generated code for types passed into EnumValuesExtension.
This call to Enum.GetValues(Type) seems to be working as expected. Following the return value in the debugger, the type is passed into a generated LookupVtableEntries method in WinRT.
When it reaches the WinRT.PrimitivesExperiment_SamplesGenericHelpers.GlobalVtableLookup, it isn't finding the PrimitivesExperiment.Samples.SwitchPresenter.Animal[] because it hasn't been generated:
This might be related to this other workaround we had to do to get XamlTypeInfo to generate on .NET Native/UWP:
Though it seems like this doesn't work for NativeAot/Wasdk.
@Sergio0694 What are your thoughts?
Interesting, this works fine in my ComputeSharp sample app (UWP, .NET 9).
It seems the CsWinRT generator is missing that Enum.GetValues call. Which I mean, makes sense, since it cannot see the Type argument being passed to it from there. I don't see how that code can work. The extension itself is also marked as not AOT-safe for pretty much the same reason. FYI @manodasanW, do you have any thoughts on this? Seems by design to me.
For now, I'll update the sample to avoid using the Enum call. @Arlodotexe I'll close this when I open that PR for the SwitchPresenter work I'm looking at. Will use @AndrewKeepCoding's just static array approach, I think.
Mind summarizing the main issues related to the intersection of things in a new issue we can use to track elsewhere? Or should we be opening something on CsWinRT? Since we have the attribute here on the Enum call, shouldn't that get flagged somewhere? Or is that a XAML Compiler thing?
Huh, actually with the array approach @AndrewKeepCoding shared for the sample I'm still seeing a different error "Value does not fall within the expected range" (though it works with UWP still no problem...)
> WinRT.Runtime.dll!WinRT.ExceptionHelpers.ThrowExceptionForHR.__Throw|38_0(int hr) Line 134 C#
Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.Controls.IItemsControlMethods.set_ItemsSource(WinRT.IObjectReference _obj, object value) Unknown
Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ItemsControl.ItemsSource.set(object value) Unknown
Maybe I won't be fixing this sample then yet... 🤔
@michael-hawker
Might be irrelevant but the workaround above is from a project before the commits after 10/25.
I want to reconfirm it with the latest commits but now I'm facing a bunch of compile errors and it won't even build. 😅
@AndrewKeepCoding thanks for the info. I based my changes on the latest main, there has been a bump to the WindowsAppSDK version in that time, so I guess there could be a regression here?
Yeah this is strange, I'm able to repro the issue @michael-hawker was running into here.
This is no longer an issue with the XAML compiler generating AoT supporting code for WinRT, something else is happening here.
After a few iterations, I landed on a setup that's strongly typed and ensures no null or empty lists are being passed for binding:
Sample .xaml:
<Page x:Class="PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:PrimitivesExperiment.Samples.SwitchPresenter"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<StackPanel>
<ComboBox x:Name="AnimalPicker"
Header="Pick an Animal"
ItemsSource="{x:Bind Items, Mode=OneWay}" />
<controls:SwitchPresenter Padding="16"
TargetType="local:Animal"
Value="{x:Bind AnimalPicker.SelectedItem, Mode=OneWay}">
<controls:Case Value="Cat">
<TextBlock FontSize="32"
Text="🐈" />
</controls:Case>
<controls:Case Value="Dog">
<TextBlock FontSize="32"
Text="🐕" />
</controls:Case>
<controls:Case Value="Bunny">
<TextBlock FontSize="32"
Text="🐇" />
</controls:Case>
<controls:Case Value="Llama">
<TextBlock FontSize="32"
Text="🦙" />
</controls:Case>
<controls:Case Value="Parrot">
<TextBlock FontSize="32"
Text="🦜" />
</controls:Case>
<controls:Case Value="Squirrel">
<TextBlock FontSize="32"
Text="🐿" />
</controls:Case>
</controls:SwitchPresenter>
</StackPanel>
</Page>
Sample .xaml.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace PrimitivesExperiment.Samples.SwitchPresenter;
[ToolkitSample(id: nameof(SwitchPresenterValueSample), "SwitchPresenter Value", description: $"A sample for showing how to use a {nameof(SwitchPresenter)} for state changes from an enum.")]
public sealed partial class SwitchPresenterValueSample : Page
{
public SwitchPresenterValueSample()
{
this.InitializeComponent();
}
public ObservableCollection<Animal> Items
{
get { return (ObservableCollection<Animal>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(nameof(Items), typeof(ObservableCollection<Animal>), typeof(SwitchPresenterValueSample), new PropertyMetadata(new ObservableCollection<Animal>(Enum.GetValues<Animal>())));
}
public enum Animal
{
Cat,
Dog,
Bunny,
Llama,
Parrot,
Squirrel
}
To which we get this error and stack trace:
Value does not fall within the expected range.
at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
at ABI.Microsoft.UI.Xaml.Controls.IItemsControlMethods.set_ItemsSource(IObjectReference _obj, Object value)
at Microsoft.UI.Xaml.Controls.ItemsControl.set_ItemsSource(Object value)
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.XamlBindingSetters.Set_Microsoft_UI_Xaml_Controls_ItemsControl_ItemsSource(ItemsControl obj, Object value, String targetNullValue) in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 27
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.SwitchPresenterValueSample_obj1_Bindings.Update_Items(ObservableCollection`1 obj, Int32 phase) in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 183
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.SwitchPresenterValueSample_obj1_Bindings.Update_(SwitchPresenterValueSample obj, Int32 phase) in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 170
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.SwitchPresenterValueSample_obj1_Bindings.Update() in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 126
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.SwitchPresenterValueSample_obj1_Bindings.Initialize() in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 120
at PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterValueSample.SwitchPresenterValueSample_obj1_Bindings.Loading(FrameworkElement src, Object data) in /_/components/Primitives/samples/obj/Debug/net8.0-windows10.0.19041.0/SwitchPresenter/SwitchPresenterValueSample.g.cs:line 159
at WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_global__Microsoft_UI_Xaml_FrameworkElement__object_.EventState.<GetEventInvoke>b__1_0(FrameworkElement sender, Object args)
at WinRT.GenericTypeInstantiations.Windows_Foundation_TypedEventHandler_2_Microsoft_UI_Xaml_FrameworkElement__object.Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args)
I'm not sure what to make of this. The problem is in binding the ComboBox, not in the SwitchPresenter or any toolkit helper. @Sergio0694 do you have any ideas? 🤔
That's weird, ObservableObject<T> should be supported out of the box. Can you reference this CsWinRT build explicitly and try again? We've improved the exception message for invalid casts. I suspect for some reason we're missing the implementation of eg. IIterable<IInspectable*> on that object being used here. That would help confirm it.
cc. @manodasanW
@Sergio0694 Can confirm, this fixes the issue.
Updating to CsWinRT 2.2 now provides me with this error, "ICustomProperty support used by XAML Binding for type":
I saw this on the StaggeredPanel too, but talking to Sergio, seems to indicate the use of {Binding} which we are using in a few places still, so that makes sense.