我目前正在开发Windows Phone应用程序,我想使用Reactive Extensions来创建异步,以获得更好的UI体验。
我使用MVVM模式:我的View在我的ViewModel中有一个ListBox绑定到ObservableCollection的Items。 Item具有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>
我在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);
}
}
}
[...]
}
它工作正常,项目在视图中更新。
现在我想创建异步。使用传统的BeginInvoke是一种新方法,我从View而不是LoadData调用它可以正常工作。
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));
}
[...]
}
但我想使用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中使用它。这是完美的!非常感谢!
答案 0 :(得分:3)
您的代码中似乎没有异步性(保存BeginInvoke,我认为它不会按照您的想法进行)。
我假设您要使用Rx,因为您的代码目前全部阻塞,并且在建立与数据库的连接并加载数据时,UI没有响应: - (
你想要做的是在后台线程上执行“繁重的工作”,然后一旦你有值,只需将它们添加到Dispatcher上的ObservableCollection。您可以通过以下三种方式之一完成此任务:
您想要的代码可能看起来像这样
public IList<Item> FetchData()
{
using (ItemDataContext context = new ItemDataContext(_connectionString))
{
//....
var results = new List<Item>();
foreach (Item it in contextItems)
{
results.Add(it);
}
return results;
}
}
public void LoadData()
{
Observable.Start(()=>FetchData())
.ObserveOnDispatcher()
.Subscribe(list=>
{
foreach (Item it in contextItems)
{
this.Checklist.Add(it);
}
});
}
代码不适合单元测试,但看起来你不感兴趣(静态成员,带有DB连接的VM等等),所以这可能会起作用吗?!
答案 1 :(得分:0)
我自己就碰到了这个。有多种方法可以到达调度员,我必须使用以下内容。我直接从我的ViewModels中调用它(不从外部传入)。
Application.Current.Dispatcher.Invoke(action);