首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Windows Phone 7的后台线程上运行函数?

如何在Windows Phone 7的后台线程上运行函数?
EN

Stack Overflow用户
提问于 2010-07-21 02:32:31
回答 3查看 14.2K关注 0票数 11

我正在使用MVVM Light构建一个WP7 (Windows Phone7)应用程序。我希望由模型执行的所有工作都在后台线程上运行。然后,当工作完成时,引发一个事件,以便ViewModel可以处理数据。

我已经发现,我不能从WP7应用程序异步调用委托。

目前,我正在尝试使用ThreadPool.QueueUserWorkItem()在后台线程上运行一些代码,并使用MVVM Light的DispatcherHelper.CheckBeginInvodeOnUI()在UI线程上引发一个事件,向ViewModel发出数据已加载的信号(当VS2010和Blend 4试图显示设计时视图时,这会导致它们崩溃)。

是否有任何示例代码可以在后台线程上运行一些代码,然后将事件分派回WP7应用程序的UI线程?

提前谢谢你,杰夫。

编辑-这是一个示例模型

代码语言:javascript
复制
public class DataModel
{
    public event EventHandler<DataLoadingEventArgs> DataLoadingComplete;
    public event EventHandler<DataLoadingErrorEventArgs> DataLoadingError;
    List<Data> _dataCasch = new List<Data>();

    public void GetData()
    {
        ThreadPool.QueueUserWorkItem(func =>
        {
            try
            {
                LoadData();
                if (DataLoadingComplete != null)
                {
                    //Dispatch complete event back to the UI thread
                    DispatcherHelper.CheckBeginInvokeOnUI(() =>
                    {
                       //raise event 
                        DataLoadingComplete(this, new DataLoadingEventArgs(_dataCasch));
                    });
                }
            }
            catch (Exception ex)
            {
                if (DataLoadingError != null)
                {
                    //Dispatch error event back to the UI thread
                    DispatcherHelper.CheckBeginInvokeOnUI(() => 
                    {
                        //raise error
                        DataLoadingError(this, new DataLoadingErrorEventArgs(ex));
                    });
                }
            }
        });
    }

    private void LoadData()
    {
        //Do work to load data....
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-07-22 04:52:41

下面是我如何解决这个问题的方法。

您的ViewModel实现了INotifyPropertyChanged,对吗?没有必要分派事件。只需在模型中将它们提升为“空的”,然后在ViewModel中分派RaisePropertyChanged。

是的,您的代码中应该包含某种类型的单例模型/数据库。毕竟,如果不是一些巨大的单例数据库,那么SQL数据库又是什么呢?因为我们在WP7中没有数据库,所以不要害怕创建一个单例对象。我有一个名为"Database“的数据库:)

我刚刚尝试将我的数据加载线程化,并意识到实际上最好的方法就是直接在模型级别上实现INotifyPropertyChanged。There's no shame in this

因此,考虑到这一点,我在单例数据库对象中执行以下操作来加载和返回我的Tour“表”(请注意thread.sleep,使其加载所需的时间可见,通常不超过100ms)。数据库类现在实现了INotifyPropertyChanged,并在加载完成时引发事件:

代码语言:javascript
复制
public ObservableCollection<Tour> Tours
{
  get
  {
    if ( _tours == null )
    {
      _tours = new ObservableCollection<Tour>();
      ThreadPool.QueueUserWorkItem(LoadTours);
    }
    return _tours;
  }
}

private void LoadTours(object o)
{
  var start = DateTime.Now;
  //simlate lots of work 
  Thread.Sleep(5000);
  _tours = IsoStore.Deserialize<ObservableCollection<Tour>>( ToursFilename ) ??  new ObservableCollection<Tour>();
  Debug.WriteLine( "Deserialize time: " + DateTime.Now.Subtract( start ).ToString() );
  RaisePropertyChanged("Tours");
}

你明白了吗?我在后台线程上反序列化Tour列表,然后引发一个propertychanged _ raising事件。

现在在ViewModel中,我想要一个要绑定到的TourViewModels列表,一旦看到Tours表发生了变化,我就用linq查询选择它。在ViewModel中监听数据库事件可能有点便宜--将其封装在模型中可能“更好”,但让我们不要做我们不需要的工作,嗯?

在Viewmodel的构造函数中挂钩Database事件:

代码语言:javascript
复制
public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}

监听适当的表更改(我们喜欢魔术字符串!;-) ):

