0

I've this class, it works fine

public partial class Home : UserControl
{
    public ObservableCollection<Activity> DataGridRows { get; set; }// = new ObservableCollection<Activity>();

    public Home()
    {
        InitializeComponent();
        DataContext = this;
        this.Init();
    }

    private void Init()
    {
        DataGridRows = new ObservableCollection<Activity>();
        refreshGrid(null, null);
    }

    private void refreshGrid(object sender, RoutedEventArgs e)
    {
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
        {
            startRefresh(); //<-- very long operation!
        }));
    }
}

My problem is that while calling startRefresh() the whole program is freezed, i can't click on other buttons or perform other operations until startRefresh is finished. However i want to run it on background. Note that i can't use the Task object with the TaskScheduler.FromCurrentSynchronizationContext() method because startRefresh performs edit operations on DataGridRows and i get this exception:

System.NotSupportedException : This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
JoulinRouge
  • 456
  • 4
  • 18
  • 1
    You can use [Background Worker](https://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx) – Adil Ansari Feb 26 '16 at 12:08
  • You need to look at multithreading using a BackgroundWorkerThread to load the data. – ChrisF Feb 26 '16 at 12:08
  • 1
    About that NotSupportedException : http://stackoverflow.com/questions/35359923/visual-basic-net-timer-vs-thread/35363155#35363155 (it's in VB .NET but same concept). So in your StartRefresh method, you must invoke the UI thread whenever you update the DataGrid – Martin Verjans Feb 26 '16 at 12:15
  • with background worker i got the same exception – JoulinRouge Feb 26 '16 at 13:35
  • i solved by using a List instead of an ObservableCollection and upgrading the ui "by hand" in the main thread instead of in a separate thread. With backGround worker it's important to not edit/modify the ui in the DoWorkEventHandler, you can modify it in the RunWorkerCompletedEventHandler – JoulinRouge Feb 26 '16 at 14:07

2 Answers2

1

You need to move the heavy data fetching and processing off the UI thread. When the data is ready, update the UI from the UI thread. If you are using .NET 4.0 or later, the Task Parallel Library makes this sort of operation MUCH easier.

NOTE: I am making the assumption that startRefresh() both fetches data and updates the UI. You will make like much easier on yourself if the data retrieval and UI update are in separate methods.

See this answer for more detail: Avoiding the window (WPF) to freeze while using TPL

Community
  • 1
  • 1
CHendrix
  • 378
  • 2
  • 12
  • can't use the method in the other answer 'cause i get an exception – JoulinRouge Feb 26 '16 at 13:16
  • 1
    If you're getting the same NotSupportedException, that means you're trying update the CollectionView from the wrong thread. You have to separate the long-running data fetching from updating the UI. Execute the data fetch on a `Task`, then update the UI from the UI thread. – CHendrix Feb 26 '16 at 13:46
  • i used the backgroundworker but you were right! Thanks! – JoulinRouge Feb 26 '16 at 14:04
0

I think you can use awaitable delegate command

 public ICommand MyCommand { get; set; }
    public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            MyCommand = new AwaitableDelegateCommand(refreshGrid);

        }

     private async Task refreshGrid()
            {
                await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
                  {
                      Thread.Sleep(10000);
                  }));
            }

You can have a look at http://jake.ginnivan.net/awaitable-delegatecommand/ for awaitable delegate command

Justin CI
  • 2,693
  • 1
  • 16
  • 34