Friday, October 19, 2012

Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on

Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on


You will get following error when you try to update a windows form control from a separate thread.

"Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on."
This article guides you on how to overcome this problem.

Problem

To reproduce this error, insert a progress bar control (progressbar1) on your form and insert a button(btnStart).

01.private void btnStart_Click(object sender, EventArgs e)
02.{
03.progressBar1.Minimum = 0;
04.progressBar1.Maximum = 100;
05. 
06.System.Threading.Thread t1 = new System.Threading.Thread(startProgress);
07.t1.Start();
08.}
09.void startProgress()
10.{
11.for (int i = 0; i             {
12.progressBar1.Value = i; //You will get error at this line
13.System.Threading.Thread.Sleep(100);
14.}
15.}

Solution


01.private void btnStart_Click(object sender, EventArgs e)
02.{
03.progressBar1.Minimum = 0;
04.progressBar1.Maximum = 100;
05. 
06.System.Threading.Thread t1 = new System.Threading.Thread(startProgress);
07.t1.Start();
08.}
09.void startProgress()
10.{
11.for (int i = 0; i             {
12.SetControlPropertyValue(progressBar1, "value", i); //This is a thread safe method
13.System.Threading.Thread.Sleep(100);
14.}
15.}

Note how SetControlpropertyValue function is used above. Following is it's definition.
01.delegate void SetControlValueCallback(Control oControl, string propName, object propValue);
02.private void SetControlPropertyValue(Control oControl, string propName, object propValue)
03.{
04.if (oControl.InvokeRequired)
05.{
06.SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
07.oControl.Invoke(d, new object[] { oControl, propName, propValue });
08.}
09.else
10.{
11.Type t = oControl.GetType();
12.PropertyInfo[] props = t.GetProperties();
13.foreach (PropertyInfo p in props)
14.{
15.if (p.Name.ToUpper() == propName.ToUpper())
16.{
17.p.SetValue(oControl, propValue, null);
18.}
19.}
20.}
21.}

You can apply same solution to any windows control. All you have to do is, copy SetControlValueCallback delegate and SetControlPropertyValue function from above code. For example if you want to set property of a label, use SetControlPropertyValue function.


SetControlPropertyValue(Label1, "Text", i.ToString());
Make sure you supply property value in correct type. In above example Text is a string property. This is why I am converting variable i to string.

Connect to Oracle using ASP.Net and C# or VB.Net

Connect to Oracle using ASP.Net and C# or VB.Net

  • Create a new project in Visual Studio using eight C# or VB.Net
  • Add reference to Oracle.DataAccess.dll file. Typically this file can be found in C:\oracle\product\10.2.0\client_1\BIN directory. This directory may be different based on your oracle configuration.
  • Once library is referenced, go to your class file where you want to create oracle connection.
  • Add following statements based on language you selected for project.

1.Imports Oracle.DataAccess.Client ' VB.NET
1.using Oracle.DataAccess.Client;  // C#
  • An Oracle connection string is inseparable from Oracle names resolution. Suppose we had a database alias of OraDb defined in a tnsnames.ora file as follows:
OraDb=
(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521))
)
(CONNECT_DATA=
(SERVER=DEDICATED)
(SERVICE_NAME=ORCL)
)
)

  • The OraDb alias defines the database address connection information for the client. To use the OraDb alias defined in the tnsnames.ora file shown above, you would use the following syntax:
1.Dim oradb As String = "Data Source=OraDb;User Id=scott;Password=tiger;"  ' VB.NET
2. 
3.string oradb = "Data Source=OraDb;User Id=scott;Password=tiger;";   // C#
You can modify the connection string to obviate the need for the tnsnames.ora file, however. Simply replace the name of the alias with how it would be defined in a tnsnames.ora file.
01.' VB.NET
02.</li>
03.</ul>
04.Dim oradb As String = "Data Source=(DESCRIPTION=" _
05.+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))" _
06.+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
07.+ "User Id=scott;Password=tiger;"
08. 
09.// C#
10.string oradb = "Data Source=(DESCRIPTION="
11.+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=ORASRVR)(PORT=1521)))"
12.+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
13.+ "User Id=scott;Password=tiger;";

  • Now you can create connection object from the connection string above. The connection string must be associated with the connection object.
