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

标签 c# multithreading compact-framework windows-phone-7 mvvm-light

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

我已经发现我无法从 WP7 应用程序异步调用委托(delegate)。

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

是否有任何示例代码可以在后台线程上运行一些代码,然后将事件分派(dispatch)回 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....
    }
}

最佳答案

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

您的 ViewModel 实现了 INotifyPropertyChanged 对吗?无需发送事件。只需在模型中“裸露”它们,然后在 ViewModel 中调度 RaisePropertyChanged。

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

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

鉴于此,这就是我在单例数据库对象中加载和返回我的 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");
}

你关注了吗?我在后台线程上反序列化 Tour 列表,然后引发 propertychanged 事件。

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

在 Viewmodel 的构造函数中挂接数据库事件:

public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}

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

private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if(e.PropertyName == "Tours")
  {
    LoadTourList();
  }
}

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

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

  RaisePropertyChanged( "AllTours" );
}

最后,在您的 ViewModelBase 中,最好检查您的 RaisePropertyChanged 是否需要分派(dispatch)。我的“SafeDispatch”方法与 MVVMlight 中的方法几乎相同:

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

这在我的代码中完美运行,我认为它相当整洁?

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

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

关于c# - 如何在 Windows Phone 7 的后台线程上运行函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3293137/

相关文章:

c# - Bitmap.LockBits "pin"位图是否存入内存?

c# - 如何在uwp中添加两个具有相同名称和单独代码文件的xaml页面

java - 使用什么类型的队列在工作线程之间分配作业

c# - 在 Windows Mobile 中如何使应用程序在设备启动时启动?

.net - 如何在.NET程序集中安全存储加密 key

c# - 如何创建签名证书并在生产中的IdentityServer4中使用它?

c# - 从 self 卸载 C# Windows 应用程序

java - isInterrupted() 未触发

python - 如何在自己的线程中扭曲运行并正确停止?

c# - 在 C# cf 中显示下一个表单后尝试关闭一个表单