Microsoft Knowledge Base Email Alertz

KBAlertz.com: (816177) - This step-by-step article describes how to query a database on a background thread, and how to use data binding to display the results of the query in a DataGrid object. When you run large queries to a database, your application may become...

Receive Microsoft Knowledge Base articles by E-Mail?

Every night we scan the Microsoft Knowledge Base. If technologies you're interested in are updated, we'll send you an e-mail. You only get one e-mail a day, and only when new articles are added.

Click here to create a
FREE account
Already have an account?
[Click here to Login]

Search KbAlertz

Advanced Search

Webmasters
Put kbAlertz on your website.
[ Click Here for more! ]





ASP.NET 3.5 Web Hosting with Windows 2008 and SQL 2008: Click Here!
Discount ASP.NET Hosting
ASP.NET 2.0 and 3.5
Windows2008 and SQL2008
US and UK Hosting
The ad says 3 - but KBAlertz referrals get
** SIX MONTHS FREE **


Bug Tracking Software
For bug tracking software or defect tracking software or issue tracking software, visit Axosoft.


Community Site



We Send hundreds of thousands of emails using ASP.NET Email



Expert Web Design & Graphic Design
Design44.com

ASP.NET 3.5 Web Hosting with Windows 2008 and SQL 2008: Click Here!
Discount ASP.NET Hosting
ASP.NET 2.0 and 3.5
Windows2008 and SQL2008
US and UK Hosting
The ad says 3 - but KBAlertz referrals get
** SIX MONTHS FREE **




Mentioned In








Microsoft Knowledge Base Article

This article contents is Microsoft Copyrighted material.
©2005-©2007 Microsoft Corporation. All rights reserved. Terms of Use | Trademarks




Article ID: 816177 - Last Review: May 16, 2007 - Revision: 2.1

How to populate a Datagrid control on a background thread with data binding by using Visual C++ .NET or Visual C++ 2005

On This Page

SUMMARY

This step-by-step article describes how to query a database on a background thread, and how to use data binding to display the results of the query in a DataGrid object.

When you run large queries to a database, your application may become unresponsive for a long time. To avoid this behavior, run large queries on a background thread. This decreases your waiting time, releases your application for other tasks until the database returns the data, and also performs data binding.

Requirements

The following list outlines the recommended hardware, software, network infrastructure, and service packs that you need:
  • Microsoft Visual Studio .NET 2003 or Microsoft Visual Studio 2005
  • Access to the SQL Northwind sample database

Background

By design, a thread (other than the thread that created a form or a control) cannot call Windows Forms methods or Windows Control methods. If you try to do this, your application throws an exception. Depending on the exception-handling that is implemented in your code, this exception may cause your application to quit. If your application does not implement exception-handling, you receive the following error message:
An unhandled exception of type 'System.ArgumentException' occurred in system.windows.forms.dll
Additional information: Controls created on one thread cannot be parented to a control on a different thread.
This exception occurs because Windows Forms are based on a single-threaded apartment (STA) model. Windows Forms can be created on any thread. However, after they are created, they cannot be switched to a different thread. Additionally, Windows Form methods cannot be accessed on another thread. This means that all method calls must be run on the thread that created the form or the control.

Method calls that originate outside a creating thread must be marshalled (run) on this creating thread. To do this asynchronously, the form has a BeginInvoke method that forces the method to run on the thread that created the form or control. You call a synchronous method with a call to the Invoke method.

Build a Windows Forms Application

