每秒限制绑定更新

时间:2014-03-24 21:52:57

标签: c# wpf mvvm

我目前正在创建一个程序,用于读取通过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,可用于检查何时手动更新,作为一个想法。

3 个答案:

答案 0 :(得分:2)

更好的方法是在数据发生变化时删除对NotifyPropertyChanged的调用。

创建计时器并刷新计时器。这样您就可以控制刷新率,并且它不受数据到达速率的约束。

答案 1 :(得分:1)

这不是一个完整的答案,但有些注意事项:

我发现您在NotifyPropertyChanged("dataItems")处理程序中正在执行CollectionChanged。我不认为你想这样做,这可能会导致性能问题。 dataItems似乎是ObservableCollection<T>类型的属性。更改集合后,集合本身会发送CollectionChanged事件。在您的用户界面中,ItemsControlComboBoxListBox等)可能绑定到dataItems属性。当集合引发其CollectionChanged事件时,您无法保证调用事件处理程序的顺序。如果您的UI首先处理事件,它可能会尝试为集合中的新旧项目分配/取消分配容器和UI元素。当您手动调用NotifyPropertyChanged("dataItems")时,UI可能会丢弃所有UI元素并重新构建它们(取决于UI元素是否足够智能以识别值未发生更改,并且还取决于容器回收逻辑)。这显然是效率低下的。除非属性的返回值/对象发生变化,否则不要发送PropertyChanged通知。

进行此更改并告知我们是否有任何重大影响。

答案 2 :(得分:0)

<块引用>

现有的两个答案都提出了有效的观点,因为这正在成为一个重复的起源,是时候添加一些更多的上下文,这可能会使这与未来的重复引用更相关

在 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 或 {{ 1}} 并使用支持字段进行设置并调用 IsLoading

对于长时间运行的操作和高频更改的另一种选择是,您可以引入一个计时器来定期调用刷新:

NotifyPropertyChanged

类似问题和进一步阅读: