I have a WPF MVVM app under NET 3.5 and Visual Studio 2008. Some controls in the view are bound to properties on the view model, so when these properties changes it will be notified to the view through the implementation of INotifyPropertyChanged.
I have some kind of splash saying "Loading..." that appears in the center of the window at the beginning, and it keeps visible while some data is being requested from database. Once data is requested from database, I want to hide this splash.
This splash is bound to a propert, "IsSplashVisible" in view model so updating the property to true, notify the splash to be shown at the beginnig and setting it to false, notify the splash to be hidden.
Setting property "IsSplashVisible" to true at the beginning there is no problem, the problem appears when setting the property to false once queued work item finishes. Once set this property to false, control (splash "Loading...") is notified and it tries to hide but fails as this is a different thread that the one who created it so the typical exception is thrown. So how can I solve this?
Below the code.
View model:
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
}
}
Dispatcher.CurrentDispatcher
is not the Dispatcher of the UI thread, because itYou should use the Dispatcher of the current Application instance:
Assuming that your TestViewModel constructor is called in the UI thread, you could have written it like shown below, where
Dispatcher.CurrentDispatcher
is called in the UI thread instead of a ThreadPool thread. However, the field is entirely redundant. You could always just callApplication.Current.Dispatcher.Invoke()
.