This section describes how to create a Windows Forms application that queries a database on a background thread, and uses the BeginInvoke method to perform data binding on a DataGrid object.
  1. Start Visual Studio .NET 2003 or Visual Studio 2005.
  2. On the File menu, point to New, and then click Project.
  3. Click Visual C++ Projects under Project Types, and then click Windows Forms Application (.NET) under Templates. By default, Form1 is created.

    Note In Visual Studio 2005, click Visual C++ under Project Types, and then click Windows Forms Application under Templates.
  4. Add a Button control to Form1, and then change the Text property of the button to Query on Thread.
  5. Add another Button control to Form1, and then change the Text property of the button to Query on Form.
  6. Add a Label control to Form1, and then clear the Text property of the label.
  7. Add a TextBox control to Form1.
  8. Add a DataGrid control to Form1.
  9. Right-click Form1, and then click View Code to display the code of your application.
  10. Add the following using statements at the top of the code window to use the System::Threading namespace and the System::Data::SqlClient namespace:
    using namespace System::Threading;
    using namespace System::Data::SqlClient;
  11. Add the following code to the Form1.h file:
    static Form1 * MyForm; 
    Thread  * UpdateThread;
    static ThreadStart * UpdateThreadStart = new ThreadStart(0,&Form1::QueryDataBase);
    static MethodInvoker  * CallDataBindToDataGrid = new MethodInvoker(0,&Form1::DataBindToDataGrid);
    static DataSet * MyDataSet;
    static SqlDataAdapter * MyDataAdapter;
    static String * MyQueryString = S"SELECT [Order Details].*, Orders.CustomerID, Orders.EmployeeID, Orders.OrderDate FROM [Order Details] CROSS JOIN Orders";
    static SqlConnection * MyConnection = new SqlConnection("data source=localhost;initial catalog=Northwind;integrated security=SSPI;");
    Note Change the connection string in this code according to your environment. The query that is used in this sample is a Cartesian product that returns over 165,000 rows from the Northwind database. The query returns a large amount of data so that the responsiveness of the form can be demonstrated.
  12. Open the Windows Forms Design view.
  13. Double-click Query on Thread, and then add the following code in the Click event for this button control:
    UpdateThread = new Thread(UpdateThreadStart);
    UpdateThread->Name = S"Update Thread";
    UpdateThread->IsBackground = true;
    UpdateThread->Start();
  14. Open the Windows Forms Design view.
  15. Double-click Query on Form, and then add the following code in the Click event for this button control:
    QueryDataBase();
  16. Add the following code after the code that you added in the previous step:
    // Subroutine that is to be run on the thread of the form.
    static void DataBindToDataGrid()
    {
    	MyForm->dataGrid1->DataSource = MyDataSet;
    	MyForm->dataGrid1->DataMember = S"MyTable";
    	MyDataSet = NULL;
    	MyDataAdapter = NULL;
    }
    
    // Subroutine that is used by the background thread to query the database.
    static void QueryDataBase()
    {
    	MyDataSet = new DataSet();
    	MyConnection->Open();
    	MyDataAdapter = new SqlDataAdapter(MyQueryString, MyConnection);		
    	MyForm->label1->Text = S"Filling the DataSet";
    	MyDataAdapter->Fill(MyDataSet, S"MyTable");
    	MyConnection->Close();
    	MyForm->label1->Text = S"DataSet Filled";
    	MyForm->BeginInvoke(CallDataBindToDataGrid);	
    }
    These subroutines are used by the background thread to query the database. These subroutines are also used to bind the database to the DataGrid control that is located on Form1 when you click the first button. The Click event of the second button directly calls the QueryDataBase function, and will be run on the Windows Form thread.
  17. Add the following line of code at the end of the InitializeComponent function:
    MyForm = this;
  18. Double-click Form1.cpp in Solution Explorer, and then modify the _tWinMain function as follows:
    Form1 * MyForm;
    MyForm = new Form1();
    Application::Run(MyForm);
  19. Press the CTRL+SHIFT+B key combination to build your application.

Demonstration

To see the benefit that is gained by using a background thread to query a database, follow these steps:
  1. Press the CTRL+F5 key combination to run your application without debugging.
  2. Click Query on Form. This begins the query on the Windows Form thread. If you try to enter some text in the text box that is displayed on Form1, your application does not respond. After the query has completed (this may take some time, depending on your computer), the DataGrid object displays the results of the query.
  3. Click Query on Thread. This creates a background thread that queries the database, and also keeps your application responsive to user interaction. To see this, click Query on Thread, and then try to type some text in the text box on the Form1 form.

