我正在对可能很慢的属性使用数据绑定。但是,我不想冻结UI。相反,我想使用新的任务库。
理想情况下,我的财产看起来像这样:
public string MyProperty
{
get
{
if (_cache != null)
return _cache;
var result = await SomeSlowFunction();
return result;
}
}
但是,这确实有效,因为属性永远不会异步。
有解决方案吗?
答案 0 :(得分:1)
我假设您已实施INotifyPropertyChanged
。那么也许这样的事情可以做到这一点:
private string _myProperty;
public string MyProperty
{
get
{
if (_myProperty != null)
return _myProperty;
MyProperty = Application.Current.Dispatcher.BeginInvoke((Action)(() => SomeSlowFunction()));
return string.Empty;
}
set
{
if (_myProperty == value) return;
_myProperty = value;
RaiseNotifyPropertyChanged("MyProperty");
}
}
答案 1 :(得分:0)
我所做的是检查realResult,如果为null,则返回“working”,然后调用BackGroundWorker。在BackGround的回调中分配realResult并调用NotifyPropertyChanged。属性上的异步没什么价值。我喜欢BackGroundWorker的结构以及取消和报告进度的能力。
private System.ComponentModel.BackgroundWorker backgroundWorker1;
private string textBackGround;
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public MainWindow()
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
InitializeComponent();
}
public string TextBackGround
{
get
{
if (!string.IsNullOrEmpty(textBackGround)) return textBackGround;
backgroundWorker1.RunWorkerAsync();
return "working";
}
}
// This event handler is where the actual,
// potentially time-consuming work is done.
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci(worker, e);
}
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
textBackGround = "Cancelled";
NotifyPropertyChanged("TextBackGround");
}
else
{
// Finally, handle the case where the operation
// succeeded.
textBackGround = e.Result.ToString();
NotifyPropertyChanged("TextBackGround");
}
}
// This is the method that does the actual work. For this
// example, it computes a Fibonacci number and
// reports progress as it does its work.
string ComputeFibonacci(BackgroundWorker worker, DoWorkEventArgs e)
{
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker.CancellationPending)
{
e.Cancel = true;
return "cancelled";
}
Thread.Sleep(1000);
if (worker.CancellationPending)
{
e.Cancel = true;
return "cancelled";
}
return "complete";
}
}
答案 2 :(得分:0)
Blam有正确的想法:您确实需要某种“进行中”返回值,因为UI数据绑定需要立即获得值。但是,您不需要BackgroundWorker
。
如果您使用null
作为“进行中”值,那么您可以执行以下操作:
// If _cache is not null, then we already have a calculated property value.
private string _cache;
// If _cacheTask is not null, then the property is being calculated.
private Task<string> _cacheTask;
public string MyProperty
{
get
{
if (_cache != null)
return _cache;
if (_cacheTask != null)
return null; // (in progress)
StartSomeSlowFunction();
// Note: _cacheTask is not null at this point.
return null; // (in progress)
}
set
{
if (value == _cache)
return;
_cache = value;
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
propertyChanged(new PropertyChangedEventArgs("MyProperty"));
}
}
private async Task StartSomeSlowFunction()
{
// Immediately start SomeSlowFunction and set _cacheTask.
_cacheTask = SomeSlowFunction();
// Asynchronously wait for SomeSlowFunction to complete,
// and set the property with the result.
MyProperty = await _cacheTask;
// Let the task be GCed; this also allows the property to be
// recalculated if it is set to null first.
_cacheTask = null;
}
private async Task<string> SomeSlowFunction();