1.Dim conn As New OracleConnection(oradb) ' VB.NET
2. 
3.OracleConnection conn = new OracleConnection(oradb); // C#
  • Now you can use this connection like any other connection and do various database tasks.
  • To open a connection, use following statements,
1.conn.Open() ' VB.NET
2. 
3.conn.Open(); // C#
  • To create a command object, use following statements,
1.Dim sql As String = "select dname from dept where deptno = 10" ' VB.NET
2.Dim cmd As New OracleCommand(sql, conn)
3.cmd.CommandType = CommandType.Text
4. 
5.string sql = "select dname from dept where deptno = 10"; // C#
6.OracleCommand cmd = new OracleCommand(sql, conn);
7.cmd.CommandType = CommandType.Text;

  • You can retrieve values from command object using following statement,
01.Dim dr As OracleDataReader = cmd.ExecuteReader() ' Visual Basic
02.dr.Read()
03. 
04.Label1.Text = dr.Item("dname") ' retrieve by column name
05.Label1.Text = dr.Item(0) ' retrieve the first column in the select list
06.Label1.Text = dr.GetString(0) ' return a .NET data type
07.Label1.Text = dr.GetOracleString(0) ' return an Oracle data type
08.OracleDataReader dr = cmd.ExecuteReader(); // C#
09.dr.Read();
10. 
11.label1.Text = dr["dname"].ToString(); // C# retrieve by column name
12.label1.Text = dr.GetString(0).ToString();  // return a .NET data type
13.label1.Text = dr.GetOracleString(0).ToString();  // return an Oracle data type
  • All open connection objects should be closed once you are done using them.
1.conn.Close()   ' Visual Basic
2.conn.Dispose() ' Visual Basic
3. 
4.conn.Close();   // C#
5.conn.Dispose(); // C#

Connecting to MySQL database using C# and .Net

Connecting to MySQL database using C# and .Net

Connecting to MySQL database using C# and .Net
Download MySQL script file
Download Sample
This article shows you how you can connect to MySQL database using MySQL Connector for .Net. I will also show you how you can update mysql database records using C#.

Prerequisites for running sample

  • Visual Studio 2005 or Visual Studio 2008
  • MySQL database installed on your local machine or remote host.
  • MySQL database admin tool that allows you to create database and run sql statements. I am using phpMyAdmin which is a web interface.

Getting Started

  • Go to MySQL admin tool, and create a new database, call it inventorydb
  • Download MySQL script from following link. This is a .sql file. It contains items table structure and data.
    Download MySQL script file
    Open this file with MySQL Admin tool or copy paste sql syntax from this file into MySQL Admin tool. Run it and it should create items table in inventorydb database.
    Connecting to MySQL database using C# and .Net


  • For Visual Studio, you need to install MySQL Connector for .Net which is basically a .Net library to support MySQL database connectivity in .Net. Go to following link to download connector and install it.
    http://dev.mysql.com/downloads/connector/net
    When you install connector make sure that you close Visual Studio before installing.
  • Once MySQL connector is installed successfully on your computer, download sample from following link and extract it in some folder. Download Sample
  • Open sample solution file with visual studio.
  • Inside Solution Explorer, open App.Config file and change connection string so that it points to MySQL database that you created before. Change database user name and password in connection string as per your database instance.
    Connection String

  • Run sample and it should give you list of items in grid.You can update, insert or delete items from this list.

Sourcecode description