Complete Code Listing

Form1.h

#pragma once

namespace DataGridThread
{
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::Threading;
    using namespace System::Data::SqlClient;

    /// <summary> 
    /// Summary for Form1
    /// </summary>
public __gc class Form1 : public System::Windows::Forms::Form
{   
public:
    Form1(void)
    {
        InitializeComponent();
    }

protected:
    void Dispose(Boolean disposing)
    {
        if (disposing && components)
        {
            components->Dispose();
        }
        __super::Dispose(disposing);
    }
private: System::Windows::Forms::Button *  button1;
private: System::Windows::Forms::Button *  button2;
private: System::Windows::Forms::Label *  label1;
private: System::Windows::Forms::TextBox *  textBox1;
private: System::Windows::Forms::DataGrid *  dataGrid1;

private:
    /// <summary>
    /// Required designer variable.
    /// </summary>
    System::ComponentModel::Container * components;

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    void InitializeComponent(void)
    {
        this->button1 = new System::Windows::Forms::Button();
        this->button2 = new System::Windows::Forms::Button();
        this->label1 = new System::Windows::Forms::Label();
        this->textBox1 = new System::Windows::Forms::TextBox();
        this->dataGrid1 = new System::Windows::Forms::DataGrid();
        (__try_cast<System::ComponentModel::ISupportInitialize *  >(this->dataGrid1))->BeginInit();
        this->SuspendLayout();
        // 
        // button1
        // 
        this->button1->Location = System::Drawing::Point(180, 16);
        this->button1->Name = S"button1";
        this->button1->Size = System::Drawing::Size(104, 23);
        this->button1->TabIndex = 0;
        this->button1->Text = S"Query on Thread";
        this->button1->Click += new System::EventHandler(this, button1_Click);
        // 
        // button2
        // 
        this->button2->Location = System::Drawing::Point(316, 16);
        this->button2->Name = S"button2";
        this->button2->Size = System::Drawing::Size(104, 23);
        this->button2->TabIndex = 1;
        this->button2->Text = S"Query on Form";
        this->button2->Click += new System::EventHandler(this, button2_Click);
        // 
        // label1
        // 
        this->label1->Location = System::Drawing::Point(24, 48);
        this->label1->Name = S"label1";
        this->label1->Size = System::Drawing::Size(496, 23);
        this->label1->TabIndex = 2;
        // 
        // textBox1
        // 
        this->textBox1->Location = System::Drawing::Point(24, 80);
        this->textBox1->Name = S"textBox1";
        this->textBox1->TabIndex = 3;
        this->textBox1->Text = S"textBox1";
        // 
        // dataGrid1
        // 
        this->dataGrid1->DataMember = S"";
        this->dataGrid1->HeaderForeColor = System::Drawing::SystemColors::ControlText;
        this->dataGrid1->Location = System::Drawing::Point(8, 112);
        this->dataGrid1->Name = S"dataGrid1";
        this->dataGrid1->Size = System::Drawing::Size(584, 296);
        this->dataGrid1->TabIndex = 4;
        // 
        // Form1
        // 
        this->AutoScaleBaseSize = System::Drawing::Size(5, 13);
        this->ClientSize = System::Drawing::Size(600, 421);
        this->Controls->Add(this->dataGrid1);
        this->Controls->Add(this->textBox1);
        this->Controls->Add(this->label1);
        this->Controls->Add(this->button2);
        this->Controls->Add(this->button1);
        this->Name = S"Form1";
        this->Text = S"Form1";
        (__try_cast<System::ComponentModel::ISupportInitialize *  >(this->dataGrid1))->EndInit();
        this->ResumeLayout(false);
        MyForm = this;

    }
    static Form1 * MyForm; 

    Thread  * UpdateThread;
    static ThreadStart * UpdateThreadStart = new ThreadStart(0,&Form1::QueryDataBase);
    static MethodInvoker  * CallDataBindToDataGrid = new MethodInvoker(0,&Form1::DataBindToDataGrid);
    static DataSet * MyDataSet;
    static SqlDataAdapter * MyDataAdapter;
    static String * MyQueryString = S"SELECT [Order Details].*, Orders.CustomerID, Orders.EmployeeID, Orders.OrderDate FROM [Order Details] CROSS JOIN Orders";
    static SqlConnection * MyConnection = new SqlConnection("data source=localhost;initial catalog=Northwind;integrated security=SSPI;");

private: System::Void button1_Click(System::Object *  sender, System::EventArgs *  e)
         {
             UpdateThread = new Thread(UpdateThreadStart);
             UpdateThread->Name = S"Update Thread";
             UpdateThread->IsBackground = true;
             UpdateThread->Start();
         }
private: System::Void button2_Click(System::Object *  sender, System::EventArgs *  e)
         {
             QueryDataBase();
         }
         // Subroutine that is to be run on the form's thread.
         static void DataBindToDataGrid()
         {
             MyForm->dataGrid1->DataSource = MyDataSet;
             MyForm->dataGrid1->DataMember = S"MyTable";
             MyDataSet = NULL;
             MyDataAdapter = NULL;
         }

         // Subroutine that is used by the background thread to query the database.
         static void QueryDataBase()
         {
             MyDataSet = new DataSet();
             MyConnection->Open();
             MyDataAdapter = new SqlDataAdapter(MyQueryString, MyConnection);       
             MyForm->label1->Text = S"Filling the DataSet";
             MyDataAdapter->Fill(MyDataSet, S"MyTable");
             MyConnection->Close();
             MyForm->label1->Text = S"DataSet Filled";
             MyForm->BeginInvoke(CallDataBindToDataGrid);   
         }
    };
}
Note You must add the common language runtime support compiler option (/clr:oldSyntax) in Visual C++ 2005 to successfully compile the previous code sample. To add the common language runtime support compiler option in Visual C++ 2005, follow these steps:
  1. Click Project, and then click <ProjectName> Properties.

    Note <ProjectName> is a placeholder for the name of the project.
  2. Expand Configuration Properties, and then click General.
  3. Click to select Common Language Runtime Support, Old Syntax (/clr:oldSyntax) in the Common Language Runtime support project setting in the right pane, click Apply, and then click OK.
