I need help on a thing, ive been populating a listview via background worker but it appears it had a row count at the end of the loop but the data for is not showing visually on the listview. i really dont know what is happening.
After the background worker process is done. ive put a message box to show the listview item count but it shows no data on listview. Please see picture.
Thank you
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
listView1.View = View.Details;
DataTable dtdt = new DataTable();
dtdt = qr.history(); // query in sql to datatable
for (int i = 0; i < dtdt.Rows.Count; i++)// loop data to listviewitem
{
DataRow dr = dtdt.Rows[i];
ListViewItem listitem = new ListViewItem(dr["custnum"].ToString());
listitem.SubItems.Add(dr["custname"].ToString().Trim());
listitem.SubItems.Add(dr["ratecable"].ToString().Trim());
listitem.SubItems.Add(dr["rateinternet"].ToString().Trim());
listitem.SubItems.Add(dr["rateext"].ToString().Trim());
listitem.SubItems.Add(dr["status"].ToString().Trim());
listitem.SubItems.Add(dr["classname"].ToString().Trim());
listitem.SubItems.Add(dr["SVCstadd"].ToString().Trim());
listitem.SubItems.Add(dr["SVCctadd"].ToString().Trim());
listitem.SubItems.Add(dr["svctelno"].ToString().Trim());
listitem.SubItems.Add(dr["bilstadd"].ToString().Trim());
listitem.SubItems.Add(dr["bilctadd"].ToString().Trim());
listitem.SubItems.Add(dr["billtel"].ToString().Trim());
listitem.SubItems.Add(dr["billtel2"].ToString().Trim());
listitem.SubItems.Add(dr["fax"].ToString().Trim());
listitem.SubItems.Add(dr["zoneno"].ToString().Trim());
listitem.SubItems.Add(dr["zoneName"].ToString().Trim());
listitem.SubItems.Add(dr["bookno"].ToString().Trim());
listitem.SubItems.Add(dr["seqno"].ToString().Trim());
listitem.SubItems.Add(dr["Balance"].ToString().Trim());
listitem.SubItems.Add(dr["balance1"].ToString().Trim());
listitem.SubItems.Add(dr["balance2"].ToString().Trim());
listitem.SubItems.Add(dr["balance3"].ToString().Trim());
listitem.SubItems.Add(dr["billamnt"].ToString().Trim());
listitem.SubItems.Add(dr["maxdate"].ToString().Trim());
this.BeginInvoke(new MethodInvoker(delegate { additemtoLV(listitem); }));
}
System.Threading.Thread.Sleep(100);
}
private delegate void additemtoLVdelegat(ListViewItem ls);
public void additemtoLV(ListViewItem ls)
{
if (InvokeRequired)
{
Invoke(new additemtoLVdelegat(additemtoLV), ls);
}
else
{
listView1.Items.Add(ls);
}
}
Further to comments posted please try the below, which should correctly use the GUI thread, and also has basic exception handling in case an unhandled exception is causing the problem :
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
listView1.View = View.Details;
DataTable dtdt = new DataTable();
dtdt = qr.history(); // query in sql to datatable
var listItems = new List<ListViewItem>();
for (int i = 0; i < dtdt.Rows.Count; i++)// loop data to listviewitem
{
DataRow dr = dtdt.Rows[i];
ListViewItem listitem = new ListViewItem(dr["custnum"].ToString());
listitem.SubItems.Add(dr["custname"].ToString().Trim());
listitem.SubItems.Add(dr["ratecable"].ToString().Trim());
listitem.SubItems.Add(dr["rateinternet"].ToString().Trim());
listitem.SubItems.Add(dr["rateext"].ToString().Trim());
listitem.SubItems.Add(dr["status"].ToString().Trim());
listitem.SubItems.Add(dr["classname"].ToString().Trim());
listitem.SubItems.Add(dr["SVCstadd"].ToString().Trim());
listitem.SubItems.Add(dr["SVCctadd"].ToString().Trim());
listitem.SubItems.Add(dr["svctelno"].ToString().Trim());
listitem.SubItems.Add(dr["bilstadd"].ToString().Trim());
listitem.SubItems.Add(dr["bilctadd"].ToString().Trim());
listitem.SubItems.Add(dr["billtel"].ToString().Trim());
listitem.SubItems.Add(dr["billtel2"].ToString().Trim());
listitem.SubItems.Add(dr["fax"].ToString().Trim());
listitem.SubItems.Add(dr["zoneno"].ToString().Trim());
listitem.SubItems.Add(dr["zoneName"].ToString().Trim());
listitem.SubItems.Add(dr["bookno"].ToString().Trim());
listitem.SubItems.Add(dr["seqno"].ToString().Trim());
listitem.SubItems.Add(dr["Balance"].ToString().Trim());
listitem.SubItems.Add(dr["balance1"].ToString().Trim());
listitem.SubItems.Add(dr["balance2"].ToString().Trim());
listitem.SubItems.Add(dr["balance3"].ToString().Trim());
listitem.SubItems.Add(dr["billamnt"].ToString().Trim());
listitem.SubItems.Add(dr["maxdate"].ToString().Trim());
listItems.Add(listitem);
}
this.BeginInvoke(((Action)(() => { listView1.Items.AddRange(listItems.ToArray()); })));
}
catch (System.Exception exc)
{
this.BeginInvoke(((Action)(() => { MessageBox.Show("BackgroundWorker error: " + exc);})));
}
}
Related
I am converting an old Outlook VBA macro I created to a C# VSTO Add-in. This tool is for bulk archiving of emails to various public folders and a local .pst file and needs to be able to find emails based on a user's search criteria. I had a whale of a time getting the AdvancedSearchComplete event to trigger in VBA, but eventually got it to work. Of course, I am now have the same problem in the C# VSTO add-in.
To use the tool, the user will input their criteria on a userform, then click the Find Emails button to perform the search with the results populating a datagridview.
public partial class Email_Archiver : Form
{
public Email_Archiver()
{
InitializeComponent();
//Add event handlers
Globals.ThisAddIn.Application.AdvancedSearchComplete += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
}
public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
{ //Handles AdvancedSearchComplete event
if (SearchObject.Tag == "Archiver Search")
{
OutlookFunctions.SearchComplete = true;
MessageBox.Show(this, "Search Complete!","Email Search Error",MessageBoxButtons.OK);
}
}
private void Find_Button_Click(object sender, EventArgs e)
{
//Set datagridview datasource
EmailList.DataSource = FindEmails();
EmailList.Columns["EntryID"].Visible = false;
}
public DataTable FindEmails()
{
var dt = new DataTable();
//Format DataTable
dt.Columns.Add("EntryID", typeof(string));
dt.Columns.Add("Project No.", typeof(int));
dt.Columns.Add("Subject", typeof(string));
dt.Columns.Add("Sender", typeof(string));
dt.Columns.Add("Recipient", typeof(string));
dt.Columns.Add("Time Sent", typeof(DateTime));
dt.Columns.Add("Size", typeof(decimal));
//Do stuff to get "searchFolders" and "searchCriteria" from form
//...
dt = OutlookFunctions.RunAdvancedSearch(searchFolders, searchCriteria);
return dt;
}
}
class OutlookFunctions
{
public static bool SearchComplete;
public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
{
//Get Outlook namespace
var oApp = Globals.ThisAddIn.Application;
var oNS = oApp.GetNamespace("mapi");
Microsoft.Office.Interop.Outlook.Search advSearch = null;
//Do other stuff to properly set up the scope and filter
//...
//Perform search
SearchComplete = false;
try
{
advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
}
catch (System.Exception ex)
{
MessageBox.Show("An error has occurred during the search for emails. \n \n"
+ ex.Message);
return null;
}
Stopwatch timer = new Stopwatch();
timer.Start();
while (!SearchComplete && timer.Elapsed.TotalSeconds < 10)
{
//Do nothing
}
if (!SearchComplete)
{
advSearch.Stop();
}
if (advSearch != null)
{
var resultTable = new DataTable();
//Send results to resultTable
return resultTable;
}
return null;
}
}
The search works and I get the proper results, but I had to add in the timer to make things move along, otherwise the code never finishes because AdvancedSearchComplete isn't triggered. I still get all of the emails I'm looking for and doing essentially the same search just in the Outlook explorer, I get the same results in a fraction of a second.
After the timer ends the search and the datagridview is filled with the results, THEN the AdvancedSearchComplete fires and I get my "Search Complete!" message.
What am I missing here that is keeping the event from triggering to end the search (in a timely fashion)?
You could refer to the following code about using backgroundwork:
BackgroundWorker bw = new BackgroundWorker();
private void button1_Click(object sender, EventArgs e)
{
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//
// Boring.... Do your long work
//
System.Threading.Thread.Sleep(20000);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.InvokeRequired)
{
//Hide your form here
this.Invoke(new MethodInvoker(delegate { this.Close(); }));
}
}
It appears that any code following the initiation for the AdvancedSearch method will essentially end the search without triggering the AdvancedSearchComplete or AdvancedSearchStopped events.
Instead, I now have the following in the ThisAddIn class:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
//Create task pane object
archiverTaskPane1 = new ArchiverTaskPane();
customTaskPane = this.CustomTaskPanes.Add(archiverTaskPane1, "Title");
//Add event handler for opening task pane
customTaskPane.VisibleChanged += new EventHandler(customTaskPane_VisibleChanged);
//Add event handler for Outlook's AdvancedSearch
Globals.ThisAddIn.Application.AdvancedSearchComplete += new Outlook.ApplicationEvents_11_AdvancedSearchCompleteEventHandler(Application_AdvancedSearchComplete);
}
public void Application_AdvancedSearchComplete(Microsoft.Office.Interop.Outlook.Search SearchObject)
{
DataTable dt = new DataTable();
OutlookFunctions oFunctions = new OutlookFunctions();
dt = oFunctions.BuildResults(SearchObject.Results);
this.archiverTaskPane1.DataGridFiller(dt);
}
In the ArchiverTaskPane Class (formerly Email_Archiver Form - I REALLY liked the Task Pane idea)
private void Find_Button_Click(object sender, EventArgs e)
{
FindEmails();
}
public void FindEmails()
{
DataTable dt = new DataTable();
//Add columns to DataTable
//Do stuff to get "searchFolders" and "searchCriteria" from form
//...
//Create OutlookFunctions instance
OutlookFunctions oFunctions = new OutlookFunctions();
oFunctions.RunAdvancedSearch(searchFolders, searchCriteria);
}
public void DataGridFiller(DataTable results)
{
EmailList.DataSource = results;
}
In the OutlookFunctions Class:
public static DataTable RunAdvancedSearch(string[] searchFolders, string[] searchCriteria)
{
//Get Outlook namespace
var oApp = Globals.ThisAddIn.Application;
var oNS = oApp.GetNamespace("mapi");
Microsoft.Office.Interop.Outlook.Search advSearch = null;
//Do other stuff to properly set up the scope and filter
//...
//Perform search
SearchComplete = false;
try
{
advSearch = oApp.AdvancedSearch(scope, filter, false, "Archiver Search");
}
catch (System.Exception ex)
{
MessageBox.Show("An error has occurred during the search for emails. \n \n"
+ ex.Message);
}
}
public DataTable BuildResults(Results searchResults)
{
DataTable resultTable = new DataTable();
//Do stuff to build DataTable from search results
//...
return resultTable;
}
I am trying to make an app that finds all images on a specific folder (exemple: CR7) and not on all directories. I have a network share and a lot of directories where I can find that specific folder "CR7". I only need images from that CR7 folder. I cand find them and trying to put those results on a datadridview, but without success. Any ideas why filesList does not go to datatable?
Here is the code:
{
public partial class FormProcuraFotos : Form
{
DataTable tableWithPhotos;
public FormProcuraFotos()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += this.Worker_DoWork;
worker.RunWorkerCompleted += this.Worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
// Create the new DataTable to be used
tableWithPhotos = new DataTable();
//Find files on a specific folder (CR7)
string allDir = #"\\share\folder01";
var CR7Directories = Directory.EnumerateDirectories(allDir, "CR7", SearchOption.AllDirectories);
List<string> extensions = new List<string>() { ".jpg", ".bmp", ".png", ".tiff", ".gif" };
List<string> filesList = new List<string>();
foreach (var dir in CR7Directories)
{
List<string> FileNames = new DirectoryInfo(dir).EnumerateFiles(dir)
.Where(x => extensions.Contains(x.Extension))
.Select(x => x.Name).ToList();
filesList.AddRange(FileNames);
}
// And now here we will add all the files that it has found into the DataTable
foreach (string entryFiles in filesList)
{
DataRow row = tableWithPhotos.NewRow();
row[0] = Path.GetFileName(entryFiles);
row[1] = entryFiles;
tableWithPhotos.Rows.Add(Path.GetFileName(entryFiles), entryFiles);
}
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
var formToOpen = new FormResultadosFotos(tableWithPhotos);
formToOpen.Show();
}
}
}
I think you are adding new row to datagridview like a datatable and it doen's work.
With datagridview, you can do:
tableWithPhotos.Rows.Add(Path.GetFileName(entryFiles), entryFiles);
Or [If there is any row in your datagridview], you can do:
DataGridViewRow row = (DataGridViewRow)tableWithPhotos.Rows[0].Clone();
row[0] = Path.GetFileName(entryFiles);
row[1] = entryFiles;
tableWithPhotos.Rows.Add(row);
Please refer this link for more details: DataGridViewRows
Because you are creating new form and add a datagrid to it, then the result is null. You can do as bellow:
option1: make filesList as a member of the class, and create new form base on filesList instead of the DataGridView tableWithPhotos, and add row to DataGridView in FormResultadosFotos similar with common your current code.
var formToOpen = new FormResultadosFotos(filesList);
formToOpen.Show();
option 2: modify your constructor of FormResultadosFotos:
public FormResultadosFotos(DataGridView dataGridView)
{
InitializeComponent();
dataGridView1.Rows.Clear();
foreach (DataGridViewRow row in dataGridView.Rows)
{
dataGridView1.Rows.Add(row.Cells[0].Value, row.Cells[1].Value);
}
}
P/S: Make sure that your DataGridView in your FormResultadosFotos already had 2 columns.
Cheer!
In my window application there are many screens with grid. And I have used DataTable as DataSource of the grid and DataTable have some really large data sets (> 50,000), which take a lot time to load data on screen if we load all at a time while loading the UI get un-responsive till all data not get loaded, So that I have implemented incremental loading in that grid using Background Worker.
Here is the code :
// DoWork Event of the background Wroker.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
while (bgstop)
{
e.Result = addNewRecord();
if (Convert.ToBoolean(e.Result) == false)
{
e.Cancel = true;
bgstop = false;
killBGWorker();
break;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// to add/merge the records in the DataTable
private bool addNewRecord()
{
int flag = 0;
try
{
Thread.Sleep(500); //optional
DataTable tableAdd = getTableData();
if (tableAdd.Rows.Count > 0)
{
dtRecords.Merge(tableAdd); // dtRecords is the DataTable which attached to grid
flag++;
}
else
backgroundWorker1.WorkerSupportsCancellation = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (flag > 0)
return true;
else
return false;
}
// To get the next slot of Records from the DataBase
private DataTable getTableData()
{
DataTable dt = new DataTable();
start = nextRows * noOfRows;
stop = start + noOfRows;
dt = SQLHelper.getAllRecords(totalRows,noOfRows, start + 1, stop);
nextRows++;
return dt;
}
// kill the backgroudworker after the all data/records get loaded from database to grid/DataTable
private void killBGWorker()
{
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.CancelAsync();
}
Above code get the first defined number of records (say 200) and after that in the background worker started and start fetching the data in a slot and merge that with grid DataSource till all data (say >50,000 records) get loaded into the grid.
But still have some issue with UI interaction, UI not get hang for 2-3 seconds many time till all records from DataBase get loaded into the grid.
I gone through this but in that example DataModel was used but in my case there is no DataModel they just fetched in DataTable from DataBase and right now we can't move to DataModel. Is there any other way to achieve incremental Loading with good UI interaction ?OR Is there any way to implement IBindingList in current scenario ?
You can achieve that by changing the DataGridView from BindingMode to VirtualMode.
The following changes will re-use as much as possible what you already have and you will see that the DataGridView gets loaded incrementally. I don't know how much records you fetch at once, but you can keep that number low.
Set the property VirtualMode to true. Remove any values from the property DataSource. Add as many Unbounded columns to your DataGridView as you have columns in your DataGrid (this could be done automatic if needed).
Add an eventhandler for CellValueNeeded.
Add the following code to that handler:
private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
e.Value = dtRecords.Rows[e.RowIndex][e.ColumnIndex];
}
On you backgroundworker1 set the property WorkerReportsProgress to True
Add an eventhandler to your backgroundworker for ProgressChanged.with the following code:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.dataGridView1.RowCount = (int) e.UserState;
}
In your method addNewRecord add below this line:
dtRecords.Merge(tableAdd); // dtRecords is the DataTable which attached to grid
// added to bring the number of records to the UI thread
backgroundWorker1.ReportProgress(42, dtRecords.Rows.Count);
And with that your datagridview should now load its data incrementally. The trick really is setting the RowCount property. That signals to the datagrid if it can show a record and it adapts its scrollbar to the same.
My solution is using BindingSource like this:
// To take data in silent
BackgroundWorker m_oWorker;
// To hold my data. tblDuToanPhanBo is my data type
List<tblDuToanPhanBo> lst2 = new List<tblDuToanPhanBo>();
BindingSource bs = new BindingSource();
// replace 50000 with your total data count
int totalData = 500000;
// No of rows to load a time by BackgroundWorker
int RowsToTake = 2000;
// No of rows loaded
int RowsTaken = 0;
Take first portion of data and let BackgroundWorker do the rest:
private void UserControl1_Load(object sender, EventArgs e)
{
m_oWorker = new BackgroundWorker();
m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
m_oWorker.ProgressChanged += new ProgressChangedEventHandler (m_oWorker_ProgressChanged);
m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler (m_oWorker_RunWorkerCompleted);
// QLQT is my DataContext
using (QLQT db = new QLQT())
{
lst2.AddRange(db.tblDuToanPhanBos.Skip(RowsTaken).Take(RowsToTake).ToList());
}
RowsTaken = lst2.Count;
bs.DataSource = lst2;
dataGridView1.DataSource = bs;
m_oWorker.RunWorkerAsync();
}
BackgroundWorker to take one portion of data:
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Load data
using (QLQT db = new QLQT())
{
lst2.AddRange(db.tblDuToanPhanBos.Skip(RowsTaken).Take(RowsToTake).ToList());
}
// Update number of rows loaded
RowsTaken = lst2.Count;
if (((BackgroundWorker)sender).CancellationPending)
{
e.Cancel = true;
return;
}
}
When BackgroundWorker is completed, update BindingSource, run BackgroundWorker against until all data loaded:
void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Loading Cancelled.");
}
else if (e.Error != null)
{
MessageBox.Show("Error while performing background operation.");
}
else
{
if (lst2.Count < totalData)
{
bs.ResetBindings(false);
m_oWorker.RunWorkerAsync();
}
else
{
bs.ResetBindings(false);
}
}
}
Hope this help :)
I'm building an WinForms application which is going to be an adressbook. I'm stuck with a problem though. When I open the program and press on my load contacts button, it loads all that's written in the txt file. But if I create a new contact and press load again, the new contact doesn't show up. Is there any way to fix this?
Also, when I try to create new methods for example a Delete() method. It says "Items collection cannot be modified when the DataSource property is set." Any ideas why is crashes?
List<string> Load()
{
StreamReader read = new StreamReader(path);
string row = "";
while ((row = read.ReadLine()) != null)
{
adressbook.Add(row);
}
read.Close();
return adressbook; //Adressbook is my List<string> adressbook = new List<string> uptop.
}
private void button2_Click(object sender, EventArgs e)
{
List<string> list = Load();
listBox1.DataSource = list;
}
You have to set to null the DataSource before clearing and binding:
private void button2_Click(object sender, EventArgs e)
{
if(listBox1.DataSource != null)
{
listBox1.DataSource = null;
listBox1.Items.Clear();
}
List<string> list = Load();
listBox1.DataSource = list;
}
In your Load you must first clear the list
List<string> Load()
{
if (adressbook.Count != 0)
{
adressbook.Clear();
}
StreamReader read = new StreamReader(path);
string row = "";
while ((row = read.ReadLine()) != null)
{
adressbook.Add(row);
}
read.Close();
return adressbook; //Adressbook is my List<string> adressbook = new List<string> uptop.
}
this question and Answer ->
C# add child to Parents treeView
is Stuck Because Volume of data is enormous. How to backgroundWorker or Thread Cell ProgressBar
Help me implement it with backgroundWorker Thread Progress Bar
code
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
//// Or by
Thread th1 = new Thread(new ThreadStart(fun1));
th1.Start();
}
private void fun1()
{
/// Business_tb_Language IS ClassLibrary (DLL) return DataTable
Business_tb_Language businessTbLanguage = new Business_tb_Language();
DataTable dt1 = businessTbLanguage.bRead();
LoadTreeView(dt1);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
/// Business_tb_Language IS ClassLibrary (DLL) return DataTable
Business_tb_Language businessTbLanguage = new Business_tb_Language();
DataTable dt1 = businessTbLanguage.bRead();
LoadTreeView(dt1);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("error");
}else if (e.Cancelled)
{
MessageBox.Show("cancel");
progressBar1.Value = 0;
}
else
{
MessageBox.Show("ok");
progressBar1.Value = 0;
}
}
private void LoadTreeView(DataTable dt)
{
var dNodes = new Dictionary<string, TreeNode>();
foreach (DataRow dRow in dt.Rows)
{
string sSublang = dRow["Sub_lang"].ToString();
string sCode = dRow["code"].ToString();
string sName = dRow["name"].ToString();
if (sSublang == "0")
{
var tn = treeView1.Nodes.Add(sCode, sName);
dNodes.Add(sCode, tn);
}
else
{
string[] arrSubLang = sSublang.Split('_');
for (int i = arrSubLang.Length - 1; i >= 0; i--)
{
string sFindCode = arrSubLang[i];
var tnLastParent = default(TreeNode);
if (dNodes.ContainsKey(sFindCode))
{
var tn = dNodes[sFindCode];
if (tnLastParent != default(TreeNode))
{
tn.Nodes.Add(tnLastParent);
tnLastParent = tn;
}
else if (!dNodes.ContainsKey(sCode))
{
tnLastParent = tn.Nodes.Add(sCode, sName);
dNodes.Add(sCode, tnLastParent);
}
}
}
}
}
treeView1.ExpandAll();
}
my code
Error in function LoadTreeView(DataTable dt)
Line
var tn = treeView1.Nodes.Add(sCode, sName);
** Text Error **
An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Action being performed on this control is being called from the wrong thread. Marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action.
Read Business_tb_Language ( IS ClassLibrary (DLL) return DataTable ) AND Show in treeView BY backgroundWorker or Thread Cell ProgressBar
Explain more :
This function "LoadTreeView (DataTable dt)" and "Business_tb_Language" Correct and true
But I want to implement the BY backgroundWorker or Thread Cell ProgressBar
Thank you.