Source code of this sample is very straight forward.
  • Initialize mysql connection using following code,
    1.//Initialize mysql connection
    2.connection = new MySqlConnection(ConnectionString);
    3. 
    4.//Get all items in datatable
    5.DTItems = GetAllItems();
  • GetAllItems() function returns all items from database table,
    01.//Get all items from database into datatable
    02.DataTable GetAllItems()
    03.{
    04.try
    05.{
    06.//prepare query to get all records from items table
    07.string query = "select * from items";
    08.//prepare adapter to run query
    09.adapter = new MySqlDataAdapter(query, connection);
    10.DataSet DS = new DataSet();
    11.//get query results in dataset
    12.adapter.Fill(DS);
    13..
    14..
    15..
    16.//return datatable with all records
    17.return DS.Tables[0];
    18.}
  • After retrieving all items in a datatable, fill grid view using datatable,
    1.dataGridView1.DataSource = DTItems;
  • When initializing dataset, set update, insert and delete commands with adapter.
    01..
    02..
    03..
    04.// Set the UPDATE command and parameters.
    05.adapter.UpdateCommand = new MySqlCommand(
    06."UPDATE items SET ItemName=@ItemName, Price=@Price, AvailableQuantity=@AvailableQuantity, Updated_Dt=NOW() WHERE ItemNumber=@ItemNumber;",connection);
    07.adapter.UpdateCommand.Parameters.Add("@ItemNumber", MySqlDbType.Int16, 4, "ItemNumber");
    08.adapter.UpdateCommand.Parameters.Add("@ItemName", MySqlDbType.VarChar, 100, "ItemName");
    09.adapter.UpdateCommand.Parameters.Add("@Price", MySqlDbType.Decimal, 10, "Price");
    10.adapter.UpdateCommand.Parameters.Add("@AvailableQuantity", MySqlDbType.Int16, 11, "AvailableQuantity");
    11.adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
    12. 
    13.// Set the INSERT command and parameter.
    14.adapter.InsertCommand = new MySqlCommand(
    15."INSERT INTO items VALUES (@ItemNumber,@ItemName,@Price,@AvailableQuantity,NOW());",connection);
    16.adapter.InsertCommand.Parameters.Add("@ItemNumber", MySqlDbType.Int16, 4, "ItemNumber");
    17.adapter.InsertCommand.Parameters.Add("@ItemName", MySqlDbType.VarChar, 100, "ItemName");
    18.adapter.InsertCommand.Parameters.Add("@Price", MySqlDbType.Decimal, 10, "Price");
    19.adapter.InsertCommand.Parameters.Add("@AvailableQuantity", MySqlDbType.Int16, 11, "AvailableQuantity");
    20.adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
    21. 
    22.// Set the DELETE command and parameter.
    23.adapter.DeleteCommand = new MySqlCommand(
    24."DELETE FROM items " + "WHERE ItemNumber=@ItemNumber;", connection);
    25.adapter.DeleteCommand.Parameters.Add("@ItemNumber", MySqlDbType.Int16, 4, "ItemNumber");
    26.adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;
    27..
    28..
    29..
  • When Save button is clicked, we need to update adapter in order to save records. Note that when adapter is updated, corresponding commands (insert, update or delete) are executed against database based on operations that you have done on grid.
    01.private void btnSave_Click(object sender, EventArgs e)
    02.{
    03.try
    04.{
    05.//Save records in database using DTItems which is datasource for Grid
    06.adapter.Update(DTItems);
    07..
    08..
    09..
  • When Delete button is clicked, we need to remove row from datatable. After that update adapter to save records.
    01.private void btnDelete_Click(object sender, EventArgs e)
    02.{
    03.if (dataGridView1.SelectedRows.Count > 0)
    04.{
    05.//Delete a row from grid first.
    06.dataGridView1.Rows.Remove(dataGridView1.SelectedRows[0]);
    07. 
    08.//Save records again. This will delete record from database.
    09.adapter.Update(DTItems);
    10..
    11..
    12..

Read Write XML data using ADO.Net dataset and C#

Read Write XML data using ADO.Net dataset and C#

Introduction

In this article, I will show you how you can write simple XML files using C# and ADO.Net dataset. You can use this technique to save data into an XML file instead of a database. This can become very handy if you need to work with a very small amount of data and you don't want to use a database.

Read/Write XML file

Download Sample

Download sample from above link and open it with Visual Studio. I have used Visual Studio 2008 for this sample. You may need to convert this project if you are using higher version of Visual Studio. When you run this sample, you would see Create XML File button on top of the form. Click on it to create a new XML file on C: drive. XML file path is mentioned in text box. You can change this path if you want. Then you can click on Read XML file. This will read XML file that program has just created. This will show you XML data in a grid view. You can modify data in Grid View and click on Save XML file button to save it back to C: drive. To check if modified data was saved, you can click on Clear Grid button and then click Read XML button. Grid should display modified data.

Source explaination

Sourcecode is very easy to understand. I have added comments between code to explain what it does.

Create XML file Button click event

This event first creates a datatable DT with dummy data. Datatable DT is then added to dataset DS. Dataset has a WriteXml() method that writes data to specified path.
01.private void btnCreateXML_Click(object sender, EventArgs e)
02.{
03.try
04.{
05.//Create a datatable to store XML data
06.DataTable DT = new DataTable();
07.DT.Columns.Add("FirstName");
08.DT.Columns.Add("LastName");
09.DT.Columns.Add("Gender");
10.DT.Columns.Add("Age");
11.DT.Rows.Add(new object[] { "Nick", "Solimine", "Male", 22 });
12.DT.Rows.Add(new object[] { "Mark", "Taylor", "Male", 32 });
13.DT.Rows.Add(new object[] { "Alice", "Warden", "Female", 20 });
14. 
15.//Create a dataset
16.DS = new DataSet();
17. 
18.//Add datatable to this dataset
19.DS.Tables.Add(DT);
20. 
21.//Write dataset to XML file
22.DS.WriteXml(txtXMLFilePath.Text);
23. 
24.MessageBox.Show("XML data written successfully to "+txtXMLFilePath.Text);
25.}
26.catch(Exception ex)
27.{
28.MessageBox.Show("Exception: "+ex.Message);
29.}
30.}

Read XML file Button click event

This event read XML data from file specified in txtXMLFilePath text box. Dataset has ReadXml() method that can be used to read XML data into Dataset.
01.private void btnReadXML_Click(object sender, EventArgs e)
02.{
03.try
04.{
05.//Initialize new Dataset
06.DS = new DataSet();
07. 
08.//Read XML data from file
09.DS.ReadXml(txtXMLFilePath.Text);
10. 
11.//Fill grid with XML data
12.dataGridView1.DataSource = DS.Tables[0];
13.dataGridView1.Refresh();
14. 
15.MessageBox.Show("XML data read successful");
16.}
17.catch(Exception ex)
18.{
19.MessageBox.Show("Exception: " + ex.Message);
20.}
21.}

Save XML file Button click event

This event save XML data from Grid to file specified in txtXMLFilePath text box.
01.private void btnSaveXML_Click(object sender, EventArgs e)
02.{
03.try
04.{
05.//Write dataset to XML file
06.DS.WriteXml(txtXMLFilePath.Text);
07.MessageBox.Show("XML data saved successfully to " + txtXMLFilePath.Text);
08.}
09.catch (Exception ex)
10.{
11.MessageBox.Show("Exception: " + ex.Message);
12.}
13.}

Clear Grid Button click event

This event clears data from Grid.
1.private void btnClearGrid_Click(object sender, EventArgs e)
2.{
3.//Clear Grid by setting it's datasource property to null.
4.dataGridView1.DataSource = null;
5.}

Summary

It's easier to read/write data into XML file using Dataset. It has a direct methods available to do that. Structuring data is more easier using datatable and dataset. It's also possible to use XmlReader and XmlWriter classes available in System.XML namespace. But I find it little difficult to use.