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

C# 如何在Windows Phone 7的后台线程上运行函数?,c#,multithreading,compact-framework,windows-phone-7,mvvm-light,C#,Multithreading,Compact Framework,Windows Phone 7,Mvvm Light,我正在使用MVVMLight构建一个WP7(WindowsPhone7)应用程序。我希望模型执行的所有工作都在后台线程上运行。然后,当工作完成时,引发一个事件,以便ViewModel可以处理数据 我已经发现我不能从WP7应用程序异步调用委托 目前,我正在尝试使用ThreadPool.QueueUserWorkItem()在后台线程上运行一些代码,并使用MVVM Light的DispatcherHelper.CheckBeginInvodeOnUI()在UI线程上引发一个事件,以向ViewMode

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

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

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

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

提前感谢,, 杰夫

编辑-这是一个示例模型

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....
    }
}
公共类数据模型
{
公共事件事件处理程序数据加载完成;
公共事件事件处理程序DataLoadingError;
列表_dataCasch=新列表();
public void GetData()
{
ThreadPool.QueueUserWorkItem(func=>
{
尝试
{
LoadData();
如果(DataLoadingComplete!=null)
{
//将完成事件分派回UI线程
DispatcherHelper.CheckBeginInvokeOnUI(()=>
{
//升起事件
DataLoadingComplete(这是新的DataLoadingEventArgs(_dataCasch));
});
}
}
捕获(例外情况除外)
{
if(DataLoadingError!=null)
{
//将错误事件分派回UI线程
DispatcherHelper.CheckBeginInvokeOnUI(()=>
{
//提出错误
DataLoadingError(这是新的DataLoadingErrorEventArgs(ex));
});
}
}
});
}
私有void LoadData()
{
//请执行加载数据的操作。。。。
}
}

我以前没有为WP7开发过,但我发现了

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

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);
        });
}
公共晚餐视图模型(目录)
{
目录=目录;
Catalog.dinner加载完成+=
新事件处理程序(
晚餐(晚餐装载完成);
}
公众聚餐()
{
Catalog.GetDinners();
}
无效晚餐\u晚餐装载完成(
对象发送方,晚餐加载事件参数(e)
{
//UI线程上的触发事件
View.Dispatcher.BeginInvoke(()=>
{
//清除列表
theDinners.Clear();
//添加新的晚餐
foreach(晚餐d在e.Results中)
添加(d);
if(LoadComplete!=null)
LoadComplete(此为空);
});
}
我希望这会有帮助:)


有一件事令人困惑:您说过,当您使用helper引发事件时,VS2010会崩溃。。。当它崩溃时,你到底看到了什么?您是否遇到了异常情况?

以下是我如何解决此问题的方法

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

是的,您的代码中应该有某种单例模型/数据库。毕竟,如果不是某个巨大的单例数据库,那么什么是SQL数据库呢?因为WP7中没有数据库,所以不要羞于创建单例对象。我有一个叫做“数据库”:

我刚刚尝试在那里线程化我的数据加载,并意识到事实上最好的方法就是在模型级别直接实现INotifyPropertyChanged

鉴于此,我在singleton数据库对象中加载并返回我的Tours“表”(注意thread.sleep,以使加载花费明显的时间,通常为100毫秒)。数据库类现在实现INotifyPropertyChanged,并在加载完成时引发事件:

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");
}
听一听相应的桌子变化(我们喜欢魔术弦!;-):

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

public void LoadTourList()
{
  AllTours = ( from t in Database.Instance.Tours
    select new TourViewModel( t ) ).ToList();

  RaisePropertyChanged( "AllTours" );
}
最后,在ViewModelBase中,最好检查RaisePropertyChanged是否需要分派。我的“SafeDispatch”方法与MVVMlight中的方法基本相同:

private void RaisePropertyChanged(string property)
{
  if ( PropertyChanged != null )
  {
    UiHelper.SafeDispatch(() =>
      PropertyChanged(this, new PropertyChangedEventArgs(property)));
  }
}
这在我的代码中工作得很好,我认为它相当整洁

最后,为专家提供额外信息:在WP7中,最好在页面中添加一个IsIndeterminate=True的进度条-这将显示“虚线”进度条。然后,您可以做的是,当ViewModel首次加载时,您可以将“ProgressBarVisible”属性设置为Visible(并引发关联的PropertyChanged事件)。将ProgressBar的可见性绑定到此ViewModel属性。当数据库PropertyChanged事件激发时,将可见性设置为Collapsed以使progressbar消失


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

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

总结如下:

1) 我从
ViewModelBase
导出了我的模型(是我的模型)。这给了我Mvvm Light的消息传递和INotifyPropertyChanged的实现,这很方便。你可以说这不是“纯粹的”,但我认为这无关紧要

2) 我使用Mvvm Light
DispatcherHelper.CheckBeginInvokeOnUI
helper和您一样(来自我的模型,而不是我的ViewModel)

希望这有帮助

我是
public void LoadTourList()
{
  AllTours = ( from t in Database.Instance.Tours
    select new TourViewModel( t ) ).ToList();

  RaisePropertyChanged( "AllTours" );
}
private void RaisePropertyChanged(string property)
{
  if ( PropertyChanged != null )
  {
    UiHelper.SafeDispatch(() =>
      PropertyChanged(this, new PropertyChangedEventArgs(property)));
  }
}