Xamarin.Forms + ReactiveUI + Xamvvm 视图仅在 iOS 上不更新

Xamarin.Forms + ReactiveUI + Xamvvm View not updating on iOS only

我们正在使用 Xamarin.Forms 和 ReactiveUI 构建跨平台应用程序。 应用程序结构基于以下示例:

https://github.com/GiusepeCasagrande/reactive-dev-days-lab

Android版本按预期工作,但是,在iOS模拟器上,当您运行命令"SyncSpeakers"时,命令成功执行,视图闪烁但该列表未填充。

谁能理解为什么会这样? 我应该放弃使用 xammvvm 进行路由并切换到其他东西吗?

speakers的ViewModel代码如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using DevDaysSpeakers.Model;
using DevDaysSpeakers.Services;
using ReactiveUI;
using Xamarin.Forms;
using Xamvvm;

namespace DevDaysSpeakers.ViewModel
{
    public class SpeakersViewModel: BasePageModelRxUI
    {
        public ReactiveList<Speaker> Speakers { get; } = new ReactiveList<Speaker>();

        Speaker speaker;
        public Speaker Speaker
        {
            get { return speaker; }
            set { this.RaiseAndSetIfChanged(ref speaker, value); }
        }

        public ReactiveCommand<Unit, IEnumerable<Speaker>> GetSpeakers { get; }

        public ReactiveCommand GoToDetails { get; }

        readonly ObservableAsPropertyHelper<bool> busy;
        public bool IsBusy => busy.Value;

        public SpeakersViewModel()
            : this(null)
        {

        }

        public SpeakersViewModel(AzureService azureService = null)
        {
            var service = azureService ?? DependencyService.Get<AzureService>();

            GetSpeakers = ReactiveCommand.CreateFromTask(_ => service.GetSpeakers());

            GetSpeakers
                .ObserveOn(RxApp.MainThreadScheduler)
                .SubscribeOn(RxApp.MainThreadScheduler)
                .Subscribe(speakers =>
                {
                    Speakers.Clear();
                    Speakers.AddRange(speakers);
                });

            GetSpeakers.IsExecuting
                .ToProperty(this, vm => vm.IsBusy, out busy);

            GetSpeakers.ThrownExceptions
                .Subscribe(error => Application.Current.MainPage.DisplayAlert("Error!", error.Message, "OK"));

            // go to details page when Speaker is set
            this.WhenAnyValue(vm => vm.Speaker)
                .Where(speaker => speaker != null)
                .Subscribe(speaker => this.PushPageFromCacheAsync<DetailsViewModel>(vm => vm.Speaker = speaker));

            this.ThrownExceptions
                .Subscribe(ex =>
            {
                Debug.WriteLine(ex.Message);
            });
        }
    }
}

视图的XAML:

<?xml version="1.0" encoding="utf-8"?>
<reactive:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:reactive="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms" 
    xmlns:xamvvm="clr-namespace:Xamvvm;assembly=Xamvvm.Forms.RxUI" 
    xmlns:local="clr-namespace:DevDaysSpeakers.ViewModel;assembly=DevDaysSpeakers" 
    x:Class="DevDaysSpeakers.View.SpeakersPage" 
    x:TypeArguments="local:SpeakersViewModel" 
    Title="Speakers" 
    x:Name="ThePage">
    <reactive:ReactiveContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
    </reactive:ReactiveContentPage.Padding>
    <reactive:ReactiveContentPage.Content>
        <StackLayout Spacing="0">
            <Button x:Name="SyncSpeakers" Text="Sync Speakers" />
            <ActivityIndicator x:Name="IsBusy" />
            <ListView x:Name="ListViewSpeakers">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ImageCell Text="{Binding Name}" Detail="{Binding Title}" ImageSource="{Binding Avatar}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </reactive:ReactiveContentPage.Content>
</reactive:ReactiveContentPage>

视图背后的代码

using System.Reactive.Disposables;
using DevDaysSpeakers.ViewModel;
using ReactiveUI;
using Xamvvm;

namespace DevDaysSpeakers.View
{
    public partial class SpeakersPage 
        : IBasePageRxUI<SpeakersViewModel>
    {
        public SpeakersPage()
        {
            InitializeComponent();

            this.WhenActivated(disposables =>
            {
                this.OneWayBind(ViewModel, x => x.Speakers, x => x.ListViewSpeakers.ItemsSource).DisposeWith(disposables);
                this.OneWayBind(ViewModel, x => x.GetSpeakers, x => x.SyncSpeakers.Command).DisposeWith(disposables);
                this.OneWayBind(ViewModel, x => x.IsBusy, x => x.IsBusy.IsVisible).DisposeWith(disposables);
                this.OneWayBind(ViewModel, x => x.IsBusy, x => x.IsBusy.IsEnabled).DisposeWith(disposables);
                this.Bind(ViewModel, x => x.Speaker, x => x.ListViewSpeakers.SelectedItem).DisposeWith(disposables);
            });
        }
    }
}

Project.json 对于 iOS

{
  "supports": {},
  "dependencies": {
    "Microsoft.Azure.Mobile.Client.SQLiteStore": "3.1.0",
    "Microsoft.Bcl.Build": "1.0.21",
    "reactiveui-events-xamforms": "7.1.0",
    "reactiveui-xamforms": "7.1.0",
    "SQLitePCLRaw.bundle_e_sqlite3": "1.1.2",
    "Xam.Plugins.TextToSpeech": "2.0.0",
    "Xamarin.Forms": "2.3.3.180",
    "Xamvvm.Forms.RxUI": "1.0.2"
  },
  "frameworks": {
    ".NETPortable,Version=v4.5,Profile=Profile7": {}
  }
}

这是一个 Xamarin.Forms 错误。你可以在这里看到讨论 https://github.com/reactiveui/ReactiveUI/issues/806

这里是错误本身:https://bugzilla.xamarin.com/show_bug.cgi?id=31415

在第一个 link 中,您可以找到一些解决方法,一个非常简单的方法就是更改 ObservableCollection 的 ReactiveList。