代码语言:javascript
复制
private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if(e.PropertyName == "Tours")
  {
    LoadTourList();
  }
}

从表中选择我想要的记录,然后告诉视图有新数据:

代码语言:javascript
复制
public void LoadTourList()
{
  AllTours = ( from t in Database.Instance.Tours
    select new TourViewModel( t ) ).ToList();

  RaisePropertyChanged( "AllTours" );
}

最后,在您的ViewModelBase中,最好检查您的RaisePropertyChanged是否需要调度。我的"SafeDispatch“方法与MVVMlight中的方法几乎相同:

代码语言:javascript
复制
private void RaisePropertyChanged(string property)
{
  if ( PropertyChanged != null )
  {
    UiHelper.SafeDispatch(() =>
      PropertyChanged(this, new PropertyChangedEventArgs(property)));
  }
}

这在我的代码中工作得很好,而且我认为它相当整洁?

最后,对于专家的额外提示:在WP7中,在页面中添加一个带有IsIndeterminate=True的ProgressBar可能会更好--这将显示“虚线”进度条。然后,当ViewModel第一次加载时,您可以将"ProgressBarVisible“属性设置为Visible (并引发相关的PropertyChanged事件)。将ProgressBar的可见性绑定到此ViewModel属性。当数据库PropertyChanged事件触发时,将可见性设置为折叠,以使进度条消失。

这样,当反序列化运行时,用户将在其屏幕顶部看到"IsIndeterminate“进度条。好的!

票数 16
EN

Stack Overflow用户

发布于 2010-07-21 03:41:41

我以前没有为WP7开发过,但我找到了this article that might be useful

以下是本文中的Dining Philosopher示例代码,它可以让您很好地了解如何从另一个线程向UI引发事件:

代码语言:javascript
复制
public DinnersViewModel(IDinnerCatalog catalog)
{
    theCatalog = catalog;
    theCatalog.DinnerLoadingComplete +=
        new EventHandler<DinnerLoadingEventArgs>(
              Dinners_DinnerLoadingComplete);
}

public void LoadDinners()
{
    theCatalog.GetDinners();
}

void Dinners_DinnerLoadingComplete(
    object sender, DinnerLoadingEventArgs e)
{
    // Fire Event on UI Thread
    View.Dispatcher.BeginInvoke(() =>
        {
            // Clear the list
            theDinners.Clear();

            // Add the new Dinners
            foreach (Dinner d in e.Results)
                theDinners.Add(d);

            if (LoadComplete != null)
                LoadComplete(this, null);
        });
}

我希望它能对您有所帮助:)。

有一件事让人困惑:你说当你使用帮助器来引发事件时,VS2010就会崩溃……当它崩溃的时候,你到底看到了什么?你是不是遇到了异常?

票数 0
EN

Stack Overflow用户

发布于 2010-09-09 04:52:12

杰夫,我自己还在想这件事。我发布了一个类似的问题,并通过构建一个简单的样本来亲自回答。这里:

A super-simple MVVM-Light WP7 sample?

1)我从ViewModelBase派生了我的模型(是的,我的模型)。这为我提供了Mvvm-Light的消息传递和INotifyPropertyChanged实现,这很方便。你可能会说这不是“纯粹的”,但我认为这无关紧要。

2)我和你一样使用了Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUIhelper (来自我的模型,而不是我的ViewModel)。

希望这能有所帮助。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3293137

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档