2017-07-25 57 views
1

我在.NET 3.5和Visual Studio 2008下有一个WPF MVVM应用程序。视图中的一些控件绑定到视图模型的属性,所以当这些属性发生变化,它将通过INotifyPropertyChanged的实现通知给视图。MVVM:在完成WPF NET 3.5中的线程池工作项后更新视图的控件可见性

我开始时会在窗口中央出现一些说“正在加载...”的闪屏,并且在从数据库请求某些数据时它会保持可见状态。一旦从数据库请求数据,我想要隐藏这种飞溅。

此飞溅在视图模型中绑定到一个属性“IsSplashVisible”,因此将属性更新为true,通知在beginnig中显示的飞溅并将其设置为false,通知要隐藏的飞溅。

在开始时将属性“IsSplashVisible”设置为true是没有问题的,当排队的工作项目完成时将属性设置为false时会出现问题。一旦将此属性设置为false,将通知控件(splash“正在加载...”),并尝试隐藏但失败,因为这是创建它的人的不同线程,因此引发了典型的异常。那我该如何解决这个问题?

下面的代码。

视图模型

public class TestViewModel : BaseViewModel 
{ 
    private static Dispatcher _dispatcher; 
    public ObservableCollection<UserData> lstUsers 

    public ObservableCollection<UserData> LstUsers 
    { 
     get 
     { 
      return this.lstUsers; 
     } 

     private set 
     { 
      this.lstUsers= value; 
      OnPropertyChanged("LstUsers"); 
     } 
    } 

    private bool isSplashVisible = false; 

    public bool IsSplashVisible 
    { 
      get 
      { 
       return this.isSplashVisible; 
      } 

      set 
      { 
       if (this.isSplashVisible== value) 
       { 
        return; 
       } 

       this.isSplashVisible= value; 
       OnPropertyChanged("IsSplashVisible"); 
      } 
    } 

    public TestViewModel() 
    { 
     this.IsSplashVisible = true; 

     ThreadPool.QueueUserWorkItem(new WaitCallback((o) => 
     { 
      var result = getDataFromDatabase(); 
      UIThread(() => 
      { 
       LstUsers = result; 
       this.IsSplashVisible = false; <---- HERE IT FAILS 
      });    
     })); 
    } 

    ObservableCollection<UserData> getDataFromDatabase() 
    {    
     return this.RequestDataToDatabase(); 
    } 

    static void UIThread(Action a) 
    { 
     if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher; 
     //this is to make sure that the event is raised on the correct Thread 
     _dispatcher.Invoke(a); <---- HERE EXCEPTION IS THROWN 
    } 
} 
+0

像@Clemens说,在你的代码把'Application.Current.Dispatcher'纳入这一点,所以它看起来像这样'如果(_dispatcher == NULL)_dispatcher = Application.Current.Dispatcher;' – XAMlMAX

+0

@ XAMlMAX除了根本不具备该领域的意义。 *更好*总是调用'Application.Current.Dispatcher.Invoke(...)'。 – Clemens

+0

虽然它可能很牵强,但理论上可以在两个不同的UI线程中创建两个视图模型实例。然后,一个静态的Dispatcher字段将无法正常工作。 – Clemens

回答

4

Dispatcher.CurrentDispatcher不是UI线程的调度,因为它

获取调度当前正在执行的线程,并创建一个新的调度,如果一个是尚未与线程关联。

您应该使用当前应用程序实例的调度:

ThreadPool.QueueUserWorkItem(o => 
{ 
    var result = getDataFromDatabase(); 

    Application.Current.Dispatcher.Invoke(() => 
    { 
     LstUsers = result; 
     IsSplashVisible = false; 
    });    
}); 

假设你TestViewModel构造函数被调用在UI线程,你可以写它像如下所示,其中Dispatcher.CurrentDispatcher在UI线程中调用,而不是ThreadPool线程。但是,这个领域完全是多余的。你总是可以拨打Application.Current.Dispatcher.Invoke()

public class TestViewModel 
{ 
    private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher; 

    public TestViewModel() 
    { 
     IsSplashVisible = true; 

     ThreadPool.QueueUserWorkItem(o => 
     { 
      var result = getDataFromDatabase(); 

      _dispatcher.Invoke(() => 
      { 
       LstUsers = result; 
       IsSplashVisible = false; 
      });    
     }); 
    } 

    ... 
} 
相关问题