ReactiveUI 版本 9.19.5 绑定不适用于 Xamarin.Forms 版本 4.1.0

The ReactiveUI ver 9.19.5 Binding is not working with Xamarin.Forms ver 4.1.0

我正在努力了解 ReactiveUI。我创建了一个包含以下主要包的新项目:

  1. Xamarin.Forms 4.1.0.618606
  2. PropertyChanged.Fody 3.0.1
  3. ReactiveUI 9.19.5
  4. ReactiveUI.XamForms 9.19.5

该项目有一个 page 和关联的 viewmodel。我想使用 ReactiveUI 将视图与 ViewModel 绑定。但是绑定不起作用。项目构建和 运行 但不会触发任何 property changed notification 也不会触发任何 command。该应用需要显示 listcompany names。但该列表不显示 ViewModel 中定义的集合。该应用程序应允许用户使用 CollectionChangeCommand 对列表进行排序,每次搜索更改 query

查看代码:

 <rxui:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                              xmlns:d="http://xamarin.com/schemas/2014/forms/design"
                              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                              xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
                              xmlns:local="clr-namespace:Demo"
                              x:TypeArguments="local:MainViewModel"
                              x:Class="Demo.MainPage">
        <Shell.TitleView>
            <SearchBar x:Name="SearchHandler"
                       Placeholder="SelectCompany" />
        </Shell.TitleView>
        <StackLayout>
            <ListView x:Name="CompaniesListView"
                      ItemsSource="{Binding Companies}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout>
                                <StackLayout Orientation="Horizontal"
                                             Margin="10">
                                    <Label x:Name="NameLabel"
                                           Text="{Binding Name}" />
                                </StackLayout>
                                <BoxView HorizontalOptions="FillAndExpand"
                                         HeightRequest="1"
                                         Color="BlueViolet" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button Text="ClickMe"
                    x:Name="OnlyButton" />
        </StackLayout>

    </rxui:ReactiveContentPage>

后面的代码:

public partial class MainPage : ReactiveContentPage<MainViewModel>
    {
        MainViewModel vm;

        public MainPage()
        {
            InitializeComponent();

            this.Bind(ViewModel, vm => vm.Query, v => v.SearchHandler.Text);
            this.BindCommand(ViewModel, vm => vm.ButtonClickedCommand, v => v.OnlyButton);

        }
    }

ViewModel代码:

[AddINotifyPropertyChangedInterface]
    public class MainViewModel : ReactiveObject
    {
        public ObservableCollection<Company> Companies { get; private set; }


        public ReactiveCommand<Unit, Unit> ButtonClickedCommand { get; set; }
        public ReactiveCommand<Unit, Unit> CollectionChange { get; set; }
        public string Query { get; set; }



        public MainViewModel()
        {
            Companies = new ObservableCollection<Company>
            {
                new Company{Name="EPF CORPORATION"},
                new Company{Name="ABC COMPANY PVT. LTD."},
                new Company{Name="UNIQUE COMPUTER SYSTEMS"},
                new Company{Name="MICROSOFT PRIVATE LIMITED"},
            };

            this.WhenAny(x => x.Query, x => !string.IsNullOrWhiteSpace(x.Value));
            CollectionChange = ReactiveCommand.CreateFromTask(async () => await SortCollection());
            ButtonClickedCommand = ReactiveCommand.CreateFromTask(async () => await ButtonClicked()); 

        async Task SortCollection()
        {

            ObservableCollection<Company> temp;
            temp = new ObservableCollection<Company>(Companies.OrderByDescending(m => m.Name.ToLower().StartsWith(Query.ToLower(), StringComparison.CurrentCulture)));
            Companies.Clear();
            foreach (Company c in temp)
                Companies.Add(c);

        }

        async Task ButtonClicked()
        {
            await Shell.Current.DisplayAlert("Button Clicked", "The reactive button command fired finally", "OK");
        }
    }

我只想使用 ReactiveObjectReactiveCommand。我会坚持 Xamarin.Forms Shell 直到 ReactiveShell 不可用。

如果有人能告诉我如何通过 xamarin.forms 正确使用 ReactiveUI,我将不胜感激。

在您的 MainPage.xaml.cs 中,您似乎从未创建过 ViewModel?

您正在 ReactiveObject 上使用 INotifyPropertyChanged fody,那里有一个 ReactiveUI fody。您不应在 ReactiveObject 上生成混合 INotifyPropertyChange。

您有一个 vm 属性 但也没有创建。

ReactiveContentControl<TViewModel> 上有一个名为 ViewModel 的 属性,您需要对其进行设置。那可以从另一个控件传入。

同样在你的 ViewModel.cs 文件中你有一个 this.WhenAny 但似乎没有对生成的可观察对象做任何事情?

另请注意,更好的排序方法可能是使用现在属于 ReactiveUI 系列的 DynamicData 框架。

你可以这样做

ViewModel.cs:

public class MainViewModel : ReactiveObject
{
    private readonly SourceList<Company> _companies;
    private readonly ReadOnlyObservableCollection<Company> _sortedCompanies;

    //Reactive Commands
    public ReactiveCommand<Unit, Unit> ButtonClickedCommand { get; }

    public ReadOnlyObservableCollection<Company> Companies => _sortedCompanies;

    [Reactive]
    public string Query { get; set; }

    public MainViewModel()
    {
        _companies = new SourceList<Company>();
        _companies.AddRange(new[]
        {
            new Company{Name="EPF CORPORATION"},
            new Company{Name="ABC COMPANY PVT. LTD."},
            new Company{Name="UNIQUE COMPUTER SYSTEMS"},
            new Company{Name="MICROSOFT PRIVATE LIMITED"},
        });

        // Delay to once every 500 milliseconds doing an update.
        var refreshObs = this.WhenAnyValue(x => x.Query).Throttle(TimeSpan.FromMilliseconds(500));

        _companies.Connect()
            .AutoRefreshOnObservable(_ => refreshObs)
            .Filter(m => Query == null || m.Name.IndexOf(Query, StringComparison.CurrentCultureIgnoreCase) >= 0) // If it contains a portion of the text.
            .Sort(SortExpressionComparer<Company>.Ascending(t => t.Name))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(out _sortedCompanies)
            .Subscribe();

        ButtonClickedCommand = ReactiveCommand.CreateFromTask(async () => await ButtonClicked());
    }

    async Task ButtonClicked()
    {
        await Shell.Current.DisplayAlert("Button Clicked", "The reactive button command fired finally", "OK");
    }
}

MainPage.xaml.cs

public partial class MainPage : ReactiveContentPage<MainViewModel>
{
    public MainPage()
    {
        InitializeComponent();

        ViewModel = new MainViewModel();

        //ReactiveUI Bindings
        this.Bind(ViewModel, vm => vm.Query, v => v.SearchHandler.Text);
        this.BindCommand(ViewModel, vm => vm.ButtonClickedCommand, v => v.OnlyButton);

        this.OneWayBind(ViewModel, vm => vm.Companies, view => view.CompaniesListView.ItemsSource);
    }
}

我在 FodyWeavers.xml

中添加了包含 ReactiveUI 的 ReactiveUI.Fody 包

我还更改了您的 ListView,使其不执行 XAML 绑定,因此 <ListView x:Name="CompaniesListView">

这对我来说似乎很有效。