我目前正在研究Windows Phone應用程序,我想使用Reactive Extensions創建異步以獲得更好的UI體驗。MVVM,ObservableCollection和Reactive Extensions(Rx)無效跨線程訪問
我使用MVVM模式:我的視圖有一個列表框在我的ViewModel綁定到一個ObservableCollection的項目。一個項目具有傳統屬性,如Name或IsSelected。
<ListBox SelectionMode="Multiple" ItemsSource="{Binding Checklist, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我實例我MainViewModel在App.xaml.cs:
public partial class App : Application
{
private static MainViewModel _viewModel = null;
public static MainViewModel ViewModel
{
get
{
if (_viewModel == null)
_viewModel = new MainViewModel();
return _viewModel;
}
}
[...]
}
我加載MainPage_Loaded我的數據。
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
this.DataContext = App.ViewModel;
this.Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
}
private void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
App.ViewModel.LoadData();
}
}
我從我的ViewModel載入我的數據(我打算以後代碼移到模型):
public class MainViewModel : ViewModelBase
{
public ObservableCollection<Item> _checklist;
public ObservableCollection<Item> Checklist
{
get
{
return this._checklist;
}
set
{
if (this._checklist != value)
{
this._checklist = value;
}
}
}
private const string _connectionString = @"isostore:/ItemDB.sdf";
public void LoadData()
{
using (ItemDataContext context = new ItemDataContext(_connectionString))
{
if (!context.DatabaseExists())
{
// Create database if it doesn't exist
context.CreateDatabase();
}
if (context.Items.Count() == 0)
{
[Read my data in a XML file for example]
// Save changes to the database
context.SubmitChanges();
}
var contextItems = from i in context.Items
select i;
foreach (Item it in contextItems)
{
this.Checklist.Add(it);
}
}
}
[...]
}
並能正常工作,項目在視圖更新。
現在我想創建異步。在我從View中調用而不是LoadData的新方法中使用傳統BeginInvoke時,它工作正常。
public partial class MainPage : PhoneApplicationPage
{
[...]
private void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
App.ViewModel.GetData();
}
}
我使用,我叫CurrentDispatcher屬性,它充滿與App.Current.RootVisual.Dispatcher的App.xaml.cs。
public class MainViewModel : ViewModelBase
{
[...]
public Dispatcher CurrentDispatcher { get; set; }
public void GetData()
{
this.CurrentDispatcher.BeginInvoke(new Action(LoadData));
}
[...]
}
但我想用Reactive Extentions。所以我嘗試了像ToAsync或ToObservable等Rx的不同元素,但是當我向清單中添加一個項目時,我有一些「UnauthorizedAccessException未處理」和「無效的跨線程訪問」。
我試圖ObserveOn其他線程可能錯誤來自UI和後臺線程之間的混合,但它不起作用。也許我不會像在這種特殊情況下那樣使用Rx?
任何幫助將不勝感激。你的答案後
編輯:
這裏是一個偉大的工程代碼!
public void GetData()
{
Observable.Start(() => LoadData())
.ObserveOnDispatcher()
.Subscribe(list =>
{
foreach (Item it in list)
{
this.Checklist.Add(it);
}
});
}
public ObservableCollection<Item> LoadData()
{
var results = new ObservableCollection<Item>();
using (ItemDataContext context = new ItemDataContext(_connectionString))
{
//Loading
var contextItems = from i in context.Items
select i;
foreach (Item it in contextItems)
{
results.Add(it);
}
}
return results;
}
正如您所看到的,我之前沒有正確使用。現在我可以使用ObservableCollection並在Susbscribe中使用它。這是完美的!非常感謝!
你真的需要考慮重塑代碼來產生你的加載項作爲'IObservable- ',然後對調度程序調度程序執行'ObserveOn()'。不要只在Rx代碼中顯示喇叭。試着讓它成爲你查詢的核心方式。 –
Enigmativity
2012-07-25 00:15:23
你是對的我想用我的起源方法使用Rx,這是一個壞主意。謝謝你的建議。 – 2012-07-27 02:55:00