DataGridView form shows up empty after background worker finish

非 Y 不嫁゛ 提交于 2019-12-11 07:14:28

问题


Need to ask you for help as I'm struggling with this for too long now. Have gone over many tutorials but can't figure it out yet...

In a first stage of this proyect I developed a console program that makes requests to a web server, processes the data and updates a MS Access DB. My problem arise now that after doing all that processing, I need to show the result inside a winform for the clients to see it, and even more the process should be going over and over again while the app is opened.

So what I've done so far is a creating a winform that runs the console program as a background worker and the results should be shown and updated as the program keeps running. For simplicity, I'm replacing all the heavy processing for just a loop that fills in a List of HashTables, which is returned to the winform to be displayed:

    namespace TheNameSpace
{
    class aldeloUpdater
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new GuiForm());
        }

        public List<Hashtable> oldMain()
        {
            List<Hashtable> products = new List<Hashtable>();

            try
            {
                Hashtable product1 = new Hashtable();
                Hashtable product2 = new Hashtable();
                Hashtable product3 = new Hashtable();

                product1.Add("productName", "Empanada de Pollo");
                product1.Add("userName", "Fabio Roman");
                product1.Add("dateAndTime", "2016-08-11 15:50:52");
                product1.Add("domiciliosOrderId", "1932211-20160811155052");
                products.Add(product1);

                product2.Add("productName", "Empanada de Carne");
                ...
                products.Add(product2);

                product3.Add("productName", "Empanada Mixta");
                ...
                products.Add(product3);

                Console.WriteLine("A message for debugging.");
                Console.ReadLine();

                return products;
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception details: " + e.ToString());
                Console.ReadLine();
                return products;
            }
        }

        // More Methods and classes
}

Now as for the winform I've got this:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;

namespace TheNameSpace
{
    public partial class GuiForm : Form
    {
        public List<Hashtable> dataGridViewProducts;

        public GuiForm()
        {
            InitializeComponent();
            InitializeBackgroundWorker();
            backgroundWorker.RunWorkerAsync(); // Initialize the whole process.
        }

        // Set up the BackgroundWorker object by 
        // attaching event handlers. 
        private void InitializeBackgroundWorker()
        {
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerComplet);
        }


        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {

        }

        private void GuiForm_Load(object sender, EventArgs e)
        {
            int index = 0;
            if (dataGridViewProducts != null && !dataGridViewProducts.Any())
            {
                foreach (Hashtable product in dataGridViewProducts)
                {
                    dataGridView1.ReadOnly = false;
                    dataGridView1.Rows.Add();
                    dataGridView1.Rows[index].Cells[0].Value = product["productName"];
                    dataGridView1.Rows[index].Cells[1].Value = product["userName"];
                    dataGridView1.Rows[index].Cells[2].Value = product["dateAndTime"];
                    dataGridView1.Rows[index].Cells[3].Value = product["domiciliosOrderId"];
                    index++;
                }
                return;
            } 
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Logic for a delete-button
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            aldeloUpdater aldeloThread = new aldeloUpdater();
            this.dataGridViewProducts = aldeloThread.oldMain();

            //this.GuiForm_Load(); // ¿Do I need to make this call? ¿How to?
            this.Show();
        }

        private void backgroundWorker_RunWorkerComplet(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else
            {
                MessageBox.Show(e.Result.ToString());
            }
        }
    }
}

I've been expecting this result:

But instead I'm getting this:

I know I'm doing something wrong, but I just don't know what is that and how to do it well, I'm PHP back-end dev and this is my very first C# program. Please help.


回答1:


Without a good Minimal, Complete, and Verifiable code example, it's impossible to know for sure what would the best solution. But based on the code you posted above, I can provide some suggestions:

  1. There doesn't appear to be any need for oldMain() to be an instance method. You can make it public static List<Hashtable> oldMain() and then call it as aldeloUpdater.oldMain(). Even better, create a separate class for the method, rather than putting it in what would otherwise be your main Program class.
  2. You wrote a comment reading "Do I need to make this call? How to?". I would answer this as "yes, you need to make this call." Since the method has to access the UI objects, specifically your dataGridView1 object, you should call it in the RunWorkerCompleted event handler:

    private void backgroundWorker_RunWorkerComplet(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else
        {
            MessageBox.Show(e.Result.ToString());
            GuiForm_Load(sender, e);
        }
    }
    

Though, you don't actually use the sender and e parameters in your GuiForm_Load() method, so it seems to me you could just omit those.

  1. The form is already shown by the Main() method, by passing it to the Application.Run() method. So there shouldn't be any need to call this.Show() in the DoWork event handler. That's the wrong place to call Show() anyway, since you're executing in the wrong thread at that point. You should just remove that call.



回答2:


I see some mysterious stuff in your code.

On the UI, you better have a good business class representing your data instead of using your hashtable stuff. Then you have to create a BindingList(Of yourclass) and set it as the datasource of your datagridview.

Then you should have your "fetching/processing" thread which should make some invoke on the UI to add the "business object" to the bindinglist (the datagridview will update automaticly).

If the fetching/processing is an endless loop, maybe you should use a Thread object with a loop instead of a backgroundworker which restart itself in the runworkerCompleted event.

As you look to have some serious leaks with winforms, you should consider trying to fill up a datagridview on a button click event before going in trouble with multithreading concept.

The click event could be your long fetching/processing, it doesn't matter if your UI freeze for 2 minutes as long it's working.

As I say: Make it. Make it work. Make it fast (and reactive).



来源:https://stackoverflow.com/questions/39108349/datagridview-form-shows-up-empty-after-background-worker-finish

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!