dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
21.98k stars 1.71k forks source link

SwipeView throw exception on windows #8870

Open hujun-open opened 2 years ago

hujun-open commented 2 years ago

Description

it seems following combintions triggers program to throw an exception of "Value does not fall within the expected range.":

I have created a simple example to reproduce: https://github.com/hujun-open/mauiswieviewissue

I only found this issue on windows, android works fine.

following are the visual studio version info:

Microsoft Visual Studio Community 2022
Version 17.3.0 Preview 4.0
VisualStudio.17.Preview/17.3.0-pre.4.0+32714.290
Microsoft .NET Framework
Version 4.8.04084

Installed Version: Community

ASP.NET and Web Tools   17.3.372.35679
ASP.NET and Web Tools

Azure App Service Tools v3.0.0   17.3.372.35679
Azure App Service Tools v3.0.0

C# Tools   4.3.0-3.22329.30+29e657c0582904529bae2a87c227220e03f509cf
C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.

Common Azure Tools   1.10
Provides common services for use by Azure Mobile Services and Microsoft Azure Tools.

Extensibility Message Bus   1.2.6 (master@34d6af2)
Provides common messaging-based MEF services for loosely coupled Visual Studio extension components communication and integration.

Microsoft JVM Debugger   1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines

Mono Debugging for Visual Studio   17.3.20 (3f4cb00)
Support for debugging Mono processes with Visual Studio.

NuGet Package Manager   6.3.0
NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/

Razor (ASP.NET Core)   17.0.0.2232702+e1d654e792aa2fe6646a6935bcca80ff0aff4387
Provides languages services for ASP.NET Core Razor.

TypeScript Tools   17.0.10701.2001
TypeScript Tools for Microsoft Visual Studio

Visual Basic Tools   4.3.0-3.22329.30+29e657c0582904529bae2a87c227220e03f509cf
Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.

Visual F# Tools   17.1.0-beta.22327.2+ddc90b20287a765a9d526da42b3be0dd8e907ec5
Microsoft Visual F# Tools

Visual Studio IntelliCode   2.2
AI-assisted development for Visual Studio.

VisualStudio.DeviceLog   1.0
Information about my package

VisualStudio.Mac   1.0
Mac Extension for Visual Studio

VSPackage Extension   1.0
VSPackage Visual Studio Extension Detailed Info

Xamarin   17.3.0.288 (main@5d42bb2)
Visual Studio extension to enable development for Xamarin.iOS and Xamarin.Android.

Xamarin Designer   17.3.0.204 (remotes/origin/d17-3@f5da0100a)
Visual Studio extension to enable Xamarin Designer tools in Visual Studio.

Xamarin.Android SDK   13.0.0.0 (d17-3/030cd63)
Xamarin.Android Reference Assemblies and MSBuild support.
    Mono: dffa5ab
    Java.Interop: xamarin/java.interop/d17-3@7716ae53
    SQLite: xamarin/sqlite/3.38.5@df4deab
    Xamarin.Android Tools: xamarin/xamarin-android-tools/main@14076a6

Steps to Reproduce

see readme @ https://github.com/hujun-open/mauiswieviewissue

Version with bug

6.0.400

Last version that worked well

Unknown/Other

Affected platforms

Windows

Affected platform versions

windows 10 Enterprise 21H2, OS build 19044.1826

Did you find any workaround?

no

Relevant log output

