How do I safely populate with data and Refresh() a DataGridView in a multi-threaded application?

前端 未结 3 535
野的像风
野的像风 2021-02-06 16:42

My app has a DataGridView object and a List of type MousePos. MousePos is a custom class that holds mouse X,Y coordinates (of type \"Point\") and a running count of this positio

相关标签:
3条回答
  • 2021-02-06 17:08

    Looks like you have your answer right there! Just in cawse you're curious about how to do cross thread calls back to ui: All controls have a Invoke() method (or BEginInvoke()- in case you want to do things asynchronously), this is used to call any method on the control within the context of the main UI thread. So, if you were going to call your datagridview from another thread you would need to do the following:

    public void refreshWindow_Elapsed(object source, EventArgs e)
    {
    
       // we use anonymous delgate here as it saves us declaring a named delegate in our class
       // however, as c# type inference sometimes need  a bit of 'help' we need to cast it 
       // to an instance of MethodInvoker
       dataGridView1.Invoke((MethodInvoker)delegate() { dataGridView1.Invalidate(); });
    }
    
    0 讨论(0)
  • 2021-02-06 17:13

    UPDATE! -- I partially figured out the answer to part #1 in the book "Pro .NET 2.0 Windows Forms and Customer Controls in C#"

    I had originally thought that Refresh() wasn't doing anything and that I needed to call the Invalidate() method, to tell Windows to repaint my control at it's leisure. (which is usually right away, but if you need a guarantee to repaint it now, then follow up with an immediate call to the Update() method.)

        dataGridView1.Invalidate();
    

    But, it turns out that the Refresh() method is merely an alias for:

        dataGridView1.Invalidate(true);
        dataGridView1.Update();             // <== forces immediate redraw
    

    The only glitch I found with this was that if there was no data in the dataGridView, no amount of invalidating would refresh the control. I had to reassign the datasource. Then it worked fine after that. But only for the amount of rows (or items in my list) -- If new items were added, the dataGridView would be unaware that there were more rows to display.

    So it seems that when binding a source of data (List or Table) to the Datasource, the dataGridView counts the items (rows) and then sets this internally and never checks to see if there are new rows/items or rows/items deleted. This is why re-binding the datasource repeatedly was working before.

    Now to figure out how to update the number of rows to display in dataGridView without having to re-bind the datasource... fun, fun, fun! :-)


    After doing some digging, I think I have my answer to part #2 of my question (aka. safe Multi-threading):

    Rather than using System.Timers.Timer, I found that I should be using System.Windows.Forms.Timer instead.

    The event occurs such that the method that is used in the Callback automatically happens on the primary thread. No cross-threading issues!

    The declaration looks like this:

    private static System.Windows.Forms.Timer refreshWindow2;
    refreshWindow2 = new Timer();
    refreshWindow2.Interval = 1000;
    refreshWindow2.Tick += new EventHandler(refreshWindow2_Tick);
    refreshWindow2.Start();
    

    And the method is like this:

    private void refreshWindow2_Tick(object sender, EventArgs e)
    {
        dataGridView1.Invalidate();
    }
    
    0 讨论(0)
  • 2021-02-06 17:14

    You have to update the grid on the main UI thread, like all the other controls. See control.Invoke or Control.BeginInvoke.

    0 讨论(0)
提交回复
热议问题