我有一些看起来像这样的代码:
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
}
}
}
问题是:值被限制为滑块控件。该滑块触发了很多变化。我不想将所有这些发送到服务器。我只想发送稳定值。本质上,我想在Value setter中插入一些东西,它只在Value没有改变一整秒后才调用代理。 (我有一个次要问题,即服务器会将过时的值更改发回给我,但我认为如果我只是延迟发送到服务器,那么这大部分都会减轻。)
我研究过使用Task.Delay
方法。但是,如果我取消延迟,它会引发异常,并且在每次更新时构建新的CancellationSource
似乎也不理想。还有更好的方法吗?
答案 0 :(得分: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 :(得分:1)
在.NET Framework 4.5或更高版本中,您可以在Slider
控件中使用BindingBase.Delay Property:
<Slider Value="{Binding Value, Delay=1000}"
答案 2 :(得分: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();
...