+       sender  {SwipeIssueDemo.WinUI.App}  object {SwipeIssueDemo.WinUI.App}
-       e   {Microsoft.UI.Xaml.UnhandledExceptionEventArgs} Microsoft.UI.Xaml.UnhandledExceptionEventArgs
-       Exception   {"Value does not fall within the expected range."}  System.Exception {System.ArgumentException}
+       Data    {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
        HResult -2147024809 int
        HelpLink    null    string
+       InnerException  null    System.Exception
        Message "Value does not fall within the expected range."    string
        ParamName   null    string
        Source  null    string
        StackTrace  null    string
        TargetSite  null    System.Reflection.MethodBase
+       Static members      
+       Non-Public members      
        Handled false   bool
        Message "The parameter is incorrect.\r\n"   string
+       Non-Public members

Depends on

VS bug #1870771

jsuarezruiz commented 2 years ago

The native exception from WinUI:

onecoreuap\windows\dwm\dcomp\winrtnested\wrtcompositioninteractionsource.cpp(109)\dcompi.dll!00007FF806E93531: (caller: 00007FF806E9676F) ReturnHr(1) tid(6684) 80070057 The parameter is incorrect.
    Msg:[PointerEventRouter (Interaction) object already has an owner.] 
GuildOfCalamity commented 2 years ago

I'm seeing a similar issue in my simple MAUI application with an ObservableCollection inside a CollectionView. I can add items, but once I remove an item and then try to add another item I get the UnhandledException: [Microsoft.UI.Xaml.UnhandledExceptionEventArgs] Exception = {"Value does not fall within the expected range."} Message = {"The parameter is incorrect."} I also have defensive code in each add/remove method to make sure the collection is not null and the item has content before removing or adding.

GuildOfCalamity commented 2 years ago

[UPDATE] I just discovered the issue was with the SwipeView inside the CollectionView. I hope someone on the MAUI team is working on the bugs with the SwipeView control. I have taken it out for now; not sure how a developer would make this work on a phone?... for now I'm just going to show a delete button.

<CollectionView
     Grid.Row="2"
     Grid.ColumnSpan="2"
     ItemsSource="{Binding Items}"
     SelectionMode="None">
     <CollectionView.ItemTemplate>
         <DataTemplate x:DataType="{x:Type x:String}">
             <!-- [I had to remove the SwipeView because of it's buggyness and the UnhandledException: "Value does not fall within the expected range."]
             <SwipeView>
                 <SwipeView.RightItems>
                     <SwipeItems>
                         <SwipeItem
                             BackgroundColor="Red"
                             Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}"
                             CommandParameter="{Binding .}"
                             Text="Delete" />
                     </SwipeItems>
                 </SwipeView.RightItems>
             -->
             <Grid Padding="0,5" ColumnDefinitions=".9*, .1*">
                 <Frame Grid.Column="0" HasShadow="True">
                     <Label
                         Grid.Row="0"
                         Margin="0"
                         Padding="0"
                         FontSize="24"
                         Text="{Binding .}"
                         VerticalOptions="Start" />
                 </Frame>
                 <Button
                     Grid.Column="1"
                     Margin="3,0,0,0"
                     Padding="24"
                     BackgroundColor="Transparent"
                     Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}"
                     CommandParameter="{Binding .}" />
             </Grid>
             <!--
             </SwipeView>
             -->
         </DataTemplate>
     </CollectionView.ItemTemplate>
 </CollectionView>

The binding source "Items" is an ObservableCollection{string} inside the ViewModel. You can uncomment the SwipeView to see the exception happen.

codemonkey85 commented 2 years ago

Are there any recommended workarounds for this? There's no conditional compilation in XAML, so the only other thing I can think of is having a separate page / view for Windows vs other platforms.

prom3theu5 commented 2 years ago

Thats what I have had to do for now Separate shells for windows vs everything else.

agustinbcu01 commented 1 year ago

The issue is possible to reproduce using a Delete Button

///ViewModels.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows.Input;

namespace Inventory.ViewModels
{

    public partial class MainViewModel : ObservableObject
    {
        public MainViewModel() {
            items = new ObservableCollection<string>();
            text = string.Empty;
        }
        [ObservableProperty]
        ObservableCollection<string> items;

        [ObservableProperty]
        string text;

        [RelayCommand]
        void Add() {
            if (string.IsNullOrEmpty(text?.Trim()))
            {
                return;
            }
            Items.Add(text);
            Text = string.Empty;
        }

