[英]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.