[英]Limit binding updates per second
我目前正在创建一个程序,该程序读取通过 COM 端口发送的数据,然后将其绘制在图表中。 数据显示使用 MVVM 原理,当数据以 10Hz 左右发送时,该原理工作正常。 但是,正在从中读取数据的设备可以达到 1 kHz 的刷新率,这意味着每分钟 1000 个数据集。 这适用于显示和更新简单的文本框,但是它破坏了图表,因为更新发生得太快了。
我想我现在需要做的是限制发送到订阅类和页面的更新事件的数量,以便只发送有限数量的数据,这使图表有机会正确绘制。 有没有办法自动限制这种情况,或者您建议手动调整哪些代码?
我收藏中的一个小代码片段改变了事件:
void dataItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("dataItems");
NotifyPropertyChanged("lastItem");
// update any charts
NotifyPropertyChanged("AccelXData");
NotifyPropertyChanged("AccelYData");
NotifyPropertyChanged("AccelZData");
}
// handle property changes
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
每个数据集也有一个 ID,可以用来检查何时手动更新,作为一个想法。
更好的方法是在数据更改时删除对 NotifyPropertyChanged 的调用。
创建一个计时器并刷新计时器。 这样您就可以控制刷新率,并且不受数据到达率的限制。
这不是一个完整的答案,但需要注意:
我看到您正在从CollectionChanged
处理程序中执行NotifyPropertyChanged("dataItems")
。 我认为您不想这样做,这可能会导致性能问题。 dataItems
似乎是ObservableCollection<T>
类型的属性。 当集合发生更改时,集合本身会发送一个CollectionChanged
事件。 在您的 UI 中, ItemsControl
( ComboBox
、 ListBox
等)可能绑定到dataItems
属性。 当集合引发其CollectionChanged
事件时,您无法保证调用事件处理程序的顺序。 如果您的 UI 首先处理该事件,它可能会尝试为您的集合中的新/旧项目分配/取消分配容器和 UI 元素。 当您手动调用NotifyPropertyChanged("dataItems")
,UI 可能会丢弃所有 UI 元素并重新构建它们(取决于 UI 元素是否足够智能以识别值未更改,也取决于容器回收逻辑) . 这(显然)是低效的。 除非PropertyChanged
的返回值/对象发生更改,否则永远不要发送PropertyChanged
通知。
进行此更改并让我们知道是否有任何重大影响。
现有的两个答案都提出了有效的观点,因为这正在成为一个重复的起源,是时候添加一些更多的上下文,这可能会使这与未来的重复引用更相关
在 MVVM 世界中,这是一种常见模式,尤其是当您定义了一些解析表达式且不受属性支持的只读属性时。 在正常使用情况下,强制调用NotifyPropertyChanged()
可能不会引起关注,但是当您增量加载大型记录集或对集合执行操作时,禁用更新直到操作结束可能会很有用:
/// <summary>
/// Example of a readonly expression backed property,
/// created to simplify MVVM bindings.
/// </summary>
public object lastItem { get => dataItems?.Last(); }
/// <summary>Flag to disable <see cref="NotifyPropertyChanged(string)"/> for collection related fields</summary>
private bool suppressCollectionUpdates = false;
void dataItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (!suppressCollectionUpdates)
{
NotifyPropertyChanged(nameof(dataItems));
NotifyPropertyChanged(nameof(lastItem));
// update any charts
NotifyPropertyChanged(nameof(AccelXData));
NotifyPropertyChanged(nameof(AccelYData));
NotifyPropertyChanged(nameof(AccelZData));
}
}
/// <summary>
/// A long running operation that causes the UI to update too frequently
/// </summary>
void Operation()
{
suppressCollectionUpdates = true;
try
{
... Do long running or incremental changes to the dataItems
}
finally
{
// leave it back in the default state
suppressCollectionUpdates = false;
// Call the change event manually, use the Reset value to indicate a dramatic change ocurred.
// You could also send null because our code does not use these references anyway ;)
dataItems_CollectionChanged(dataItems, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
}
}
// handle property changes
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
如果这是 MVVM,您可能会将某种进度微调器或其他形式的视觉反馈绑定到这个suppressCollectionUpdates
集合IsBusy
,在这种情况下,您将标志命名为更合适的名称,例如IsBusy
或IsLoading
并使用支持字段进行设置并调用NotifyPropertyChanged
.
长时间运行的操作和高频更改的另一个选择是您可以引入一个计时器来定期调用刷新:
private void notifyCollectionChangeTimer_Tick(object sender, EventArgs e)
{
suppressCollectionUpdates = false;
dataItems_CollectionChanged(dataItems, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
suppressCollectionUpdates = true;
}
/// <summary>
/// A long running operation that causes the UI to update too frequently
/// </summary>
/// <remarks>A timer will call refresh periodically</remarks>
void Operation()
{
suppressCollectionUpdates = true;
DispatcherTimer timer = new DispatcherTimer();
try
{
timer.Interval = TimeSpan.FromSeconds(1); // how often to refresh the UI
timer.Tick += notifyCollectionChangeTimer_Tick;
timer.Start();
... Do long running or incremental changes to the dataItems
}
finally
{
_refreshTimer.Stop(); // stop timer
_refreshTimer.Tick -= OnTick; // unsubscribe from timer's ticks (just in case you move the timer to a parent scope ;)
// leave it back in the default state
suppressCollectionUpdates = false;
// Call the change event manually, use the Reset value to indicate a dramatic change ocurred.
// You could also send null because our code does not use these references anyway ;)
dataItems_CollectionChanged(dataItems, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.