        [RelayCommand]
        void Delete(string value)
        {
            if (string.IsNullOrEmpty(value?.Trim()))
            {
                return;
            }
            if (Items.Any(s => s.Equals(value)))
            {
                Items.Remove(value);
            }
        }

    }

}
//MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Inventory.MainPage"
             xmlns:viewmodel="clr-namespace:Inventory.ViewModels"
             x:DataType="viewmodel:MainViewModel">
    <Grid RowDefinitions="100, auto, *" ColumnDefinitions=".75*, .25*" Padding="10" RowSpacing="10" ColumnSpacing="10">
        <Image Grid.ColumnSpan="2" Source="dotnet_bot.png" BackgroundColor="Orange"></Image>
        <Entry Placeholder="Enter the task Nanme" Grid.Row="1" Text="{Binding Text}"></Entry>
        <Button Text="Add" Grid.Row="1" Grid.Column="1" Command="{Binding AddCommand}"></Button>
        <CollectionView Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding Items}">

            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="{x:Type x:String}">
                    <SwipeView>
                        <SwipeView.RightItems>
                            <SwipeItems>
                                <SwipeItem Text="Delete" BackgroundColor="Red"></SwipeItem>
                            </SwipeItems>
                        </SwipeView.RightItems>
                        <Grid  RowDefinitions="auto" ColumnDefinitions="*, auto">
                        <Frame BackgroundColor="Navy">
                            <Label Text="{Binding .}" FontSize="24" Grid.Column="1"></Label>

                            </Frame>
                            <Button Text="Delete" BackgroundColor="Red" Grid.Column="2" 
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}" 
                                    CommandParameter="{Binding .}"/>
                        </Grid>
                    </SwipeView>

                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>        
    </Grid>

</ContentPage>
//MainPage 
public partial class MainPage : ContentPage
{
    public MainPage( MainViewModel vm)
    {
        InitializeComponent();
        BindingContext = vm;
    }

}
//MauiProgram.cs

using Inventory.ViewModels;

namespace Inventory;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });
        builder.Services.AddSingleton<MainPage>();
        builder.Services.AddSingleton<MainViewModel>();

        return builder.Build();
    }
}
jbe2277 commented 1 year ago

UPDATE 2: I still have the issue with VS 2022 17.4.5.

UPDATE: My issue seems to be fixed with VS 2022 17.3.6.


I get the same exception now on Windows.

It worked with VS 2022 17.3.2. After upgrading to VS 2022 17.3.5 this issue occurred in my App as well on Windows. But it works on Android 12.

Reproduce:

  1. Clone: https://github.com/jbe2277/waf/tree/904ae96984603d982eaa85bd6a411bc89717bbac
  2. Open src\NewsReader\NewsReader.sln

Try out:

ghost commented 1 year ago

Hi @hujun-open. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

mattleibow commented 1 year ago

Is this fixed? Can we close?

david-maw commented 1 year ago

@mattleibow I'm curious, how @hujun-open would be expected to know if it is fixed, is the merged PR available somewhere? I submitted issue #9423 which was closed as a duplicate of this issue, as well as #9233 which is probably fixed by the same PR, so I'd like a fix too but I'm not sure how to go about discovering if this is it until the PR changes are released. Did you have some mechanism in mind?

codemonkey85 commented 1 year ago

I ran into this issue about two days ago, right after upgrading to .NET 7 release. So it's definitely unsolved.

hujun-open commented 1 year ago

I moved away from MAUI for now, no longer have the setup, but there is repo to reproduce the issue, anyone could use that to see if the issue is fixed

mdbill commented 1 year ago

I just wasted an hour or so figuring out this crash was the swipeview, so still a problem on 17.4.1

jaldinger commented 1 year ago

Same problem here. Quite annoying to hit so many bugs actually. Is this being looked at least? My workaround for the moment is to instantiate a new ObservableCollection and (since that breaks change notifications) triggering the change notification manually, like so:

#if WINDOWS
                    // only necessary because of: https://github.com/dotnet/maui/issues/8870
                    DocumentLines = new();
#else
                    DocumentLines.Clear();
#endif
                    foreach (var l in hiddenDocumentLines.OrderBy(l => l.VisualOrder))
                        DocumentLines.Add(l);
                    hiddenDocumentLines.Clear();
#if WINDOWS
                    // only necessary because of: https://github.com/dotnet/maui/issues/8870
                    OnPropertyChanged(nameof(DocumentLines));
#endif

Does anyone have a better workaround than this?

jimbarry0406 commented 1 year ago

This worked for me, but I had to use if (DeviceInfo.Current.Platform == DevicePlatform.WinUI)

ghost commented 1 year ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

codemonkey85 commented 1 year ago

I don't understand how this is being put off again. I guess no one is using MAUI for simultaneous desktop and mobile deployments? Maybe no one is using SwipeView? 🤔

david-maw commented 1 year ago

