xamarin.formsrealmreactiveui

InvokeCommand() does not fire


I am using ReactiveUI in one of my Xamarin.Forms app. It have a search functionality. Just like the official sample on ReactiveUI Github page. But the Search command does not fire when the query string changes. I am using realm mobile database for local storage. The View code:

  <Shell.TitleView>
    <SearchBar x:Name="SearchHandler"
               Placeholder="Select Company" />
</Shell.TitleView>
<ContentPage.Content>

    <StackLayout>
        <ListView x:Name="CompaniesListView">
            <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.Footer>
                <StackLayout Spacing="0"
                             HorizontalOptions="FillAndExpand">
                    <Frame CornerRadius="20"
                           Margin="10"
                           Padding="0">
                        <Entry Placeholder="Company Name"
                               x:Name="NewCompanyEntry"></Entry>
                    </Frame>

                    <Button Text="Add"
                            x:Name="AddButton"
                            HorizontalOptions="Center" />
                </StackLayout>
            </ListView.Footer>
        </ListView>

    </StackLayout>

</ContentPage.Content>

The Code behind:

   public partial class MainPage : ReactiveContentPage<MainViewModel>
{


    public MainPage()
    {
        InitializeComponent();

        ViewModel = new MainViewModel();


        this.Bind(ViewModel, vm => vm.Query, v => v.SearchHandler.Text);
        this.BindCommand(ViewModel, vm => vm.AddCompanyCommand, v => v.AddButton);
        this.Bind(ViewModel, vm => vm.NewCompany, v => v.NewCompanyEntry.Text);
        this.OneWayBind(ViewModel, vm => vm.Companies, view => view.CompaniesListView.ItemsSource);


    }
}

The ViewModel:

 public class MainViewModel : ReactiveObject
{

    public IEnumerable<Company> Companies { get; set; }


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

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

    public ReactiveCommand<Unit, Unit> AddCompanyCommand { get; set; }
    public ReactiveCommand<Unit, Unit> Search { get; set; }

    Realm _realm;

    public MainViewModel()
    {

        _realm = Realm.GetInstance();
        Companies = _realm.All<Company>();

        this.WhenAnyValue(x => x.Query).Select(query => !String.IsNullOrWhiteSpace(query)).Select(_ => Unit.Default).Throttle(TimeSpan.FromSeconds(1)).InvokeCommand(this, x => x.Search);

        AddCompanyCommand = ReactiveCommand.CreateFromTask(async () => await AddButtonClicked());
        Search = ReactiveCommand.CreateFromObservable(
            () =>
               Observable
               .StartAsync(SortCollection)
        );
    }



    async Task AddButtonClicked()
    {
        if (!string.IsNullOrWhiteSpace(NewCompany))
        {
            _realm.Write(() =>
            {
                _realm.Add(new Company { Name = NewCompany });
            });
            NewCompany = string.Empty;
        }
    }

    async Task SortCollection()
    {

        Companies = _realm.All<Company>().OrderBy(m => m.Name).Where(company => company.Name.ToLower().Contains(Query.ToLower()));

    }
}

This demo app is also available on my GitHub profile.

Thank you.


Solution

  • The answer by Glenn Watson is not complete. Though he helped me through comments and slack to resolve the issue. The complete answer is:

    1. Move the Unit.Default select statement to after throttle.
    2. Use Where instead of Select to check if string is NullOrWhiteSpace.
    3. Move WhenAnyValue after where the command is created.
    4. Finally use MainUIThread to set the Companies from return value of ReactiveCommand.