2

遅い可能性のあるプロパティに対してデータバインディングを使用しています。ただし、UIをフリーズしたくありません。代わりに、新しいタスクライブラリを使用したいと思います。

理想的には、私のプロパティは次のようになります。

public string MyProperty
{
   get
   {
      if (_cache != null)
         return _cache;
      var result = await SomeSlowFunction();
      return result;
   }
}

ただし、プロパティが非同期になることはないため、これは機能するようになりました。

解決策はありますか?

4

3 に答える 3

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");
   }
}
于 2012-04-07T23:58:31.663 に答える
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();
于 2012-04-08T00:26:39.200 に答える
0

私がしているのは、realResult をチェックすることです。それが null の場合は、「作業中」を返し、BackGroundWorker を呼び出します。BackGround からのコールバックで、realResult を割り当て、NotifyPropertyChanged を呼び出します。async on プロパティはほとんど価値がありません。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";
    }
}
于 2012-04-08T00:06:31.457 に答える