Изображение обновляется гораздо реже, чем состояние ViewModel. Можно ли это пофиксить?
Лет 10 не писал на WPF, да и не сильно хорошо тогда его изучил. И тут понадобилось опять на нём написать задачку. Нагородил возможно велосипедов, хотелось бы разобраться, как сделать по уму.
В общем, в XAML есть сторонний контрол для быстрого отображения графиков ScottPlot в варианте для WPF. Он довольно ограниченный и не умеет жить во ViewModel, а только в code behind. Я не разобрался, как можно обновить что-то в code behind из View Model, поэтому во View Model делаются какие-то расчёты, а в code behind висит DispatcherTimer, по тикам таймера проверяется, выставил ли я флажок во ViewModel, и если выставил, то происходит отрисовка в контроле. В целом это выглядит в упрощённом виде так:
Slider перемещается
|
V
обновляются данные ViewModel
|
V
отрисовка контрола в code behind
При этом реакция на движение слайдера выглядит какой-то дёрганной. Даже если таймер запускать вообще без задержки. Стал разбираться, добавил статус бар, в котором стал писать всякую статистику. Выяснилось, что расчёты выполняются меньше чем за 1мс, а рендер контрола в code behind 5мс. При этом, само обращение к расчётам происходит раз в 10 чаще, чем рендер. Если уменьшать задержку таймера, то соотношение рендеров к расчётам становится чуть лучше, но не намного. Либо сам таймер срабатывает тоже с какой-то задержкой, либо я ещё чего-то не понимаю.
Итак, вопросы:
- Может есть какой-то другой способ инициировать обновление контрола
ScottPlot.WPFвcode behind, без участия таймера? Как правильно послать сигнал изViewModelвcode behind? - Либо может как-то
DispatcherTimerпочинить/заменить, чтобы он более часто срабатывал? Да хорошо ли это будет для приложения в целом - всё время проверять вcode behindкакой-то флаг без остановки? В то время, когда есть всякие биндинги казалось бы специально для этого. - Либо я вообще всю концепцию неправильно понимаю и нужно всё как-то совсем по-другому делать. Может есть какой-то пример правильного кода для
WPF, который что-то графическое отрисовывает, реагируя наSliderили другой подобный контрол в реальном времени без задержек?
Конечно, если бы этот контрол дружил с WPF, то можно было бы его забиндить на поле во ViewModel и он бы сам сразу отображался. Со ScottPlot.WPF так, к сожалению, нельзя сделать, об этом прямо в документации написано, что это сознательное решение. Рисуем быстро, но с широким функционалом WPF не дружим.
Ещё есть вариант в XAML повесить Image, а во ViewModel через функции ScottPlot делать рендер в bmp и потом подставлять готовую картинку в этот Image, я так ещё один контрол уже сделал. Но тогда теряются фичи ScottPlot.WPF по зуму и скроллированию графиков. Может они мне и не нужны будут, эти фичи, но пока не хотел уходить совсем в этот вариант.
Да, и ещё конечно можно повесить в code behind обработчики событий слайдера и делать все пересчёты там, а следом рендер. Но как-то не хотелось бы на такой низкий уровень совсем спускаться от биндингов без крайней необходимости.
Ответы (1 шт):
Зачем вообще рендер по таймеру?
Берёте INPC вьюмодель, реальзуете вызов события ProperyChanged, подписываетесь на него в юзерконтроле, и на каждое изменение любого свойства тригерите рендер, сами из кода.
public class MyViewModel : INotifyPropertyChanged
{
private bool _myFlag;
public bool MyFlag
{
get => _myFlag;
set
{
if (_myFlag != value)
{
_myFlag = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Теперь подписаться
MyViewModel vm = new();
vm.PropertyChanged += OnVmPropertyChanged;
private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(MyViewModel.MyFlag))
{
Render();
}
}
Если нужно отследить добавление/удаление элементов ObservableCollection, так же подпишитесь на CollectionChanged у неё.
Только подписываясь на события не забудьте отписываться, когда контрол покидает визуальное дерево или диспозится.