问题
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:
- There doesn't appear to be any need for
oldMain()
to be an instance method. You can make itpublic static List<Hashtable> oldMain()
and then call it asaldeloUpdater.oldMain()
. Even better, create a separate class for the method, rather than putting it in what would otherwise be your mainProgram
class. 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 theRunWorkerCompleted
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.
- The form is already shown by the
Main()
method, by passing it to theApplication.Run()
method. So there shouldn't be any need to callthis.Show()
in theDoWork
event handler. That's the wrong place to callShow()
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