Суть приложения, брать с сайта название фильма, закачивать картинки по названию, и устанавливать их на задний фон с названием фильма.
У меня проблема в том, что я не понимаю как правильно сделать, обновление view должно же происходить во viewmodel классе?!.
Но у меня стоит таймер в 5 секунд, который каждые 5 секунд обновляет картинку и название(берет это все из List)
И получается так, что 2 переменные CurrentFilmName,CurrentFilmPicture находятся в классе viewmodel. И чтобы установить в них значения, мне нужно сделать их статичными, либо передать класс в конструктор и с ним уже работать.
Как мне сделать правильно? Перекинуть метод таймера в класс viewmodel? Передать viewmodel в класс таймера? Или класс таймера, реализовать интерфейс INotifyPropertyChanged и из него, обновлять данные.
Main
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
VM viewmodel = new VM();
DataContext = viewmodel;
new TimerMain(viewmodel).StartScrollTimer();
}
}
ViewModel
class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
private string _filmname;
public string CurrentFilmName
{
get => _filmname;
set
{
_filmname = value;
OnPropertyChanged();
}
}
private string _picturepath;
public string CurrentPicturePath
{
get => _picturepath;
set
{
_picturepath = value;
OnPropertyChanged();
}
}
}
Model(Таймер)
class TimerMain
{
private Images images = new Images();
private List<Classes.Image> listimages = new List<Classes.Image>();
private int numbertick = 0;
private VM viewmodel;
public TimerMain(VM viewmodel)
{
this.viewmodel = viewmodel;
}
public void StartScrollTimer()
{
images.TakeImage += (Classes.Image img) => listimages.Add(img);
// Подписываемся на событие, когда скачивается картинка, и добавляем в массив Объектов Image(В нем хранится название, и ссылка на файл картинки)
// Запускаем парсинг фильмов, он парсит с сайта название фильма и ищет ему картинку, при успешном нахождении, скачивает, и уведомляет о новом, созданном Image
_ = Task.Run(async () => await images.ParsFilmsAndDownload());
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(async (object obj, EventArgs e) =>
{
while (listimages.Count < numbertick + 1)
await Task.Delay(25);
try
{
viewmodel.CurrentFilmName = listimages[numbertick].FilmName;
viewmodel.CurrentPicturePath = listimages[numbertick].FilmPicture;
}
catch (Exception)
{
viewmodel.CurrentFilmName = listimages[numbertick].FilmName;
viewmodel.CurrentPicturePath = listimages[numbertick = numbertick == 4 ? 0 : numbertick + 1].FilmPicture;
}
numbertick = numbertick == 4 ? 0 : numbertick + 1;
});
timer.Interval = new TimeSpan(0, 0, 5);
timer.Start();
}
}
Сколько статей и видео не пересматривал, до конца не пойму, как правильнее, решил у вас спросить. Пока мне кажется правильной идея, реализовать TimerMain : INotifyPropertyChanged там сделать переменные, и обновить от туда(но тогда не соответствую паттерну, viewmodel должен же обновлять view).
Изменения:
VMFilms
class VMFilms : INPC
{
private string filmName;
public string CurrentFilmName
{
get => filmName;
set
{
filmName = value;
OnPropertyChanged();
}
}
private string picturePath;
public string CurrentPicturePath
{
get => picturePath;
set
{
picturePath = value;
OnPropertyChanged();
}
}
}
INPC
class INPC : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}

DataContext = viewmodel- это не очень правильно, ибо View не должно быть ответственно за создание других слоев (читаем).INotifyPropertyChanged- вынесите в отдельный класс, который будет ответственен только за реализацию INPC, от него дальше и наследуйтесь. Именования: в C# принято писать каждое слово с заглавной буквы (SomeValue), если это приватное значение, то первая буква идет маленькой, но остальные по-прежнему с заглавной (someValue), то есть всякиеfilmnameдолжны бытьfilmName. – EvgeniyZ Jul 30 '21 at 09:59FilmViewModelи у него свойствоNameиPicturePath, вVMинициализируйте и запускайте логику по обновлению. Далее,new TimerMain(viewmodel).StartScrollTimer();- MVVM, это разделение всего на мало связанные слои, где View отдельный слой, который не знает что-либо про M и VM, как и Model не знает что-либо про VM и V. Почему вдруг у вас основным классом стал M слой? Пусть VM все собирает, инициализирует его и обращается к нужным объектам. – EvgeniyZ Jul 30 '21 at 10:05INotifyPropertyChanged, далее для всехVMунаследовал его. + У меня появиласьVMFilmsсFilmNameиFilmPicture. И тут я встал. Как мне это сделатьв VM инициализируйте и запускайте логику по обновлению. Логика по обновлению в самихFilmName, FilmPicture. Дальше идет проблема у меня сTimerMain, Вы предлагаете его инициализировать вVMи все, или его логику перенести вVMFilms? И последний вопрос, как мне дать переменные изVMFilmsвMain Timer, Наследование? Инициализация? – Houl Jul 30 '21 at 12:18как мне дать переменные из VMFilms в Main Timer- еще раз прочитайте мои комментарии и поймите, что "MVVM - это подход программирования, когда код делят на мало связанные друг с другом слои". У вас не должен Model слой знать чего либо про ViewModel. Если для его работы требуется некое значение, то делаете метод, который принимает это значение, но не VM слой целиком (прим:GetFilm(id: 154)). По поводу таймера - вот у вас есть слой Model - ваши данные, естьViewModel- слой, который общается с M подготавливает данные для V. Где должны быть обновления данных? Наверно в M слое? – EvgeniyZ Jul 30 '21 at 12:42FilmModel- некий класс, который содержит в себе данные фильма, в нем нужные свойства и запущенный таймер (или другой слушатель), по его "тику" будет вызываться событие (event), назовем его к примеруDataUpdated, это событие будет передавать "слушателям" новые данные (новая картинка, название итд), то естьDataUpdated?.Invoke(new FilmModel("Новое назване", "Новая картинка")). Дальше в главной VM (обычноMainViewModel), я бы проинициализировалFilmModelкак приватное свойство, подписался бы на событие и по его вызову реализовал VM свойство фильма. – EvgeniyZ Jul 30 '21 at 12:48