For more information about the common language runtime support compiler option, visit the following Microsoft Web site:
/clr (Common Language Runtime Compilation)
http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx (http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx)

Form1.cpp

#include "stdafx.h"
#include "Form1.h"
#include <windows.h>

using namespace DataGridThread;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    Form1 * MyForm;
    MyForm = new Form1();
    Application::Run(MyForm);
}

REFERENCES

For more information, visit the following Microsoft Developer Network (MSDN) Web site:
http://msdn2.microsoft.com/en-us/library/3s8xdz5c(vs.71).aspx (http://msdn2.microsoft.com/en-us/library/3s8xdz5c(vs.71).aspx)

APPLIES TO
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C++ .NET 2003 Standard Edition
  • Microsoft ADO.NET 1.0
  • Microsoft ADO.NET 1.1
Keywords: 
kberrmsg kbdataadapter kbsqlclient kbthreadsync kbthread kbhowtomaster KB816177
       

Community Feedback System

Very often, it takes hours to solve a problem. Very often, you've looked high and low, and have tried a lot of solutions. When you finally found it, chances are, it was because someone else helped you. Here's your chance to give back. Use our community feedback tool to let others know what worked for you and what didn't.

Please also understand that the community feedback system is not warranted to be correct, it's simply a system that we've built to let people try and help each other. If something in a feedback response doesn't make sense to you, or you're not comfortable making changes that the feedback talks about (like registry edits), please consult a professional.

Thank you for using kbAlertz.com Feedback System.

-- Scott Cate

Be the first to leave feedback, to help others about this knowledge base article.

(Optional) Name

(Optional) Public URL Or Email

Comments
No HTML -- Text Only Please