I am new to .net Maui (and Xamarin forms), but I am trying to get an example of MVVM working as obtained from a video on https://www.youtube.com/watch?v=oNIvnCNzAqU
My problem is that when I click on the bin icon the app crashes totally.
Here is the relevant code TodoListViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MauiApp1
{
public class TodoListViewModel : BindableObject
{
public ObservableCollection<TodoItem> TodoItems { get; set; }
public TodoListViewModel()
{
TodoItems = new ObservableCollection<TodoItem>
{
//test
//new TodoItem("todo 1", false),
//new TodoItem("todo 2", true),
//new TodoItem("todo 3", false)
};
}
string newTodoText = string.Empty;
public string NewTodoText
{
get { return newTodoText; }
set
{
if (value == newTodoText) return;
newTodoText = value;
OnPropertyChanged();
}
}
public ICommand AddTodoCommand => new Command( AddTodoItem );
public void AddTodoItem()
{
//Console.WriteLine(NewTodoText);
TodoItems.Add(new TodoItem(NewTodoText, false));
//clear entry and refocus
NewTodoText = string.Empty;
//Todo.Focus();
////dismiss keyboard
//Todo.IsEnabled = false;
//Todo.IsEnabled = true;
}
public ICommand RemoveTodoCommand => new Command( RemoveTodoItem );
void RemoveTodoItem(Object o)
{
TodoItem todoItemBeingRemoved = o as TodoItem;
TodoItems.Remove(todoItemBeingRemoved);
}
}
}
and the xaml 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"
xmlns:local="clr-namespace:MauiApp1"
x:Class="MauiApp1.MainPage"
>
<ContentPage.BindingContext>
<local:TodoListViewModel />
</ContentPage.BindingContext>
<Grid RowDefinitions="Auto, Auto, Auto, *">
<Entry
Grid.Row="0"
FontSize="24"
Placeholder="Enter a ToDo"
Text="{Binding NewTodoText}"
ReturnCommand="{Binding AddTodoCommand}"
/>
<Button
Grid.Row="1"
Margin="10"
Padding="10"
FontSize="24"
Text="Add a Todo"
Command="{Binding AddTodoCommand}"
/>
<ListView
Grid.Row="2"
x:Name="TodoList"
ItemsSource="{Binding TodoItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid
Margin="10, 0, 0, 0"
ColumnDefinitions="auto, auto, *">
<CheckBox
Grid.Column="0"
IsChecked="{Binding Complete}" />
<Label
Grid.Column="1"
Text="{Binding TodoText}"
FontSize="24" />
<ImageButton
Grid.Column="2"
HorizontalOptions="End"
Source="delete.png"
Scale="0.7"
BackgroundColor="Transparent"
Command="{Binding Path=BindingContext.RemoveTodoCommand, Source={x:Reference TodoList}}"
CommandParameter="{Binding .}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
I've tried debugging this and the ObservableCollections seems to work (and why wouldn't it, this is fairly robust tech) but the linkage to the xaml seems to be what is failing.
So I add two items and delete the first one, crash! and here is the voluminous error output from the immediate window:
> am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -n "com.companyname.mauiapp1/crc64e632a077a20c694c.MainActivity"
> Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.companyname.mauiapp1/crc64e632a077a20c694c.MainActivity }
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/MauiApp1.dll
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/Mono.Android.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Runtime.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/Java.Interop.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Collections.dll [External]
Resolved pending breakpoint for 'Android.Runtime.JNIEnv.RegisterJniNatives(System.IntPtr, System.Int32, System.IntPtr, System.IntPtr, System.Int32)' to /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:124 [0x00000].
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Threading.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Runtime.InteropServices.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Threading.Thread.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Diagnostics.StackTrace.dll [External]
Loaded assembly: data-0x756590c69030 [External]
Loaded assembly: data-0x756590c59020 [External]
Loaded assembly: data-0x75637a336040 [External]
Loaded assembly: data-0x756590c79040 [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/netstandard.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Linq.dll [External]
Loaded assembly: /data/data/com.companyname.mauiapp1/files/.__override__/System.Collections.Concurrent.dll [External]
Resolved pending breakpoint for 'Xamarin.HotReload.HotReloadAgent.BreakpointSendToIde(System.String)' to D:\a\_work\1\s\HotReload\Source\Xamarin.HotReload.Agent\HotReloadAgent.cs:419 [0x00000].
...hundreds of lines removed
[nyname.mauiapp] runtime.cc:663] at int androidx.recyclerview.widget.LinearLayoutManager.fill(androidx.recyclerview.widget.RecyclerView$Recycler, androidx.recyclerview.widget.LinearLayoutManager$LayoutState, androidx.recyclerview.widget.RecyclerView$State, boolean) (LinearLayoutManager.java:1591)
[nyname.mauiapp] runtime.cc:663] at void androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler, androidx.recyclerview.widget.RecyclerView$State) (LinearLayoutManager.java:668)
[nyname.mauiapp] runtime.cc:663] at void androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2() (RecyclerView.java:4309)
[nyname.mauiapp] runtime.cc:663] at void androidx.recyclerview.widget.RecyclerView.dispatchLayout() (RecyclerView.java:4012)
[nyname.mauiapp] runtime.cc:663] at void androidx.recyclerview.widget.RecyclerView.onLayout(boolean, int, int, int, int) (RecyclerView.java:4578)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void androidx.viewpager2.widget.ViewPager2.onLayout(boolean, int, int, int, int) (ViewPager2.java:527)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(androidx.coordinatorlayout.widget.CoordinatorLayout, android.view.View, int) (HeaderScrollingViewBehavior.java:148)
[nyname.mauiapp] runtime.cc:663] at boolean com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(androidx.coordinatorlayout.widget.CoordinatorLayout, android.view.View, int) (ViewOffsetBehavior.java:43)
[nyname.mauiapp] runtime.cc:663] at boolean com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(androidx.coordinatorlayout.widget.CoordinatorLayout, android.view.View, int) (AppBarLayout.java:2122)
[nyname.mauiapp] runtime.cc:663] at void androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(boolean, int, int, int, int) (CoordinatorLayout.java:953)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.setChildFrame(android.view.View, int, int, int, int) (LinearLayout.java:1829)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.layoutVertical(int, int, int, int) (LinearLayout.java:1673)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.onLayout(boolean, int, int, int, int) (LinearLayout.java:1582)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void androidx.drawerlayout.widget.DrawerLayout.onLayout(boolean, int, int, int, int) (DrawerLayout.java:1263)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.setChildFrame(android.view.View, int, int, int, int) (LinearLayout.java:1829)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.layoutHorizontal(int, int, int, int) (LinearLayout.java:1818)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.onLayout(boolean, int, int, int, int) (LinearLayout.java:1584)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.setChildFrame(android.view.View, int, int, int, int) (LinearLayout.java:1829)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.layoutVertical(int, int, int, int) (LinearLayout.java:1673)
[nyname.mauiapp] runtime.cc:663] at void android.widget.LinearLayout.onLayout(boolean, int, int, int, int) (LinearLayout.java:1582)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[nyname.mauiapp] runtime.cc:663] at void android.view.ViewGroup.layout(int, int, int, int) (ViewGroup.java:6389)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.layoutChildren(int, int, int, int, boolean) (FrameLayout.java:332)
[nyname.mauiapp] runtime.cc:663] at void android.widget.FrameLayout.onLayout(boolean, int, int, int, int) (FrameLayout.java:270)
[nyname.mauiapp] runtime.cc:663] at void com.android.internal.policy.DecorView.onLayout(boolean, int, int, int, int) (DecorView.java:784)
[nyname.mauiapp] runtime.cc:663] at void android.view.View.layout(int, int, int, int) (View.java:22844)
[libc] Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 31838 (nyname.mauiapp1), pid 31838 (nyname.mauiapp1)
Told you it was voluminous and I can't figure out anything. I mean what is anyone supposed to do with all that!!!
This is probably a very often used use pattern, but I just cannot figure out what I've done wrong. Any pointers gratefully receieved and hopefully it isn't a spelling error... :sigh
G
CodePudding user response:
ListView
is one of 4 "legacy" Renderer-based views.
I recommend using CollectionView
instead. Some properties will be different, but in your example, only TWO changes ared needed:
- Change
ListView
toCollectionView
. - Remove
<ViewCell>
from theItemTemplate
. (Or will crash with "Can't cast" exception.)
<CollectionView Grid.Row="2" x:Name="TodoList" ItemsSource="{Binding TodoItems}">
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- REMOVED ViewCell -->
<Grid Margin="10, 0, 0, 0" ... />
...
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
- Verified that this version works (does not crash when delete first item) on Android.