Populate GridView Datasource(BindingList) from Event/BackgroundWorker

4 posts, 0 answers
  1. Regis
    Regis avatar
    3 posts
    Member since:
    Jul 2013

    Posted 03 Jul 2013 Link to this post

     I have a gridview with the datasource set to a bindinglist.  My goal is for the gridview to accurately reflect changes in the bindinglist’s composition (specifically additions at this point).  When I add objects to the bindinglist I get an error saying “Bounds cannot be changed while locked”.   I believe this is an issue with the Gridview because if I do not set the datasource, the bindinglist is populated as expected.  After a lot of searching I came across a few other posts which were similar to my situation:

    However these deal with updating data already present in the bindinglist.  My application starts with an empty bindinglist set as the datasource and is populated through code.

     

    My C# .Net 3.5  winforms program uses multiple threads to search through all the directories on the machine and return files that meet certain criteria.   The recursive search creates a new thread for each directory found to iterate over it's files:

    private  static void IndexDirectory(string directory)
     {
       foreach (string d in Directory.GetDirectories(directory))
           {
               try
                 {
                    Thread t = new Thread(new ParameterizedThreadStart(GetFiles));
                     t.Start(d);
                     IndexDirectory(d);
                  } catch(System.UnauthorizedAccessException){ }
            }
    }

    These files are used to create objects which are stored in a custom collection object which inherits from System.Collections.ObjectModel.Collection<T>.  I’ve created an event that fires when an object is added to the collection and subscribe to it in my form.  The arguments for the event include the object being added to the collection, so in the event handler I add the object to the bindinglist:
    //form field variable used as the datasource for the gridview.
    BindingList<MyFileObj> bindingList = new BindingList<MyFileObj>();
     
    MyCollectionChangedEventHandler(MyEventArgs e)
    {
      bindingList.Add( (MyFileObj) e.GetMyFileObj);
    }


    When that didn’t work I modified the program to use a backgroundworker and queue hoping that putting the code in the WorkerCompleted method would force it to all execute on the UI thread:

     

    Queue<MyFileObj> addMe = new Queue<MyFileObj>();
     
    MyCollectionChangedEventHandler(MyEventArgs e)
    {
       addMe.Enqueue ( (MyFileObj) e.GetMyFileObj);
       if(!backgroundworker.IsBusy)
           backgroundworker.RunWorkerAsync ();
    }
     
     backgroundworker_DoWork(object sender, DoWorkEventArgs e)
    {
       Try{
           e.Result = (MyFileObj) addMe.Dequeue();
       }Catch()
       {   //queue empty
       }
    }
     
    backgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
         bindingList.Add((MyFileObj) e.Result);
    }

    Both of these methods of updating the bindinglist result in the same error.

    How can I get my changes to the bindinglist to not crash my program when it is set as a datasource? Preferably, without sacrificing the speed gained through multithreading.   

    Thanks for any help you can provide. 
  2. Anton
    Admin
    Anton avatar
    167 posts

    Posted 08 Jul 2013 Link to this post

    Hi Regis,

    Thank you for writing.

    In .NET. you cannot bind controls from the UI thread to a binding list that is created and after that is being updated in another thread. With such binding approach the UI thread will not receive the notifications from the binding list. However, this cannot lead to the deadlock that you are experiencing in your project.

    RadGridView is using only the UI thread for its CRUD operations related with the DataSource. The issue appears because several threads are trying to access the BindingList at a time and the BindingList is not a thread safe collection. However, how to create and use threads in .Net is not directly related to our controls.

    My advice is to read the following internet resources where you can find more information about how use threads in .Net:
    I also noticed that you are creating a lot of threads according your directory structure which is considered a bad practice, because you may have huge amount of threads and this will not lead to performance improvement, contrariwise, this will decrease of the performance. The threads are expensive resource and you should be careful how much of them you will create. You can read this forum thread for more information: http://stackoverflow.com/questions/5358851/costs-of-a-new-thread-versus-cross-thread-marshalling.

    I hope this information helps.

    Regards,
    Anton
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  3. Regis
    Regis avatar
    3 posts
    Member since:
    Jul 2013

    Posted 09 Jul 2013 Link to this post

    Anton, thanks for all the information on threading, you really went above and beyond.  I appreciate the advice and plan to modify my code. Since I’m not doing anything with my threads other than calling a method I should just use the threadpool. 


    I was under the impression that if an object was databound to a control, methods of that object which force the control to redraw itself would be automatically run on the UI thread.  I see now that this is a problem for controls in general not just the gridview.  I fixed the problem with the technique described in this forum:

    http://stackoverflow.com/questions/782274/using-c-sharp-methodinvoker-invoke-for-a-gui-app-is-this-good

     

    However, doing this for lots of additions to the bindinglist in a short amount of time causes the UI to become unresponsive.  Now I’m playing around with introducing waits in the do work method of the backgroundworker and not having the list raise an event for every addition using Suspend/ResumeLayout() and Begin/EndUpdate() methods, and manually calling Refresh().  Unbinding and rebinding the datasource with a timer control is also an option, it just doesn't seem like a good one.  If you’re going to do that why even use a bindinglist in the first place?

  4. Anton
    Admin
    Anton avatar
    167 posts

    Posted 11 Jul 2013 Link to this post

    Hi Regis,

    Thank you for writing back.

    Indeed if you update BindingList a lot of times in short period this may lead to unresponsive UI due on large amount of refresh that should execute on UI Thread. For example:

    Thread1      UIThread
    Update1 => Grid Refresh
    Update2 => Grid Refresh
    Update3 => Grid Refresh

    You can find more information how to resolve this issue in following online resources:

    I hope this information helps.

    Regards,
    Anton
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
Back to Top