C# - Outlook VSTO - Getting AdvancedSearchComplete to trigger - c#

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;
}

Related

I got nothing in my grid view (C# dependency)

I have a windows application using SqlDependency. This application represents a log monitor UI get the latest rows added in a specific table in the database and view it in a DataGridView
The code is realize the change in SQL Server but the DataGridView show nothing (No errors, and no data, just as many line as in datatable)
Here is my code
public partial class Form1 : Form
{
private int changeCount = 0;
private const string tableName = "NhanVien";
private const string statusMessage = "Đã có {0} thay đổi.";
//' The following objects are reused
//' for the lifetime of the application.
private SqlConnection connection = null;
private SqlCommand command = null;
private DataSet dataToWatch = null;
public Form1()
{
InitializeComponent();
Form2 frm = new Form2();
frm.Show();
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch (Exception ex)
{
return false;
}
}
private string GetConnectionString()
{
// To avoid storing the connection string in your code,
// you can retrive it from a configuration file.
// Return "Data Source=THU-PC\TINTIN;Initial Catalog=QLVT;Persist Security Info=True;User ID=sa;Password=kc;Pooling = false"
return "Data Source=MSI;Initial Catalog=ChuyenDeCNPM;User ID=sa;Password=123;";
}
private string GetSQL()
{
return "select manv as [Mã NV],Ho as [ Họ],Ten as [Tên],phai as [Phái],diachi as [ Địa chỉ] from dbo.NhanVien";
}
private void nhanVienBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.nhanVienBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.chuyenDeCNPMDataSet);
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'chuyenDeCNPMDataSet.NhanVien' table. You can move, or remove it, as needed.
//this.nhanVienTableAdapter.Fill(this.chuyenDeCNPMDataSet.NhanVien);
if (CanRequestNotifications() == true)
BatDau();
else
MessageBox.Show("Bạn chưa kích hoạt dịch vụ Broker", "Cảnh báo", MessageBoxButtons.OK);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// It is illegal to update the UI from a worker thread
// The following code checks to see if it is safe update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);
object[] args = new[] { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler since it's only good
// for a single notification
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
// At this point, the code is executing on the
// UI thread, so it is safe to update the UI.
++changeCount;
this.label1.Text = string.Format(statusMessage, changeCount);
// Add information from the event arguments to the list box
// for debugging purposes only.
{
var withBlock = this.ListBox1.Items;
withBlock.Clear();
withBlock.Add("Info: " + e.Info.ToString());
withBlock.Add("Source: " + e.Source.ToString());
withBlock.Add("Type: " + e.Type.ToString());
}
// Reload the dataset that's bound to the grid.
GetData();
}
private void GetData()
{
// Empty the dataset so that there is only
// one batch worth of data displayed.
dataToWatch.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataToWatch, tableName);
DataGridView1.DataSource = null;
DataGridView1.DataSource = dataToWatch;
DataGridView1.DataMember = tableName;
}
/*DataTable dt = new DataTable();
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;*/
}
private void BatDau()
{
changeCount = 0;
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop(GetConnectionString());
try
{
SqlDependency.Start(GetConnectionString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
connection.Open();
}
if (command == null)
// GetSQL is a local procedure that returns
// a paramaterized SQL string. You might want
// to use a stored procedure in your application.
command = new SqlCommand(GetSQL(), connection);
if (dataToWatch == null)
dataToWatch = new DataSet();
GetData();
}
private void button_update_Click(object sender, EventArgs e)
{
Update form = new Update();
form.Show();
}
private void Form1_FormClosed(System.Object sender, System.Windows.Forms.FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
connection.Close();
}
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void button_exit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
This is my datagridview after start
I tried debug and saw that the datasource is set. But it's still empty in GridView
I've just found out that i have TableAdapter and BindingSource connect with GridView [![like this][1]][1], so it's cause fault. If you delete all that, use datatable instead of dataset, the code will be like this:
private void GetData()
{
dt.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;
}
You should declare DataTable as global variable. Everything should work well. Good Luck!
[1]: https://i.stack.imgur.com/aODka.png

C# Update DataGridView from BackgroundWorker

Still struggling to get live data from my database to datagridview. This time I am attempting to do it via background worker. The only thing it gives from doWork method is an INT for backgroundWorker1_ProgressChanged method. I want to send my objects such as DataTable to it as well, so I could refresh data which is on main thread Form1. How can I pass my objects to backgroundWorker1_ProgressChanged event? Marked the place in the code where I need the data from DoWork
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string IP_db_mysql = "1";
string Port_db__mysql = "1";
string UserName_db__mysql = "1";
string Password_db__mysql = "1";
string dbName__mysql = "1";
string connString = "Server =" + IP_db_mysql + "; Port =" + Port_db__mysql + "; Database =" + dbName__mysql + "; Uid =" + UserName_db__mysql + ";Pwd =" + Password_db__mysql + ";";
MySqlConnection conn = new MySqlConnection();
DataTable dt = new DataTable();
string query = "SELECT * FROM mysql.test_table;";
int sum = 0;
for (int i = 1; i <= 10; i++)
{
sum = sum + i;
try
{
// open connectipon
conn.ConnectionString = connString;
conn.Open();
// build command
MySqlCommand cmd = new MySqlCommand(query, conn);
// execute command
MySqlDataReader dataReader = cmd.ExecuteReader();
// fill datatable
dt.Load(dataReader);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
// Calling ReportProgress() method raises ProgressChanged event
// To this method pass the percentage of processing that is complete
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
// Check if the cancellation is requested
if (backgroundWorker1.CancellationPending)
{
// Set Cancel property of DoWorkEventArgs object to true
e.Cancel = true;
// Reset progress percentage to ZERO and return
backgroundWorker1.ReportProgress(0);
return;
}
}
// Store the result in Result property of DoWorkEventArgs object
e.Result = sum;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
labelProgress.Text = e.ProgressPercentage.ToString() + "%";
**----------->> dataGridView1.DataSource = HOW TO GET DATATABLE FROM DO WORK METHOD !? <<------------------------**
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
labelProgress.Text = "Processing cancelled";
}
else if (e.Error != null)
{
labelProgress.Text = e.Error.Message;
}
else
{
labelProgress.Text = e.Result.ToString();
}
}
private void button23_Click(object sender, EventArgs e)
{
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!backgroundWorker1.IsBusy)
{
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
}
private void button24_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
// Cancel the asynchronous operation if still in progress
backgroundWorker1.CancelAsync();
}
}
At the end of your BackgroundWorker's DoWork you need to set its DataSource to your result. However, since you're in a background worker if you do that directly you'll get an exception because WinForms won't let you access the UI from another thread.
You can do this with the following code:
// create a method to handle updating the datasource
public void UpdateDataGridViewSource(object data)
{
// check if we need to swap thread context
if(this.dataGridView1.InvokeRequired)
{
// we aren't on the UI thread. Ask the UI thread to do stuff.
this.dataGridView1.Invoke(new Action(() => UpdateDataGridViewSource(data)));
}
else
{
// we are on the UI thread. We are free to touch things.
this.dataGridView1.DataSource = data;
this.dataGridView1.DataBind();
}
}
// at the end of your DoWork()
this.UpdateDataGridViewSource(result);
Use a state object like code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApplication76
{
class Program
{
static void Main(string[] args)
{
}
}
public enum ReportTypes
{
PROGRESS,
DATATABLE
}
public class Report
{
public int progress { get; set; }
public ReportTypes type { get; set; }
public DataTable table { get; set; }
}
public class Worker
{
public BackgroundWorker backgroundWorker1 { get; set; }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Report report = new Report();
report.progress = 50;
report.type = ReportTypes.PROGRESS;
backgroundWorker1.ReportProgress(50, report);
DataTable dt = new DataTable();
report.table = dt;
report.type = ReportTypes.DATATABLE;
backgroundWorker1.ReportProgress(50, report);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int progress = e.ProgressPercentage;
Report report = e.UserState as Report;
switch (report.type)
{
case ReportTypes.PROGRESS :
BeginInvoke((MethodInvoker)delegate
{
WriteStatusAndError("Query Completed");
});
break;
case ReportTypes.DATATABLE :
break;
}
}
}
}

