2015-02-07 42 views
5

我覺得我正在做這個正確的方式,但我不確定。我在我的ViewModel異步加載對象到ReactiveList中。通過Xamarin Forms,我已經將列表綁定到Xamarin Forms中的ListView的ItemSource屬性。我也有一個搜索框,它將清除ReactiveList並將新項目添加到ReactiveList。綁定ReactiveList <T> Xamarin形式的ListView

第一次打開視圖時,沒有任何交互操作會加載列表,並且綁定到ReactiveCommand的按鈕用於將更多項目加載到ReactiveList中時被禁用。

第二次打開查看ListView幾乎立即渲染新ViewModel中的項目,但是再一次,交互似乎不起作用。但是,更改搜索框確實會清除ListView中的項目,但項目不會被添加。

此行爲在iOS上。我是ReactiveUI 6.3.1和Xamarin Forms 1.3.2.6316。

這裏是簡化的問題:

public class MyViewModel : ReactiveObject, IRoutableViewModel 
{ 
    public MyViewModel(IScreen hostScreen = null) 
    { 
     HostScreen = hostScreen ?? Locator.Current.GetService<IScreen>(); 

     List = new ReactiveList<ItemViewModel>() 
     { 
      ChangeTrackingEnabled = true 
     }; 

     //This never gets shown 
     List.Add(new ItemViewModel() 
     { 
      Name = "TEST" 
     }); 

     //Tried doing this on the main thread: same behavior 
     LoadItemsCommand = ReactiveCommand.CreateAsyncTask(_ => GetItems()), RxApp.TaskpoolScheduler); 

     LoadItemsCommand 
      .ObserveOn(RxApp.MainThreadScheduler) 
      .Subscribe(results => 
      { 
       //debugger breaks here in EVERY case and successfully changes List but doesn't necessarily affect the view 

       try 
       { 
        List.AddRange(results.Select(e => new ItemViewModel() 
        { 
         Name = e.Name 
        })); 
       } 
       catch (Exception ex) 
       { 
        //breakpoint does not break here. 
        Debug.WriteLine(ex.Message); 
        throw; 
       } 

      }); 

     //No exceptions here either 
     LoadItemsCommand.ThrownExceptions 
      .Select(ex => new UserError("Error", "Please check your Internet connection")) 
      .Subscribe(Observer.Create<UserError>(x => UserError.Throw(x))); 

     this.WhenAnyValue(e => e.SearchText).Subscribe(e => ResetPage()); 
    } 

    private Task<IEnumerable<Item>> GetItems() 
    { 
     //asynchronously get items 
     return ...; 
    } 

    private int ResetPage() 
    { 
     List.Clear(); 
     return 0; 
    } 

    [DataMember] 
    public ReactiveList<ItemViewModel> List { get; private set; } 

    private string _searchText; 
    [DataMember] 
    public string SearchText 
    { 
     get { return _searchText; } 
     set { this.RaiseAndSetIfChanged(ref _searchText, value); } 
    } 

    public ReactiveCommand<IEnumerable<Item>> LoadItems { get; protected set; } 

    public class ItemViewModel : ReactiveObject 
    { 
     public string Name { get; set; } 
    } 

    public string UrlPathSegment 
    { 
     get { return "Page"; } 
    } 

    public IScreen HostScreen { get; protected set; } 


} 

我的XAML:

<StackLayout VerticalOptions="FillAndExpand" Orientation="Vertical"> 
    <Entry x:Name="_searchEntry" Placeholder="Search"></Entry> 
    <ListView x:Name="_myListView" RowHeight="80"> 
     <ListView.ItemTemplate> 
     <DataTemplate> 
      <ViewCell> 
      <StackLayout Orientation="Vertical" > 
       <Label Text="{Binding Name}"></Label> 
       <Label Text="{Binding Address}"></Label> 
       <Label Text="{Binding PhoneNumber}"></Label> 
      </StackLayout> 
      </ViewCell> 
     </DataTemplate> 
     </ListView.ItemTemplate> 
    </ListView> 
    <Button x:Name="_loadMoreButton" Text="Load More" TextColor="White" BackgroundColor="#77D065"></Button> </StackLayout> 
</XamForms:ReactiveContentPage> 

我的觀點隱藏代碼:

using System; 
using System.Reactive.Concurrency; 
using System.Threading.Tasks; 

using ReactiveUI; 
using ReactiveUI.XamForms; 

namespace views 
{ 
    public partial class MyView : ReactiveContentPage<MyViewModel> 
    { 
     private IDisposable _disconnectHandler; 

     public NearbyPlacesView() 
     { 
      InitializeComponent(); 

      this.Bind(this.ViewModel, model => model.SearchText, view => view._searchEntry.Text); 
      this.OneWayBind(this.ViewModel, model => model.List, view => view._myListView.ItemsSource); 
      this.BindCommand(this.ViewModel, model => model.LoadItemsCommand, view => view._loadMoreButton); 
     } 

     protected override void OnAppearing() 
     { 
      base.OnAppearing(); 

      //Is this the proper way to do this? seems 
      _disconnectHandler = UserError.RegisterHandler(async error => 
      { 
       RxApp.MainThreadScheduler.ScheduleAsync(async (scheduler, token) => 
       { 
        await DisplayAlert("Error", error.ErrorMessage, "OK"); 
       }); 

       return RecoveryOptionResult.CancelOperation; 
      }); 

      //Load the items when the view appears. This doesn't feel right though. 
      ViewModel.LoadItemsCommand.Execute(null); 
     } 

     protected override void OnDisappearing() 
     { 
      base.OnDisappearing(); 

      _disconnectHandler.Dispose(); 
      _disconnectHandler = null; 
     } 
    } 
} 
+0

我得到了要顯示的初始測試條目。原因是因爲ResetPage()清除了最初的測試條目。 – kmc059000 2015-02-07 16:03:22

+0

我想了很多這個。在ViewModel構造函數中放置調用非常重要!我將SearchText的'this.WhenAnyValue(...)'移到LoadItemsCommand設置之前。現在調用'LoadItemsCommand.Execute(null);' 然後我從後面的視圖代碼中的'OnAppearing()'中取出'LoadItemsCommand.Execute(null);'。但是,我仍然在第一次查看該頁面時出現問題,沒有任何內容出現。 – kmc059000 2015-02-07 16:15:10

回答

4

這似乎是一個bug或者Xamarin Forms或ReactiveUI。問題在這裏記錄:https://github.com/reactiveui/ReactiveUI/issues/806

我改變了public ReactiveList<ItemViewModel> List的類型爲public ObservableCollection<ItemViewModel> List修復了這個問題。

+0

我對這個問題感到非常驚訝,ReactiveUI在過去的2 - 3年中以其穩定性給我留下了深刻的印象。 – Felix 2017-08-21 09:31:42

0

您的ItemViewModel是一個ReactiveObject,但是,您尚未正確編碼設置器。請記住,你應該在setter中使用this.RaiseAndSetIfChanged來提高NPC。

+0

ReactiveList <>屬性不需要,除非您新建覆蓋原始。 – kenny 2015-07-16 17:24:43

+0

不知道爲什麼它不需要,如果顯然這不起作用? – 2015-07-16 20:31:03