CollectionView ItemsSource 绑定视觉更新 (Xamarin.Forms)

CollectionView ItemsSource binding visual update (Xamarin.Forms)

我试图在我的应用程序中加入一些 MVVM 模式,但我 运行 遇到了数据可视化表示的问题。如果绑定的 observablecollecrion 更新了数据,但视觉对象没有更新。 一些代码: 视图模型:

public class HlavnaViewModel : BaseViewModel
{
    public HlavnaViewModel()
    {
    }
    

    private Doklady _selectedDok;
    public Doklady vm_selectedDok
    {
        get => _selectedDok;
        set
        {
            _selectedDok = value;
            OnPropertyChanged(nameof(vm_selectedDok));
            update_polozky();
        }
    }

    public async void update_polozky()
    {
        Polozky dok = new Polozky() { id_doklad = _selectedDok.id };
        ObservableCollection<Polozky> pol = new ObservableCollection<Polozky>(await App.Database.GetPolozkyAsync(dok));
        vm_polozky = pol;
    }
    private ObservableCollection<Polozky> _polozky;
    public ObservableCollection<Polozky> vm_polozky
    {
        get => _polozky;
        set
        {
            _polozky =value;
            OnPropertyChanged(nameof(vm_polozky));
        }
    }
}

在XAML:

<CollectionView x:Name="polozky" SelectionMode="Single" ItemsSource="{Binding vm_polozky}">...

基础视图模型:

public class BaseViewModel : INotifyPropertyChanged
{
    string title = string.Empty;
    public string Title
    {
        get { return title; }
        set { SetProperty(ref title, value); }
    }

    protected bool SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;
        if (changed == null)
            return;

        changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

终于在视图中:

public Hlavna()
    {
        InitializeComponent();
        hvm = new HlavnaViewModel();
        this.BindingContext = hvm;
        
    }

如果我 select CollectionView 中的一行设置了 vm_selectedDok 绑定,它 select 是该项目,触发 update_polozky(),vm_polozky 填充了正确的数据,但视觉效果只是不显示来自 vm_polozky 的项目。

我读过几个类似的问题,但我不知道我在哪里犯了错误。

编辑: 所以问题出在其他地方,我的 grid.rowdefinitions 设置错了,因此网格在可见区域之外。

@ToolmakerSteve 在调用 async/await 时提出了很好的建议,请阅读他的回答。

基本上归结为,不要这样做:ObservableCollection<Polozky> pol = new ObservableCollection<Polozky>(await App.Database.GetPolozkyAsync(dok));

每当您创建一个新的 ObservableCollection 时,它都会丢失与 UI 的数据绑定。使用 .Clear() 清除 ObservableCollection 并使用 for 循环向其中添加新项目。例如:

public async void update_polozky()
{
    Polozky dok = new Polozky() { id_doklad = _selectedDok.id };
    var results = await App.Database.GetPolozkyAsync(dok);

    vm_polozky.Clear();

    foreach(var item in results)
        vm_polozky.Add(item);
}

有两种替代方法可以解决此问题。


一种方式是杰拉尔德的回答。这对于小的 collections 没问题,但是如果要添加很多项目,可能 会更慢。


第二种方法是做你已经做的 - 替换 collection。但是您的代码中需要修复。

您调用 update_polozky 的方式无法可靠地工作。您不会在 await.

中启动 async/await 序列

替换:

update_polozky();

有:

Device.BeginInvokeOnMainThread(async () => {
    await update_polozky();
});

可选:也可以进行此更改。 (虽然它不应该是必要的。)这提供了一个放置断点的好地方,以查看“结果”是否获得预期的内容。

替换:

ObservableCollection<Polozky> pol = new ObservableCollection<Polozky>(await App.Database.GetPolozkyAsync(dok));

有:

var result = await App.Database.GetPolozkyAsync(dok);
ObservableCollection<Polozky> pol = new ObservableCollection<Polozky>(result);

其他程序员的重要注意事项:

第二种方法(“替换 collection”)依赖于 ObservableCollectionsetter 中的 OnPropertyChanged(nameof(vm_polozky));

你有那个,所以对你来说不是问题。我向任何可能改编此代码的人提及这一点。

例如,我看到有人试图直接设置私有值,例如:

// Don't do this to replace the collection. XAML won't know you changed it!
_myPrivateField = new ObservableCollection<MyItem>(result);

我也看到有人试图拥有一个没有 setter:

的 ObservableCollection
// This is okay UNLESS you replace the collection - in which case you need `OnPropertyChanged(nameof(MyCollection))` somewhere:
public ObservableCollection<MyItem> MyCollection { get; set; }