2015-05-29 26 views
0

後,我有一些代碼,看起來像這樣:屬性值發送到服務器的穩定

class MyVM : VMBase { 
    public MyVM(IMyServerProxy proxy) { 
    _proxy = proxy; 
    _proxy.ValueChanged += OnValueChangedFromServer; 
    } 
    private void OnValueChangedFromServer(int value){ 
    _value = value; 
    RaisePropertyChanged(() => Value); 
    } 
    public int Value { // bound to slider 
    get { return _value; } 
    set { 
     _value = value; 
     // need something here to only send stable values to server 
     _proxy.ModifyValue(value); // async 
    } 
    } 
} 

問題是這樣的:值綁定到一個滑塊控件。該滑塊會觸發很多更改。我不想將所有這些發送到服務器。我只想發送穩定的價值。從本質上講,我想插入一些東西到價值設置器,只有在值沒有改變一整秒後才調用代理。 (我有一個次要的擔憂,即服務器會將過時的值更改回給我,但如果我只是延遲發送到服務器,我認爲這會大部分減少。)

我看着使用Task.Delay方法。但是,如果我取消延遲,則會引發異常,並且在每次更新時構建新的CancellationSource似乎也不是理想的選擇。有更好的方法嗎?

+0

@EugenePodskal,根據您的建議,第一次更改將始終發送。這也意味着一些財產正在定期設定,事實並非如此。我不認爲這會奏效。 – Brannon

回答

0

考慮了多種選擇後,其中的Rx擴展,我去了System.Threading.Timer類。當您調用其Change方法時,該類將重新啓動計時器。

字段:

private readonly System.Threading.Timer _valueUpdater; 
private bool _sendingValue; 
在構造

_valueUpdater = new System.Threading.Timer(OnSendValue, null, System.Threading.Timeout.Infinite, 0); 

回調:

private void OnSendValue(object state) 
{ 
    _proxy.ModifyValue(_value).Wait(); 
    _sendingValue = false; 
    if (_isDisposed) 
     _valueUpdater.Dispose(); 
} 

裝定:

_value = value; 
_sendingValue = true; 
_valueUpdater.Change(DelayMs, 0); 

破壞者:

private bool _isDisposed; 
public void Dispose() 
{ 
    _isDisposed = true; 

    if (!_sendingValue) 
     _valueUpdater.Dispose(); 
... 
1

該技術屬於被稱爲「事件合併」的邏輯範疇。也許最最小的佔用空間的實現方式如下:

class MyVM : VMBase { 
    private bool _isChangePending = false; 

    public MyVM(IMyServerProxy proxy) { 
    _proxy = proxy; 
    _proxy.ValueChanged += OnValueChangedFromServer; 
    } 

    private void OnValueChangedFromServer(int value){ 
    _value = value; 
    RaisePropertyChanged(() => Value); 
    } 

    public int Value { // bound to slider 
    get { return _value; } 
    set { 
     lock(_isChangePending) { 
     _value = value; 
     // only send send "stable" values to server 
     if (!_isChangePending){ 
      _isChangePending = true; 
      System.Threading.ThreadPool.QueueUserWorkItem(delegate { 
      this.SendAfterStabilize(value); 
      }, null); 
     } 
     } 
    } 
    } 

    private void SendAfterStabilize(int lastChangedValue) { 
    while (true) { 
     System.Threading.Thread.Sleep(1000); // control coalescing delay here 
     lock(_isChangePending) { 
     if (_value == lastChangedValue) { 
      _isChangePending = false; 
      _proxy.ModifyValue(lastChangedValue); // async 
      return; 
     } 
     else { 
      lastChangedValue = _value; 
     } 
     } 
    } 
    } 
} 

注意,lock() { }塊在技術上是必要的,以保證盡一切可能的最後變化(無論時間)總是由一秒鐘後到達服務器。如果刪除了lock() { }塊,代碼仍然可以在99.99%的時間內運行,但很少發生的最後一次更改可能無法發送到服務器(由於線程之間缺少內存訪問同步)。

1

在.NET框架4.5或更高版本,你可以在你的Slider控制使用BindingBase.Delay Property

<Slider Value="{Binding Value, Delay=1000}" 
+0

我不認爲這將適用於我的方案,因爲我有幾個其他文本字段綁定到值。我希望它們全部在滑塊移動時更新。我可以將這些值綁定到滑塊控件的值,而不是視圖模型Value,但我的置信度很低;綁定綁定已經讓我失望了很多次。 – Brannon