【问题标题】:The ReactiveUI ver 9.19.5 Binding is not working with Xamarin.Forms ver 4.1.0ReactiveUI 9.19.5 版绑定不适用于 Xamarin.Forms 4.1.0 版
【发布时间】:2019-12-04 12:34:36
【问题描述】:

我正在尝试使用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 将 View 与 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 不可用。

我将感谢任何可以向我展示如何正确使用 ReactiveUI 和 xamarin.forms 的人。

【问题讨论】:

  • 嘿,您可能想提出一个Minimal, reproducible example,目前它只是一个相当大的存储库作为您的示例,不能满足最小化。
  • @GlennWatson 我修改了问题。最小可复制项目文件的链接是send.firefox.com/download/11666c7d71103813/…。该绑定适用于默认 Xamarin.Forms 绑定。但不适用于 ReactiveUI。谢谢
  • @GlennWatson 还有一个查询。如何按查询字符串升序对公司名称进行排序。
  • github.com/reactiveui/dynamicdata#sorting sort() 扩展方法只接受一个比较器,所以在你的情况下它可能是Sort(SortExpressionComparer&lt;Company&gt;.Ascending(t =&gt; t.Name))

标签: xamarin.forms reactiveui


【解决方案1】:

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

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

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

ReactiveContentControl&lt;TViewModel&gt; 上,它有一个名为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 绑定,所以 &lt;ListView x:Name="CompaniesListView"&gt;

这对我来说似乎很有效。

【讨论】:

  • 感谢您的回答。像您这样的专家的支持对于像我这样的新手入门非常有帮助。
  • 在示例中添加了排序表达式以匹配您的原始示例(根据您对主要问题的评论)。
猜你喜欢
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多