Ответ 1
Вот как я подхожу к решению этого.
В вашей модели ViewModel реализовано право INotifyPropertyChanged? Там нет необходимости отправлять события. Просто поднимите их "голыми" в модели, а затем отправьте RaisePropertyChanged в ViewModel.
И да, в вашем коде должна быть какая-то модель/база данных singleton. В конце концов, что такое база данных SQL, если не какой-то гигантский синглтон? Поскольку у нас нет базы данных в WP7, не стесняйтесь создавать одноэлементный объект. У меня один называется "База данных":)
Я только что попробовал потопить мои dataloads там и понял, что на самом деле лучший подход - это просто реализовать INotifyPropertyChanged прямо на уровне модели. Нет никакого позора в этом.
Поэтому, учитывая то, что я делаю в объекте Singleton Database, загружать и возвращать таблицу "Путевые расходы" (обратите внимание на 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 в фоновом потоке, а затем создаю событие, измененное собственностью.
Теперь в ViewModel мне нужен список TourViewModels для привязки, который я выбираю с помощью запроса linq, как только вижу, что таблица Tours изменилась. Вероятно, немного дешево слушать событие Database в ViewModel - возможно, было бы "лучше" инкапсулировать это в модели, но пусть не делает работу, мы не нуждаемся в ней?
Захватите событие Database в конструкторе Viewmodel:
public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}
Слушайте соответствующее изменение таблицы (мы любим магические строки!;-)):
private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Tours")
{
LoadTourList();
}
}
Выберите записи, которые я хочу из таблицы, а затем скажу, что есть новые данные:
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 может быть полезно добавить ProgressBar с IsIndeterminate = True на вашу страницу - это отобразит индикатор "пунктирный". Тогда, когда вы впервые загрузите ViewModel, вы можете установить свойство "ProgressBarVisible" для Visible (и повысить связанное событие PropertyChanged). Привяжите видимость ProgressBar к этому свойству ViewModel. Когда событие Database PropertyChanged срабатывает, установите видимость на "Свернутый", чтобы ускорить работу.
Таким образом, пользователь будет видеть индикатор выполнения "IsIndeterminate" в верхней части экрана во время выполнения десериализации. Ницца!