0

Assuming large set of data to be loaded, I want the UI be responsive while loading data. Currently the only working code is the following which constantly refreshes the UI which is not desired. How to have data loaded in non-UI thread and gets the final update on view?

private static object sync_lock = new object();

private void Load()
{
    MyEntities db = new MyEntities();

    TestEntityViewModel testEntityViewModel = (TestEntityViewModel)FindResource("testEntityViewModel");

    testEntityViewModel.Source = db.TestEntities.Local; // Source is ObservableCollection<TestEntity>

    BindingOperations.EnableCollectionSynchronization(testEntityViewModel.Source, sync_lock);

            db.TestEntities.LoadAsync().ContinueWith(new Action<Task>(
                (t) =>
                {
                    this.Dispatcher.Invoke(new Action(() =>
                    {
                        View.MoveCurrentToFirst();

                        CommandManager.InvalidateRequerySuggested();
                    }));
                }));
}

Note: If I remove the call to EnableCollectionSynchronization, data is loaded but the ICollectionView and its SourceCollection would have only 1 item.

Mohsen Afshin
  • 13,273
  • 10
  • 65
  • 90

1 Answers1

1

you seem to be using MVVM. this is how i do it in my apps.

interface ISomeDataService{
    void GetModelItems(Action<Model[], Exception> callback);
}


class SomeDataServiceImpl:ISomeDataService{
    void GetModelItems(Action<Model[], Exception> callback){
        Task.Factory.StartNew(()=>{
            //get model items from WCF for example than call the callback
            //expected to take a while. 
            //you can also directly access DbContext from here
            //if you like
            callback(res, null);
        });
    }
}

you now need to use this implementation in your VM. you can do this like below.

class MyDemoVM{
    private Model[] items;
    private readonly ISomeDataService service;
    private readonly IDispatcherService dispService;
    public MyDemoVM(){
        service=new SomeDataServiceImpl();
        dispService=new DispatcherService();
        //i use ctor injection here in order to break the dependency on SomeDataServiceImpl and on DispatcherService.
        //DispatcherService delegates to the App dispatcher in order to run code
        //on the UI thread.
    }
    public Model[] Items{
        get{
            if(items==null)GetItems();
            return items;
        }
        set{
            if(items==value)return;
            items=value;
            NotifyChanged("Items");
        }
    }
    private void GetItems(){
        service.GetModelItems((res,ex)=>{
            //this is on a different thread so you need to synchronize
            dispService.Dispatch(()=>{
                Items=res;
            });
        });
    }
}

this code uses lazy loading. the data will be downloaded when the Items property will be read by the UI control. this code will refresh the collection only once, after all data is downloaded. please note that this example is good for .net 4. if you are using .net 4.5 you could use the async/await keywords to make the service more readable. the concept is the same though.

flo_badea
  • 774
  • 5
  • 8