Не получается обновить свойство в ViewModel и соответственно View через событие
Есть класс в котором генерируется событие:
public class StringGenerator
{
public event Action<string> StringEvent;
// Функционал активирующий StringEvent
}
Есть класс который подписан на это событие и два свойства типа string и ObservableCollection<string>, которые обновляются этим событием (см конструктор):
public class StringProvider : INotifyPropertyChanged
{
private ObservableCollection<string> _stringCollection;
private string _textMessage;
private StringGenerator _stringGenerator;
public ObservableCollection<string> StringCollection
{
get { return _stringCollection; }
set
{
_stringCollection = value;
OnPropertyChanged(nameof(StringCollection));
}
}
public string TextMessage
{
get { return _textMessage; }
set
{
_textMessage = value;
OnPropertyChanged(nameof(TextMessage));
}
}
public StringProvider()
{
TextMessage = "XXXXXXX";
StringCollection = new ObservableCollection<string>();
_stringGenerator = new StringGenerator();
// Подписка на StringEvent
_stringGenerator.StringEvent += param =>
{
TextMessage = param;
StringCollection.Add(param);
};
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Есть промежуточный класс для реализации MVVM:
public interface IDataService
{
StringProvider StringProviderLocal { get; set; }
}
public class DataService:IDataService
{
private StringProvider _stringProviderLocal;
public StringProvider StringProviderLocal
{
get { return _stringProviderLocal; }
set { _stringProviderLocal = value; }
}
public DataService()
{
_stringProviderLocal = new StringProvider();
}
}
Есть ViewModel в которой есть экземпляр DataService и свойства типа string и ObservableCollection<string> которые по задумке должны обновляться при срабатывании StringEvent.
public class ViewModelLocal : INotifyPropertyChanged
{
private DataService _dataService;
public ObservableCollection<string> StringCollection
{
get { return _dataService.StringProviderLocal.StringCollection; }
set
{
_dataService.StringProviderLocal.StringCollection = value;
OnPropertyChanged(nameof(StringCollection));
}
}
public string TextMessage
{
get { return _dataService.StringProviderLocal.TextMessage; }
set
{
_dataService.StringProviderLocal.TextMessage = value;
OnPropertyChanged(nameof(TextMessage));
}
}
public ViewModelLocal()
{
_dataService = new DataService();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Есть View где реализуется Binding:
<DataGrid ItemsSource="{Binding StringCollection}"/>
<TextBlock Text = "{Binding TextMessage}" />
При запуске приложения TextBlock Text содержит "XXXXXXX". Все как и должно быть.
Однако, после срабатывания события StringEvent обновление ObservableCollection<string> StringCollection происходит в вот обновление string TextMessage нет.
Любые попытки изменить значение TextMessage внутри класса StringProvider:
TextMessage = "UUUUUUU";
не приводят к изменению данных во View
Как решить эту проблему?
Обновление коллекции происходит не потому, что вы написали там OnPropertyChanged(nameof(StringCollection)), а потому что коллекция реализует интерфейс INotifyCollectionChanged, т.е. она выдаёт уведомление об изменении при добавлении / замене / удалении элементов внутри коллекции.
Вы можете убирать OnPropertyChanged из сеттера коллекции, т.к. он отработает только в том случае, если вы сделаете StringCollection = new ObservableCollection.
Попробуйте убрать класс DataService и напрямую взаимодействовать с элементами, вызывая обновления из вью-модели. В той части кода, которую вы показали, можно обойтись и без него. Вы только дублируете один и тот же код несколько раз
Поскольку вы предоставляете обёртку к StringProviderLocal.TextMessage, вы должны извещать пользователей вашей ViewModelLocal о том, что это свойство поменялось. Для этого вы должны подписаться на StringProviderLocal.PropertyChanged, и при приходе события отправлять собственное событие.
(Вариант с пробросом подписки внутрь StringProviderLocal выглядит намного хуже, и прибавляет ненужной связности между моделью и клиентами VM.)
Да, это всё будет работать лишь если никто не меняет _dataService.StringProviderLocal. У вас нет возможности выяснить, менялся ли StringProvider, и обновить подписку, т. к. DataService не реализует INPC.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей