Search code examples
.netmvvmmauimaui-community-toolkitcommunity-toolkit-mvvm

.Net Maui Editing ObservableCollection Item Does Not Update ObservableView


The title says it all. When adding or removing an item, the MVVM Community Toolkit ObservableView updates perfectly, but not when editing an item. Breaking after editing the item shows that the item is updated correctly in the ObservableCollection. I have bound IsRefreshing and manually set it to true. I manually Clear and repopulate my ObservableCollection.

My Model:

public partial class ToDoTask : ObservableObject
    {
        string taskName;
        string? description = null;
        string? notes = null;
        DateTime createdDate;
        DateTime? dueDate = null;
        bool hasDueDate;
        bool isOverdue;
        bool isComplete;
        string? taskLocation = null;
        Employee? assignor = null;
        Project? project = null;
        ObservableCollection<ToDoTask> subtasks = new ObservableCollection<ToDoTask>();
        ObservableCollection<Employee> assignees = new ObservableCollection<Employee>();


        
        public string TaskName { get => taskName; set => SetProperty(ref taskName, value); }

My ViewModel:

public partial class MainViewModel : ObservableObject
    {
        public MainViewModel()
        {
            Items = new ObservableCollection<ToDoTask>();
        }

        [ObservableProperty]
        ObservableCollection<ToDoTask> items;

        [ObservableProperty]
        bool isRefreshing;

        [ObservableProperty]
        string text;

        [ObservableProperty]
        DateTime? dueDate;

        [ObservableProperty]
        DateTime? createdDate;

        [RelayCommand]
        void Add()        
        {
            NewTask();
        }
        
        [RelayCommand]
        void Delete(ToDoTask s)
        {
            if(Items.Contains(s))
            {
                Items.Remove(s);
            }
        }

        

        [RelayCommand]
        public void PageAppearing()
        {
            IsRefreshing = true;
            List<ToDoTask> temp = new();
            foreach(ToDoTask t in Items)
            {
                temp.Add(t);
            }
            Items.Clear();
            foreach (ToDoTask t in temp)
            {
                Items.Add(t);
            }
            IsRefreshing = false;
        }

        [RelayCommand]
        async Task Tap(ToDoTask s)
        {
            await Shell.Current.GoToAsync($"{nameof(TaskPage)}", new Dictionary<string, object> { { "Item", s }, { "Items", Items} });
        }

        async Task NewTask()
        {
            ToDoTask temp = new ToDoTask();
            await Shell.Current.GoToAsync($"{nameof(TaskPage)}", new Dictionary<string, object> { { "Items", Items }, { "Item", temp } });
        }


 <RefreshView Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=RefreshCommand}"
                     CommandParameter="{Binding .}"
                     IsRefreshing="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=IsRefreshing}"
                     Grid.Row="2" 
                     Grid.ColumnSpan="2">
            <CollectionView 
                        ItemsSource="{Binding Items}"
                        SelectionMode="None" 
                        CanReorderItems="True">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="{x:Type x:String}">
                        <SwipeView>
                            <SwipeView.RightItems>
                                <SwipeItems>
                                    <SwipeItem Text="Delete"
                                           BackgroundColor="Red"
                                           Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}"
                                           CommandParameter="{Binding .}"/>
                                </SwipeItems>
                            </SwipeView.RightItems>
                            <SwipeView.LeftItems>
                                <SwipeItems>
                                    <SwipeItem Text="Edit"
                                           BackgroundColor="LightGreen"
                                           Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=EditTaskCommand}"
                                           CommandParameter="{Binding .}"/>
                                </SwipeItems>
                            </SwipeView.LeftItems>
                            <Grid Padding="0,5">
                                <Frame>
                                    <Frame.GestureRecognizers>
                                        <TapGestureRecognizer
                                        NumberOfTapsRequired="2"
                                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=TapCommand}"
                                        CommandParameter="{Binding .}"/>
                                    </Frame.GestureRecognizers>
                                    <Label Text="{Binding .}"
                                   FontSize="18"/>
                                </Frame>
                            </Grid>
                        </SwipeView>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </RefreshView>

Per 1 posted solution I added Maui Community Toolkit and followed those directions:

xmlns:behaviors="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TaskMaster_DEV.MainPage"
             xmlns:viewmodel="clr-namespace:TaskMaster_DEV.ViewModel"
             x:DataType="viewmodel:MainViewModel">
    <ContentPage.Behaviors>
        <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding PageAppearingCommand}">
        </behaviors:EventToCommandBehavior>
    </ContentPage.Behaviors>

[QueryProperty("Item", "Item")]
    [QueryProperty("Items", "Items")]
    public partial class TaskViewModel : ObservableObject
    {
        [ObservableProperty]
        ToDoTask item;
        [ObservableProperty]
        ObservableCollection<ToDoTask> items;

        [RelayCommand]
        public void Save()
        {
            //I only added this to try to get my ObservableView to update. Fail.
            if (!Items.Contains(Item))
            {
                Items.Add(Item);
            }
            Shell.Current.GoToAsync("..");
        }
    }

None of the ideas suggested anywhere I have found have worked and end up breaking what was working. I am pretty new to programming, and obviously Maui is new to all of us, but has anyone been able to successfully update an ObservableView by editing an item? Any help will be much appreciated!


Solution

  • Found the solution:

    I'm not sure this is ideal for MVVM design pattern but first, I gave my CollectionView in MainPage.xaml the name taskCollection, then in MainPage.xaml.cs overrode OnAppearing I set the taskCollection.ItemsSource to null and then repopulated it from my ObservableCollection.

    public partial class MainPage : ContentPage
    {
        MainViewModel temp;
        public MainPage(MainViewModel vm)
        {
            InitializeComponent();
            BindingContext = vm;
            temp = vm;
        }
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
            taskCollection.ItemsSource = null;
            taskCollection.ItemsSource = temp.Items;
        }
    }