Multi column Listview populated data wont display

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);})));
}
}

backgroundWorker or Thread Cell ProgressBar read DLL And Show treeView

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.

MOSS 2007, C#, Web Part - Log Retrievel using LinkButtons

Technology: SharePoint/MOSS 2007 --
IDE: Visual Studio 2008 --
Language: C#
I have created a SharePoint/MOSS 2007 web part that displays a list of log files.
The log files are rendered on screen as LinkButtons. The LinkButtons are within a DataTable that is set as the data source of a SPGridView and bound to it.
This SPGridView object is then added to the 'Controls' within the overridden "CreateChildControls()" method of the web part.
I am utilising the following "DownloadAssistant" helper class to display a specified file.
It's "DownloadFile" method is called from the '.Click' event of each LinkButton.
using System;
using System.Web;
/// <summary>
/// A class that helps to build download buttons/links on the fly
/// </summary>
static public class DownloadAssistant
{
static public void DownloadFile(string filePath)
{
try
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("Content-Disposition", string.Concat("attachment; filename=", filePath));
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.WriteFile(filePath);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
}
catch (Exception ex)
{
throw ex;
}
}
}
After one LinkButton is clicked on screen, I receive the download pop up window as expected and can go ahead and open the first log file. However after this first log file is opened, i.e. after the first LinkButton click event is triggered, I cannot trigger any other .Click event - it is as it I need to post back to the screen. When I click on any of the other LinkButtons nothing happens?
The Web Part code:
namespace LogRetrievalWebPart
{
[Guid("fd243ec2-83e3-4bad-af5e-c5c16acbc6dd")]
public class LogRetrievalWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
// Member variables prefixed with "m_"
private Label m_InfoLbl;
private SPGridView m_GridView;
private DataTable m_LogFileDataTable;
private DropDownList m_DirectoryDropDown;
private const String DROPDOWN_OPTION_DEFAULT = "---";
private const String DROPDOWN_OPTION_TRACE_LOGS = "Trace Logs";
private const String DROPDOWN_OPTION_BATCH_LOGS = "Batch Logs";
private const String DROPDOWN_OPTION_OTHER_LOGS = "Other Logs";
public LogRetrievalWebPart()
{
this.ExportMode = WebPartExportMode.All;
}
protected override void CreateChildControls()
{
EnsureChildControls();
base.CreateChildControls();
m_InfoLbl = new Label();
Label dropDownLbl = new Label();
dropDownLbl.Text = " Please select a directory: ";
this.Controls.Add(dropDownLbl);
m_DirectoryDropDown = new DropDownList();
m_DirectoryDropDown.Items.Add(DROPDOWN_OPTION_DEFAULT);
m_DirectoryDropDown.Items.Add(DROPDOWN_OPTION_TRACE_LOGS);
m_DirectoryDropDown.Items.Add(DROPDOWN_OPTION_BATCH_LOGS);
m_DirectoryDropDown.Items.Add(DROPDOWN_OPTION_OTHER_LOGS);
m_DirectoryDropDown.TextChanged += new EventHandler(directoryDropdown_TextChanged);
m_DirectoryDropDown.AutoPostBack = true;
m_LogFileDataTable = new DataTable("LogFiles");
AddColums();
m_GridView = new SPGridView();
m_GridView.AutoGenerateColumns = false;
BoundField idField = new BoundField();
idField.DataField = "ID";
idField.HeaderText = "ID";
m_GridView.Columns.Add(idField);
TemplateField colName = new TemplateField();
colName.HeaderText = "Log File Name";
colName.SortExpression = "LogFileName";
colName.ItemTemplate = new LinkTemplate("LogFileName", "Path");
m_GridView.Columns.Add(colName);
this.Controls.Add(m_DirectoryDropDown);
this.Controls.Add(m_InfoLbl);
this.Controls.Add(m_GridView);
this.Load += new EventHandler(LogRetrievalWebPart_Load);
this.PreRender += new EventHandler(LogRetrievalWebPart_PreRender);
}
void LogRetrievalWebPart_Load(object sender, EventArgs e)
{
EnsureChildControls();
}
protected void directoryDropdown_TextChanged(object sender, EventArgs e)
{
ViewState["LogRetrieval"] = null;
String selectedDirectoryName = m_DirectoryDropDown.SelectedItem.Text;
if (DROPDOWN_OPTION_TRACE_LOGS.Equals(selectedDirectoryName))
{
m_InfoLbl.Text = " *** TRACE Logs: *** ";
GetLogFiles("LogFiles/TraceLogs");
}
else if (DROPDOWN_OPTION_BATCH_LOGS.Equals(selectedDirectoryName))
{
m_InfoLbl.Text = " *** BATCH Logs: *** ";
GetLogFiles("PortalExecutables/Logs");
}
else if (DROPDOWN_OPTION_OTHER_LOGS.Equals(selectedDirectoryName))
{
m_InfoLbl.Text = " *** OTHER Logs: *** ";
GetLogFiles("PortalExecutables/GMExecutables");
}
else
{
m_InfoLbl.Text = " *** No Logs to display for this selection!!! *** ";
}
ViewState["LogRetrieval"] = m_LogFileDataTable;
m_GridView.DataSource = m_LogFileDataTable;
m_GridView.DataBind();
}
private void GetLogFiles(string aSelectedDirectory)
{
string directoryPath = HttpContext.Current.Server.MapPath(ResolveUrl("/LogFiles/" + aSelectedDirectory));
DirectoryInfo directory = new DirectoryInfo(directoryPath);
FileInfo[] files = directory.GetFiles();
int count = 1;
foreach (FileInfo fileInfo in files)
{
string fullFileName = fileInfo.FullName;
string fileName = fileInfo.ToString();
AddRow(count, fileName, fullFileName);
count++;
}
}
private void AddRow(int id, string logFileName, string fullFileName)
{
DataRow newRow = m_LogFileDataTable.Rows.Add();
newRow["ID"] = id;
newRow["LogFileName"] = logFileName;
newRow["Path"] = fullFileName;
}
private void AddColums()
{
DataColumn idCol = m_LogFileDataTable.Columns.Add("ID", typeof(Int32));
idCol.Unique = true;
m_LogFileDataTable.Columns.Add("LogFileName", typeof(String));
m_LogFileDataTable.Columns.Add("Path", typeof(String));
}
public void LogRetrievalWebPart_PreRender(object sender, EventArgs e)
{
if (this.Page.IsPostBack)
{
if (ViewState["LogRetrieval"] != null)
{
m_LogFileDataTable = (DataTable)ViewState["LogRetrieval"];
m_GridView.DataSource = m_LogFileDataTable;
m_GridView.DataBind();
}
}
}
public class LinkTemplate : ITemplate
{
string logFileName;
string logFilePath;
public LinkTemplate(string fieldName, string path)
{
logFileName = fieldName;
logFilePath = path;
}
public void InstantiateIn(Control container)
{
LinkButton link = new LinkButton();
container.Controls.Add(link);
link.DataBinding += new EventHandler(link_DataBinding);
link.Click += new EventHandler(link_Click);
}
private void link_DataBinding(Object sender, EventArgs e)
{
LinkButton link = (LinkButton)sender;
DataRowView dataRow = (DataRowView)((SPGridViewRow)link.NamingContainer).DataItem;
link.Text = dataRow[logFileName].ToString();
link.CommandArgument = dataRow[logFilePath].ToString();
}
private void link_Click(object sender, EventArgs e)
{
LinkButton link = (LinkButton)sender;
DownloadAssistant.DownloadFile(link.CommandArgument);
}
}
}
}
I found a solution.
I had to:
Set the button's client-side click event to: "exportRequested=true;"
Register the some JavaScript: For exact details, refer to:
http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/55136e4e-e1f7-4a79-9b75-be09cd5594c2

Categories