Since this was used to close at least #9540, #9423 and #8870 there seems to be a fair amount of interest. PR #11229 seems like it corrects this but says "The PR require some changes in the public API. Will maintain the changes up to date but need to wait to .NET 8."

Fair enough, but if breaking changes are the problem at least put something in the community toolkit temporarily because waiting for .NET 8 to fix a bug in .NET 6 seems excessive.

agustinbcu01 commented 1 year ago

Long live to xamarin form them.

On Fri, Feb 3, 2023 at 2:07 PM David Maw @.***> wrote:

Since this was used to close at least #9540 https://github.com/dotnet/maui/issues/9540, #9423 https://github.com/dotnet/maui/issues/9423 and #8870 https://github.com/dotnet/maui/issues/8870 there seems to be a fair amount of interest. PR #11229 https://github.com/dotnet/maui/pull/11229 seems like it corrects this but says "The PR require some changes in the public API. Will maintain the changes up to date but need to wait to .NET 8."

Fair enough, but if breaking changes are the problem at least put something in the community toolkit temporarily because waiting for .NET 8 to fix a bug in .NET 6 seems excessive.

— Reply to this email directly, view it on GitHub https://github.com/dotnet/maui/issues/8870#issuecomment-1416348243, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFZB5A7QIOGUNCHBZJPHAWTWVVQPZANCNFSM54FRA3VQ . You are receiving this because you commented.Message ID: @.***>

GuildOfCalamity commented 1 year ago

Seems like there's no love for desktop apps in the new tech. The lack of a mouse-over event in MAUI was proof of this. I'm just wondering how this was missed or not tested properly? Usually you test features in a desktop/native build before porting to mobile during the dev cycle.

jbe2277 commented 1 year ago

This issue still exists with Visual Studio 17.4.5 and MAUI 7.0.59.

How to reproduce:

  1. Create a new MAUI project with .NET 7

  2. Replace content of MainPage.xaml with

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.MainPage">
    
    <Grid RowDefinitions="Auto,*" Margin="10" RowSpacing="10">
        <Button Text="Insert items" Clicked="InsertItemsClicked" HorizontalOptions="Start"/>
    
        <CollectionView Grid.Row="1" ItemsSource="{Binding Items}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <SwipeView>
                        <Grid>
                            <Border Stroke="Blue" StrokeThickness="2">
                                <Label Text="{Binding}" Padding="5"/>
                            </Border>
                        </Grid>
                    </SwipeView>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
    </ContentPage>
  3. Replace content of MainPage.xaml.cs with

using System.Collections.ObjectModel;

namespace MauiApp1;

public partial class MainPage : ContentPage
{
    int counter = 1;

    public MainPage()
    {
        InitializeComponent();
        Items = new();
        BindingContext = this;
    }

    public ObservableCollection<string> Items { get; }

    private void InsertItemsClicked(object sender, EventArgs e)
    {
        const int numberOfEntries = 5;
        for (int i = 0; i < numberOfEntries; i++) { Items.Insert(0, $"Item {counter}"); counter++; }
    }
}
  1. Press Insert items button until the items do not fit in the view anymore and would need a scrollbar -> Exception occurs. See screen cast:

Maui-Win-Issue

Note: Remove the SwipeView from the DataTemplate. Then this error does not occur.

AndreKraemer commented 1 year ago

I just ran into the same problem with Visual Studio 17.4.5 and MAUI 7.0.59 in a demo project I'm developing for a conference where I'll be talking about .NET MAUI and wanted to share my workaround.

I was able to get around the issue by migrating my XAML code to C# markup using the helper methods from the [.NET MAUI Community Toolkit] (https://learn.microsoft.com/de-de/dotnet/communitytoolkit/maui/markup/markup). Within the C# markup file, I was able to use conditional compilation to exclude the SwipeView on Windows.

See:

https://github.com/AndreKraemer/basta-2023-se-maui-demo/blob/fb33963810b7788795fcf6445d4d5f4cf256e26b/ConferenceMauiDemo/Views/SessionsPageMarkup.cs#L152

Since the SwipeView doesn't work on Windows when using the application with a mouse, it wasn't a big deal to exclude it from my view. To give Windows users the same functionality that users of other platforms have with the SwipeView, I added a context menu (right click). See: https://github.com/AndreKraemer/basta-2023-se-maui-demo/blob/fb33963810b7788795fcf6445d4d5f4cf256e26b/ConferenceMauiDemo/Views/SessionsPageMarkup.cs#L238

