takayoshi
takayoshi

Reputation: 2799

long background process refreshing ui wpf

I have long search operations which periodically updates UI (found occurence -> Update UI)

I've tried to realize it many ways:

  1. async/await

    public void PushButton()
    {
        await AsyncSearchAll();
    }
    
    public async Task AsyncSearchAll(SearchPanelViewModel searchPanelViewModel, SearchSettings searchSettings, CancellationToken cancellationToken)
    {
        await Task.Factory.StartNew(() =>
                                          {
                                              //searching for occurence
                                              //write it into panel
                                          }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    }
    
  2. BackgroundWorker

    I want to use it but I don't want access UI using only .ReportProgress()

  3. Simple background thread with calling Dispatcher.BeginInvoke(()=>{//updating UI})

    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            var backgroundThread = new Thread(CountToTen)
                {
                    IsBackground = true
                };
            backgroundThread.Start();
        }
    
        private void CountToTen()
        {
            for (int i = 1; i <= 10000; i++)
            {
                var j = i;
                Dispatcher.BeginInvoke(new Action(() => Seconds.Text = j.ToString(CultureInfo.InvariantCulture)));
            }
        }
    

All methods writing all data after completing thread. Is there any method to run background task which periodically updating UI without slowing program by blocking ui?

Upvotes: 3

Views: 3614

Answers (2)

svick
svick

Reputation: 244777

I think that in cases like this, it's best if you can use binding. With the new collection synchronization, you can do things like adding to a bound ObservableCollection<T> from another thread.

If that's not enough for your purposes, you can use Progress<T> to perform an action on the UI thread whenever your produce some result on the other thread (though, as its name suggests, Progress is primarily meant for progress reporting).

If that doesn't fit either, you could use TPL Dataflow. You would have a single ActionBlock<T> with TaskScheduler set to the UI scheduler. Your worker thread would send the generated items to the block and the block would process them on the UI thread.

Upvotes: 0

Stephen Cleary
Stephen Cleary

Reputation: 456477

It's best if you can separate your "worker" logic from your "UI update" logic.

Something like this:

public async Task AsyncSearchAll(SearchPanelViewModel searchPanelViewModel, SearchSettings searchSettings, CancellationToken cancellationToken)
{
  while (..)
  {
    var results = await Task.Run(() => /* search more */);
    /* update panel with results */
  }
}

But if you want actual progress updates, there's ways to do that too:

public async void PushButton()
{
  Progress<MyUpdateType> progress = new Progress<MyUpdateType>(update =>
  {
    /* update panel */
  });
  await Task.Run(() => SearchAll(..., progress));
}

public void SearchAll(SearchPanelViewModel searchPanelViewModel,
    SearchSettings searchSettings, CancellationToken cancellationToken,
    IProgress<MyUpdateType> progress)
{
  while (..)
  {
    /* search more */
    if (progress != null)
      progress.Report(new MyUpdateType(...));
  }
}

Upvotes: 4

Related Questions