Search code examples
c#winformsdatagridviewbackgroundworker

DataGridView form shows up empty after background worker finish


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:

enter image description here

But instead I'm getting this:

enter image description here

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.


Solution

  • 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.