codemonkey85 commented 1 year ago

Within the C# markup file, I was able to use conditional compilation to exclude the SwipeView on Windows.

Was literally just thinking of this today. Thanks for testing it out and letting us know!

jevonsflash commented 1 year ago

I ran into the same problem, but not with swipeview i create a collectionview and binding with Multiple selection mode

  <CollectionView x:Name="collectionView"
                        ItemsSource="{Binding NoteSegments}"
                        SelectedItems="{Binding SelectedNoteSegments}"
                        SelectedItem="{Binding SelectedNoteSegment}"
                        SelectionMode="Multiple"
                        ItemTemplate="{StaticResource NoteSegmentDataTemplateSelector}" />

After the page is loaded, I delete several selected items ,then

private void RemoveSelectedSegmentAction(object obj)
        {
            foreach (var noteSegments in SelectedNoteSegments.ToList())
            {
                NoteSegments.Remove((INoteSegmentService)noteSegments);
            }
        }

Then add some items

private async void EditNotePageViewModel_OnFinishedChooise(object sender, NoteSegment noteSegment)
        {
            noteSegment.NoteId = this.NoteId;
            noteSegment.NoteSegmentPayloads = new List<NoteSegmentPayload>();

            var newModel = noteSegmentServiceFactory.GetNoteSegmentService(noteSegment);
            if (newModel != null)
            {
                newModel.Create.Execute(null);
                this.NoteSegments.Add(newModel);
            }
           (sender as NoteSegmentStoreListPageViewModel).OnFinishedChooise -= EditNotePageViewModel_OnFinishedChooise;

            await navigationService.HidePopupAsync(noteSegmentStoreListPage);

        }

        private async void CreateSegmentFromStoreAction(object obj)
        {
            using (var objWrapper = iocResolver.ResolveAsDisposable<NoteSegmentStoreListPage>())
            {
                noteSegmentStoreListPage = objWrapper.Object;
                (noteSegmentStoreListPage.BindingContext as NoteSegmentStoreListPageViewModel).OnFinishedChooise += EditNotePageViewModel_OnFinishedChooise;
                await navigationService.ShowPopupAsync(noteSegmentStoreListPage);
            }
        }

And here's the problem

KeithBoynton commented 1 year ago

I'm using .net8 preview 5 and the problem still exists for me, my project is using windows and mac desktop, and iOS and Android. Working fine in all but Windows where it crashes because I have a swipe view inside a collection view.

Is this being worked on for .net8 ?

david-maw commented 1 year ago

Absent a response from Microsoft I can only guess, but it looks like it's being worked on. I noticed a while back that PR #11229 by @jsuarezruiz seems like it corrects this but says "The PR require some changes in the public API. Will maintain the changes up to date but need to wait to .NET 8." That was in November of 2022, however it looks like a new decision has been reached and PR #11229 was closed at the beginning of July 2023 with "We are going to close this PR focusing on fix crashes or any problem that prevents the use of SwipeView in Windows while we continue to promote the implementations and accessibility on the desktop (keyboard support etc)."

A SwipeView without mouse support is not a very practical desktop UI element but as long as its presence does not crash the app (as it currently does in this example) it shouldn't be too hard to code around that and offer an alternative desktop interface. Not hard, but tedious.

KeithBoynton commented 1 year ago

I agree, I have a swipe view for use on mobile devices and am using context menus for desktop. At a minimum we need the presence of a swipe view in a collection view to at least not crash!

jsuarezruiz commented 1 year ago

Could be related with: https://github.com/microsoft/microsoft-ui-xaml/issues/2527

Can reproduce the issue even with the changes:

Rabidgoalie commented 9 months ago

I don't know if this will help the majority of projects encountering this problem, but I have found an All-XAML workaround to this problem. This should work with any control that uses a DataTemplate, but, as I am fairly new to .NET MAUI / Xamarin, I don't know how far this workaround will extend to everyone's issues. I do hope that this helps someone, though.

