You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Maui编辑ObservableCollection项后ObservableView未更新问题

问题描述

使用MVVM Community Toolkit时,添加或删除项时ObservableCollection能正常更新视图,但编辑项时视图无法同步更新。调试确认ObservableCollection中的项已经被正确修改,试过绑定IsRefreshing手动刷新、清空再重新填充集合,都没用。

相关代码

模型类

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); }
}

主视图模型

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 } });
    }
}

XAML代码

<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>
尝试过的解决方案

添加了MAUI Community Toolkit并做了如下配置:

XAML配置

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()
    {
        // 尝试让视图更新,失败了
        if (!Items.Contains(Item))
        {
            Items.Add(Item);
        }
        Shell.Current.GoToAsync("..");
    }
}

这些方法都没解决问题,还搞坏了原本正常的功能。作为新手,想知道有没有人成功实现编辑项后更新CollectionView的方案?

解决方案

问题核心是绑定配置错误,导致视图无法感知到项的属性变化,以下是修复步骤:

  1. 修正DataTemplate的类型绑定
    你的CollectionView绑定的是ToDoTask集合,但DataTemplate错误地指定了x:DataType="{x:Type x:String}",这会让绑定系统无法识别ToDoTask的属性。
  • 先在XAML根节点添加ToDoTask所在的命名空间,比如:
    xmlns:local="clr-namespace:TaskMaster_DEV" <!-- 替换成你的项目命名空间 -->
    
  • 把DataTemplate的x:DataType改成{x:Type local:ToDoTask},同时把Label的绑定改成具体属性(比如TaskName):
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="{x:Type local:ToDoTask}">
            <SwipeView>
                <!-- 其他SwipeItem代码不变 -->
                <Label Text="{Binding TaskName}" FontSize="18"/>
                <!-- 其他代码不变 -->
            </SwipeView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
    
  1. 移除无效的手动刷新逻辑
    ToDoTask已经继承ObservableObject,且属性通过SetProperty实现了INotifyPropertyChanged通知,只要绑定正确,属性更新时视图会自动刷新。删掉PageAppearing方法里清空再重新添加集合的代码,这个操作完全多余,还可能引发异常。

  2. 简化Save方法
    编辑现有任务时,你传递的是集合里的原实例,属性修改后绑定系统会自动同步,不需要判断是否添加到集合:

[RelayCommand]
public void Save()
{
    Shell.Current.GoToAsync("..");
}

完成以上修改后,编辑ToDoTask的任意属性(比如TaskName),CollectionView都会自动更新对应的视图内容。


内容的提问来源于stack exchange,提问作者AFranklin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 16:27:37