ReactiveUI 6 со списком не обновляется

Возился с reactiveui и пытался получить простой пример, работающий в универсальном приложении и имеющий некоторые различия.

У меня есть модель просмотра:

public class Rooms : ReactiveObject
{
    private RoomsService roomService;
    private IReactiveCommand<IList<Room>> fetchRooms;

    private ObservableAsPropertyHelper<IList<Room>> myRooms;
    public IList<Room> MyRooms
    {
        get { return myRooms.Value; }
    }

    public Rooms(RoomsService roomsService)
    {
        this.RoomService = roomsService;
        fetchRooms = ReactiveCommand.CreateAsyncTask<IList<Room>>(Observable.Return<bool>(true), async x => await RoomService.Rooms);
        fetchRooms.Subscribe(_ => LogMessage("Cool, it was invoked!"));
        fetchRooms.ToProperty(this, x => x.MyRooms, out myRooms);

    }

    public RoomsService RoomService
    {
        get { return roomService; }
        set { roomService = value; }
    }
}

а служба, которая получает информацию, это:

public class RoomsService
{
    private FakeRoomService service = new FakeRoomService();

    public IObservable<IList<Room>> Rooms
    {
        get
        {
            LogMessage("RoomsService - Rooms property called.");
            return Observable
                .Interval(TimeSpan.FromSeconds(1))
                .Select(_ => service.GetRoomsForUser());
        }
    }
}

с базовым кодом позади:

public sealed partial class HubPage : Page
{
    private NavigationHelper navigationHelper;
    private Rooms rooms = new Rooms(new RoomsService());

    /// <summary>
    /// Gets the NavigationHelper used to aid in navigation and process lifetime management.
    /// </summary>
    public NavigationHelper NavigationHelper
    {
        get { return this.navigationHelper; }
    }

    /// <summary>
    /// Gets the rooms view model.
    /// </summary>
    public Rooms Rooms
    {
        get { return this.rooms; }
    }

    public HubPage()
    {
        this.InitializeComponent();
        this.navigationHelper = new NavigationHelper(this);
        this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
    }

и последний xaml:

<Page
x:Name="pageRoot"
x:Class="HubPage"
DataContext="{Binding Rooms, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Hub"
xmlns:data="using:Hub.Data"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Hub SectionHeaderClick="Hub_SectionHeaderClick">
        <HubSection Width="500" x:Uid="Section1Header" Header="Section 1">
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="300" />
                    </Grid.RowDefinitions>

                    <ListView Name="RoomListView" Grid.Row="1" ItemsSource="{Binding MyRooms}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" />
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </Grid>
            </DataTemplate>
        </HubSection>         
    </Hub>
</Grid>

I'm clearly missing something obvious in the viewmodel, but I'm not sure what it is. The task seems to tick over but the the property never gets updated... or if it does, its not propagating through to the view?

Любой совет или есть где-нибудь действительно простой рабочий пример для v6?


person Marty Henderson    schedule 03.11.2014    source источник


Ответы (1)


Если вы await Observable, это примерно эквивалентно в Rx:

someObservable.TakeLast(1).Publish(new AsyncSubject<T>()).Subscribe();

Но вот в чем проблема, проверьте свой Observable:

return Observable
    .Interval(TimeSpan.FromSeconds(1))
    .Select(_ => service.GetRoomsForUser());

Он никогда не завершается, поэтому TakeLast никогда не возвращается!

Ват До?

Вместо этого мы должны просто написать:

fetchRooms = ReactiveCommand.CreateAsyncTask<IList<Room>>(_ => Task.Run(service.GetRoomsForUser());

Затем в представлении кода программной части мы можем настроить таймер для вызова команды:

Observable.Interval(TimeSpan.FromSeconds(5), RxApp.MainThreadScheduler)
    .InvokeCommand(this, x => x.ViewModel.FetchRooms);

Почему именно просмотр кода программной части?

Настройка таймеров и вызов команд Do Stuff в конструкторе ViewModel затрудняет тестирование, потому что вам нужно развернуть 3032-кратные макеты, чтобы предотвратить возникновение множества вещей.

Вместо этого конструктор View должен подключиться к виртуальной машине, чтобы запустить эти действия, и тесты виртуальной машины могут вызывать их, когда они хотят их явно протестировать.

person Ana Betts    schedule 03.11.2014
comment
Спасибо, Пол! Итак, является ли общая рекомендация выполнять всю работу, связанную с настройкой таймеров и периодических запросов в коде представления позади? - person Marty Henderson; 05.11.2014
comment
@KieranH Дело не в том, что вы выполняете работу в коде просмотра позади, а в том, что работа инициируется кодом просмотра позади. Вы пишете код в виртуальной машине, но запускаете его в представлении - person Ana Betts; 05.11.2014