I have broken this long post into two parts, the background for my issue (in case some of you are facing something similar, or where this information may be useful to someone). And, a second section called 'The Good Stuff'. If you just want to see the solution that I used, skip the first section (it may be a waste of your time to read it anyway). 😃

Background for My Issue

I was encountering this issue when I deleted an item in my ViewModel's ObservableCollection (which is bound to a CollectionView in my main view's XAML) and then added a new item during the same session. On Windows it would crash the application, but it worked fine on Android (I didn't test it on iOS or MacOS). This crash occurred during the Shell.Current.GoToAsync() method, which I am using to pass an object back to the main view's VM, using the ApplyQueryAttributes() method to capture the passed in data. All of the application's data is being stored to a SQLite database, which was important to me in figuring out that it wasn't just a null value of some sort causing a crash.

Execution would follow as one would expect, by entering the command I have defined to create the data object (when the 'Create' button is pressed) and call Shell.Current.GoToAsync() to go back to the main view, passing the data object. The data object is valid; my immediate thought was that somehow it may be null when I encountered the error, but it contained all the necessary data. The execution then jumped from GoToAsync() to the VM's ApplyQueryAttributes() method where it correctly processed the data object by adding it to my ObservableCollection and storing it in the database. Good, no issues. This is where things seem strange to me, because then execution jumped back to the GoToAsync() method again, even though I have await-ed the GoToAsync() method. And this point in the execution is where the application would crash. To be clear, the data object is added to the ObservableCollection and to the database successfully. After the crash, I can start the app up again and the added object (which caused the crash) will be in the CollectionView...so, it was added to the database successfully, and adding the object to the ObservbleCollection is not where the app crashed. It was only when execution jumped back to GoToAsync() that things went sideways.

Again, I am new to .NET MAUI and to be completely honest, I am fairly new to C# (although I have been programming in C/C++ for many years). I was under the (clearly misguided) belief that the application would wait for the GoToAsync() method's execution to complete before jumping to the ApplyQueryAttributes() method in my ViewModel since the ApplyQueryAttributes() method needs the information being passed through GoToAsync() and that the main thread should have to halt for that information to be passed. To me, this seems strange that it would begin executing the GoToAsync() method, jump to the ApplyQueryAttributes() method, and then jump back to the GoToAsync() method. Meanwhile, all of the data being passed through the GoToAsync() method is valid, it is received correctly in my VM and is fully processed, and then the execution of GoToAsync() completes. 🤷

I am only including all of this information for completeness. I don't know how much of this actually applies to the underlying issue with the SwipeView control, of if it matters at all.

Now, on to the solution that worked for me.

The Good Stuff

I am an inherently lazy person, and since I intent to make all the source code to my app open source, I am just going to copy-paste what I have to fix this problem.

TopicCard View XAML

<?xml version="1.0" encoding="utf-8" ?>
<Frame xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:local="clr-namespace:CornellPad"
       xmlns:viewmodel="clr-namespace:CornellPad.ViewModels"
       xmlns:entry="clr-namespace:CornellPad.Models"
       x:DataType="entry:TopicModel"      
       x:Class="CornellPad.Views.Cards.TopicCard"
       CornerRadius="20"
       Margin="10,10, 15, 0"
       BorderColor="{AppThemeBinding Light={StaticResource Blue200Accent}, Dark={StaticResource Gray400}}">

    <Frame.GestureRecognizers>
        <TapGestureRecognizer
        Command="{Binding GoToTopicViewCommand, Source={RelativeSource AncestorType={x:Type viewmodel:LibraryViewModel}}}"
        CommandParameter="{Binding .}"/>
    </Frame.GestureRecognizers>

    <Grid ColumnDefinitions="120, Auto"
        RowDefinitions="130"
        Margin="5">

        <Image Grid.Column="0"
            Source="{Binding Icon}"
            HeightRequest="100"
            WidthRequest="100"/>
        <VerticalStackLayout Grid.Column="1"
                            Padding="5">
            <Border
            Padding="10, 6"
            Stroke="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray500}}"
            StrokeThickness="3"
            StrokeShape="RoundRectangle 0,10,10,0"
            VerticalOptions="CenterAndExpand"
            HorizontalOptions="Start"
            BackgroundColor="{AppThemeBinding Light={StaticResource Blue300Accent}, Dark={StaticResource Gray900}}">

                <Label
                Text="{Binding TopicName}"
                Padding="5"
                FontSize="{OnPlatform Default=Subtitle, Android=Small}"/>
            </Border>

            <Label
                Text="{Binding NumberOfNotes, StringFormat='{0} Notes in Topic'}"
                Padding="10"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="Start"/>
        </VerticalStackLayout>
    </Grid>
</Frame>

There is nothing special about this. Literally the only changed that I had to make in the code-behind was changing the parent class to Frame instead of ContentView. I just copied the XAML from my main view and pasted it in here, with a few alterations to wire everything up, of course.

LibraryView (my main view) XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:local="clr-namespace:CornellPad"
             xmlns:viewmodel="clr-namespace:CornellPad.ViewModels"
             xmlns:entry="clr-namespace:CornellPad.Models"
             xmlns:cards="clr-namespace:CornellPad.Views.Cards"
             x:DataType="viewmodel:LibraryViewModel"
             x:Class="CornellPad.Views.LibraryView"
             x:Name="LibraryViewPage"
             Title="{Binding Title}">
    <ContentPage.Resources>
        <ResourceDictionary>

            <DataTemplate
                x:Key="WithDesktop"
                x:DataType="entry:TopicModel">
                <cards:TopicCard>
                    <FlyoutBase.ContextFlyout>
                        <MenuFlyout>
                            <MenuFlyoutItem
                                Text="Delete"
                                CommandParameter="{Binding .}"
                                Command="{Binding Source={x:Reference LibraryViewPage}, Path=BindingContext.DeleteTopicCommand}"/>
                        </MenuFlyout>
                    </FlyoutBase.ContextFlyout>
                </cards:TopicCard>
            </DataTemplate>

            <DataTemplate
                x:Key="WithoutDesktop"
                x:DataType="entry:TopicModel">
                <SwipeView
                    IsVisible="{OnPlatform WinUI=False, MacCatalyst=True, Android=True, iOS=True}">
                    <SwipeView.RightItems>
                        <SwipeItems>
                            <SwipeItemView
                                CommandParameter="{Binding .}"
                                Command="{Binding DeleteTopicCommand, Source={RelativeSource AncestorType={x:Type viewmodel:LibraryViewModel}}}">
                                <Frame
                                    BackgroundColor="IndianRed"
                                    WidthRequest="100"
                                    HeightRequest="100"
                                    VerticalOptions="Center"
                                    CornerRadius="50"
                                    Padding="0">
                                    <Label
                                        Text="Delete"
                                        VerticalOptions="Center"
                                        HorizontalOptions="Center"/>
                                </Frame>
                            </SwipeItemView>
                        </SwipeItems>
                    </SwipeView.RightItems>
                    <cards:TopicCard/>
                </SwipeView>                
            </DataTemplate>

        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.ToolbarItems>
        <ToolbarItem 
            Text="Add"
            IconImageSource="{AppThemeBinding Light=add_light.png, Dark=add_dark.png}"
            Command="{Binding GoToCreateTopicViewCommand}"/>
    </ContentPage.ToolbarItems>

    <CollectionView
        ItemsSource="{Binding TopicEntries}"
        ItemTemplate="{OnPlatform WinUI={StaticResource WithDesktop}, MacCatalyst={StaticResource WithDesktop}, Default={StaticResource WithoutDesktop}}"
        SelectionMode="None">
    </CollectionView>
</ContentPage>

By defining my DataTemplates as a resource with a key, I can then easily access them according to the platform I am compiling for. I didn't have to change any code-behind anywhere (with the one small exception mentioned above), nor did I have to change any of the code in my ViewModel. By breaking out the XAML markup into it's own separate 'card', I still have one source to define the UI/UX elements for that card. I was very concerned about having duplicate XAML markup, but this seems to circumvent that issue. [Looks around nervously]

As an aside, my xmlns:entry should be named xmns:models, which I plan to change. But as I said, I am inherently lazy and I haven't gotten around to that yet. So, you get the XAML markup with warts and all. 😛

Despite the issues that I have encountered, I have to say that I ❤️ .NET MAUI, even if we have to live with some issues. I hope this helps someone and that you all have a great day.

indyjason79 commented 6 months ago

Thank you @Rabidgoalie for the awesome work-around! I didn't want to turn my template into a view, so I used a ControlTemplate instead and it works great! I was tired of having to maintain 2 templates (1 for swipe and 1 for desktop).

<ControlTemplate x:Key="ItemTemplate">
    <Grid ColumnDefinitions="8,84,*,*"
          RowDefinitions="auto,auto"
          ColumnSpacing="8"
          BackgroundColor="White">
        <Image Grid.Column="1"
               Grid.Row="0"
               Grid.RowSpan="2"
               VerticalOptions="Center"
               HorizontalOptions="Center"
               HeightRequest="36">
            <Image.Source>
                <FontImageSource Glyph="{x:Static icons:Icon.Tag}"
                                 Color="Blue"
                                 Size="36"
                                 FontFamily="{x:Static icons:Font.IconSet}" />
            </Image.Source>
        </Image>

        <VerticalStackLayout Grid.Column="2"
                             Grid.Row="0"
                             Margin="0,8"
                             Grid.RowSpan="2">
            <Label FontSize="14"
                   Text="{Binding Name}"
                   LineBreakMode="TailTruncation"
                   VerticalOptions="Center" />
            <Label FontSize="14"
                   Text="{Binding Info}"
                   LineBreakMode="TailTruncation"
                   Margin="0,5,0,5"
                   TextColor="Gray"
                   VerticalOptions="Center" />
        </VerticalStackLayout>

        <Label Grid.Column="3"
               Grid.Row="0"
               Grid.RowSpan="2"
               Margin="0,0,12,0"
               VerticalOptions="Center"
               HorizontalOptions="End"
               FontSize="16"
               Text="{Binding Price, Converter={StaticResource CurrencyConverter}}"
               TextColor="Purple" />

        <BoxView Grid.Column="0"
                 Grid.ColumnSpan="4"
                 Grid.Row="1"
                 HeightRequest="1"
                 VerticalOptions="End"
                 Color="LightGray" />
    </Grid>
</ControlTemplate>

<DataTemplate x:Key="MobileItemTemplate"
              x:DataType="data:ItemModel">
    <SwipeView>
        <SwipeView.RightItems>
            <SwipeItems>
                <SwipeItem Text="Delete"
                           IsDestructive="True"
                           BackgroundColor="Red"
                           Command="{Binding RemoveCommand}"
                           CommandParameter="{Binding .}"
                           IsVisible="{Binding RemoveCommand, Converter={StaticResource NotNullConverter}}" />
            </SwipeItems>
        </SwipeView.RightItems>

        <ContentView ControlTemplate="{StaticResource ItemTemplate}" />
    </SwipeView>
</DataTemplate>

<DataTemplate x:Key="DesktopItemTemplate"
              x:DataType="data:ItemModel">
    <ContentView ControlTemplate="{StaticResource ItemTemplate}">
        <FlyoutBase.ContextFlyout>
            <MenuFlyout>
                <MenuFlyoutItem Text="Remove"
                                Command="{Binding RemoveCommand}"
                                CommandParameter="{Binding .}"
                                IsEnabled="{Binding RemoveCommand, Converter={StaticResource NotNullConverter}}">
                    <MenuFlyoutItem.IconImageSource>
                        <FontImageSource Glyph="{x:Static icons:Icon.Remove}"
                                         FontFamily="{x:Static icons:Font.IconSet}" />
                    </MenuFlyoutItem.IconImageSource>
                </MenuFlyoutItem>
            </MenuFlyout>
        </FlyoutBase.ContextFlyout>
    </ContentView>
</DataTemplate>
bkaankose commented 1 month ago

This is a native crash on WinUI3 and has nothing tot much to do with MAUI. I am working on a plain UWP app without MAUI and it's crashing for me as well.

It's reproducible once you have SwipeControl items in a ListView data template and scroll quickly up and down. In UWP it is fired as stowed exception. My investigation with WinDbg showed that this line of code throws it

https://github.com/microsoft/microsoft-ui-xaml/blob/66a7b0ae71c19f89c6a7d86a1986794ad1a1bf09/src/controls/dev/SwipeControl/SwipeControl.cpp#L786

My guess is that once you share LeftItems and RightItems as a resource, interaction tracker code loses child-parent relation with the owner due to UI virtualization.