我目前正在开发 Windows Phone 应用程序,我想使用 Reactive Extensions 来创建异步以获得更好的 UI 体验。
我使用 MVVM 模式:我的 View 在我的 ViewModel 中有一个 ListBox 绑定到 Items 的 ObservableCollection。项目具有传统属性,如名称或 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>
我在 App.xaml.cs 中实例化我的 MainViewModel:
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.xaml.cs 中填充了 App.Current.RootVisual.Dispatcher。
public class MainViewModel : ViewModelBase
{
[...]
public Dispatcher CurrentDispatcher { get; set; }
public void GetData()
{
this.CurrentDispatcher.BeginInvoke(new Action(LoadData));
}
[...]
}
但我想使用反应式扩展。因此,我尝试了 Rx 的不同元素,例如 ToAsync 或 ToObservable,但是当我将项目添加到清单时,我遇到了一些“UnauthorizedAccessException 未处理”和“无效的跨线程访问”。
我尝试观察其他线程,因为错误可能来自 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 并在订阅中使用它。这是完美的!非常感谢!