更新
我将问题分解为其核心组件,并在另一个问题中将更短的最小工作示例隔离开来:
Observable.Interval not updating UI with expected frequency
我需要将一系列图像(从文件夹中读取)显示为"电影",以一个固定的,先前已知的帧率。
我在WPF中有一个Image控件,其Source被数据绑定到ViewModel属性Image
。然后,我使用Observable.Interval
事件源及时更新该图像。对于每个已用时间间隔,系统会调用UpdateImage
,这会更新Task.Run()
来电中的图像。
我的问题是:当我通过实验增加有效帧速率(取决于实际帧速率以及播放速度)时,播放保持正常速度上升给定值。高于该值,它开始看起来更慢。
我认为这与RaisePropertyChanged
电话有关,但我不确定。我尝试使用SubscribeOnDispatcher
(或者说它ObserveOnDispatcher
?)但是无论如何它都没有效果。
问题是: - 我做错了什么? - 我如何调查并解决问题?
更新:
值得一提的是Image
调用CreateImageForIndex()
的getter,这是一种可能具有非平凡成本的方法。是否可以" async"它?
ViewModel(部分):
CancellationTokenSource _cancelPlay;
double _speed;
public void Play(double speed)
{
_speed = speed;
_cancelPlay = new CancellationTokenSource();
Observable.Interval(TimeSpan.FromSeconds(1.0 / (Math.Abs(speed) * Exame.Model.Configurações.FrameRate)))
.Subscribe(t => ExecuteRun(), _cancelPlay.Token);
}
public void Stop()
{
_cancelPlay?.Cancel();
}
void ExecuteRun()
{
Task.Run(() =>
{
Index = Math.Max(0, Math.Min(_model.MaxIndex, Index + 1 * Math.Sign(_speed)));
});
}
public int Index
{
get { return _model.Index; }
set
{
_model.Index = value;
RaisePropertyChanged(null); // this tells view to get a new value for `Image` too!
}
}
public ImageSource Image => _model.CreateImageForIndex(Index);
答案 0 :(得分:0)
我跟随@Enigmativity的提示(在我发布的另一个答案中),在Windows中,计时器的分辨率最多为15ms。
所以我测试了另一种策略:旋转一个带有“当经过时间”状态的循环,类似于游戏循环,用Stopwatch
测量时间,现在一切正常。
public void Play(double speed)
{
_speed = speed;
_cancelPlay?.Cancel();
_cancelPlay = new CancellationTokenSource();
Task.Run(() => PlayLoop(_cancelPlay.Token), _cancelPlay.Token);
}
void PlayLoop(CancellationToken token)
{
var sw = Stopwatch.StartNew();
double previous = sw.ElapsedMilliseconds;
double timeInterval = 1000.0 / (Math.Abs(_speed) * _exame.Model.Configurações.FrameRate);
while (!token.IsCancellationRequested)
{
var current = sw.ElapsedMilliseconds;
var elapsed = current - previous;
if (elapsed > timeInterval)
{
Índice = Math.Max(0, Math.Min(ÍndiceMáximo, Índice + 1 * Math.Sign(_speed)));
previous = current;
}
}
}